N'a pas été fait : la structuration du projets en sous fichiers assemblés par #include qui n'ont pas été reconnus à la compilation.
Non plus : le transport du projet sur clef USB. La librairie SDL n'est plus reconnue au changement d'ordinateur....
Reste un artefact lors de certains chocs boule/boule dans lesquels elles se retrouvent collées l'une à l'autre
if ( SDL_Init( SDL_INIT_VIDEO|SDL_INIT_TIMER) <0 ) { printf( "initialisation impossible de SDL: %s\n", SDL_GetError() ); return 1; } |
Essaye d'initialiser le mode vidéo et la gestion du temps. En cas d'erreur, SDL_Init renvoie un résultat négatif et le printf écrit le message dans le fichier standard de sortie : stdout.txt qui se trouve dans le sous répertoire bin |
atexit(SDL_Quit); |
s'assure de la bonne fermeture à la fin |
SDL_Surface* ecran = SDL_SetVideoMode( LARGEUR_ECRAN, HAUTEUR_ECRAN, 32, SDL_HWSURFACE|SDL_DOUBLEBUF); |
Réserve en mémoire vidéo (SDL_HWSURFACE) unemplacement pour placer une image en couleur 32bits (232 couleurs) avec, si possible, un double tampon (SDL_DOUBLEBUF) d'affichage : l'image est préparée dans le tampon et affichée d'un seul coup lorsqu'elle est prète par SDL_blit Cette image sera accessible par la variable pointeur ecran de type SDL_Surface |
if ( !ecran ) { printf("Impossible de créer le rectangle video: %s\n", SDL_GetError()); return 1; } |
envoie un message d'erreur si la rservation de mémoire n'a pas pu se faire |
SDL_FillRect(ecran, NULL, SDL_MapRGB(ecran->format, 255,255,255)); |
Crée un rectangle de couleur à partir du bord haut gauche (NULL) en couleur (Red Green Blue =RGB) blanche dans l'image ecran |
SDL_WM_SetCaption("Billard", NULL); |
Affiche le nom Billard en haut de la fenêtre. |
Les bord seront faits en dégradés par une succession de lignes horizontales et de barres verticales de plus en plus sombres.
Pour obtenir des bords biseautés, il faut placer la ligne à des coordonnées décalées et à une longueur dimiuée de 2. (un pixel perdu à chaque extrémité)
Pour griser, on augmente la quantité de Red Green et Blue du de 255/epaisseur à chaque ligne/barre
Une fois le fond créé, on peut libérer la mémoire de ligne et de barre
SDL_Surface* creerLeFond(int epaisseur) { //creation d'une surface blanche unie SDL_Surface *leFond=SDL_CreateRGBSurface(SDL_HWSURFACE, LARGEUR_ECRAN, HAUTEUR_ECRAN, 32, 0, 0, 0, 0); SDL_FillRect(leFond, 0, SDL_MapRGB(leFond->format, 255, 255, 255)); //création d'un dégradé de gris //sur des lignes horizontale et des barres verticales SDL_Surface *ligne=SDL_CreateRGBSurface(SDL_HWSURFACE, LARGEUR_ECRAN, 1, 32, 0, 0, 0, 0); SDL_Surface *barre=SDL_CreateRGBSurface(SDL_HWSURFACE, 1, HAUTEUR_ECRAN, 32, 0, 0, 0, 0); SDL_Rect dstHorHaut,dstHorBas,dstVertDrt,dstVertGch,tailleH,tailleV; // dst est la position de collage dstHorHaut.x=0; dstHorHaut.y=0; dstHorBas.x=0; dstHorBas.y=HAUTEUR_ECRAN; dstVertDrt.x=0; dstVertGch.y=0; dstVertDrt.x=LARGEUR_ECRAN; dstVertDrt.y=0; // longeur de ligne à coller tailleH.w=LARGEUR_ECRAN; tailleH.h=1; tailleH.x=0; tailleH.y=0; //longueur de la barre à coller tailleV.w=1; tailleV.h=HAUTEUR_ECRAN; tailleV.x=0; tailleV.y=0; int i=0; int pas=int(255/epaisseur); for (i=epaisseur;i>0;i--) { //décalage de 1 de la position de collage et diminution de 2 de la largeur à coller // pour avoir un biseau dans l'angle //ligne du haut dstHorHaut.x++; dstHorHaut.y++; //ligne du bas dstHorBas.x++; dstHorBas.y--; tailleH.w-=2; //barre de droite dstVertGch.x++; dstVertGch.y++; //barre de gauche dstVertDrt.x--; dstVertDrt.y++; tailleV.h-=2; // grise la ligne SDL_FillRect(ligne, NULL, SDL_MapRGB(ligne->format, pas*i,pas*i,pas*i)); SDL_BlitSurface(ligne, &tailleH , leFond, &dstHorBas); SDL_BlitSurface(ligne, &tailleH , leFond, &dstHorHaut); // grise la barre SDL_FillRect(barre, NULL, SDL_MapRGB(ligne->format, pas*i,pas*i,pas*i)); SDL_BlitSurface(barre, &tailleV , leFond, &dstVertDrt); SDL_BlitSurface(barre, &tailleV , leFond, &dstVertGch); }; SDL_free(ligne); SDL_free(barre); return leFond; } |
La structure vecteur ne contient que 2 champs : ses coordonnées .x, .y.
ils sont de type float afin de permettre des mouvements de moins d'un pixel.
La structure boule contiendra des champ de
Les fonctions permettront de tester le ercouvrement des boules lors du placement (et plus tard, lors des déplacements)
typedef struct vecteur { float x,y; } vecteur; typedef struct tBoule { vecteur coord,vitesse; SDL_Surface* img; } tBoule; |
float distCarre(vecteur point1, vecteur point2) { return (point1.x-point2.x)*(point1.x-point2.x)+(point1.y-point2.y)*(point1.y-point2.y); } float norme(vecteur v) { return sqrt( v.x*v.x+v.y*v.y); } float prodScal(vecteur u, vecteur v) { return u.x*v.x+u.y*v.y; } |
tBoule liste[NOMBRE_DE_BOULES]; |
int chargeBoules(tBoule *liste,int nbrBoules) { SDL_Surface* boule = SDL_LoadBMP("boule.bmp"); if (!boule) { printf("n'a pas pu cherger l'image: %s\n", SDL_GetError()); return 0; } SDL_Surface* bouleBleue = SDL_LoadBMP("bouleBleue.bmp"); if (!bouleBleue) { printf("n'a pas pu cherger l'image: %s\n", SDL_GetError()); return 0; } //définit la couleur blanche Uint32 couleurTransparence=SDL_MapRGB(boule->format, 255, 255, 255); //rend le blanc transparent SDL_SetColorKey(boule, SDL_SRCCOLORKEY, couleurTransparence); SDL_SetColorKey(bouleBleue, SDL_SRCCOLORKEY, couleurTransparence); // affecte l'image à la boule int i=0; for (i=0;i<nbrBoules-1;i++) { liste[i].img=boule; liste[i].vitesse.x=0; liste[i].vitesse.y=0; } liste[nbrBoules-1].img=bouleBleue; return 1; } |
On déplace la souris au centre de l'écran avec SDL_WarpMouse puis,
Pour chaque boule for (i=0;i <nbrBoules;i++)
int placeBoules(tBoule *liste,int nbrBoules,SDL_Surface *ecran) { // place le curseur au centre // promène les boules jusqu'au clic // et n'accepte le clic que si la position n'empiète sur aucune des boules déja placées. // au clic, affecte les coordonnées aux centres de chaque boule. // et une vitesse nulle. SDL_Surface* boule; SDL_Surface* tamponBoule; // fstBoule contient la position précédente/courante de la boule SDL_Rect tailleBoule,dstBoule,origine; vecteur coordi; // coordonnées du candidat int x,y,diametreCarre; // replace la souris au centre x=LARGEUR_ECRAN/2; y=HAUTEUR_ECRAN/2; SDL_WarpMouse(x,y); |
// i boule courante, j boule comparée int i=0,j=0; ////////////////////////////////////// pour chaque boule///////////////////////////////////////// for (i=0;i <nbrBoules;i++) { // boule promenée à l'écran boule=liste[i].img; // cree le tampon boule SDL_GetClipRect(boule, &tailleBoule); tamponBoule = SDL_CreateRGBSurface(SDL_HWSURFACE, tailleBoule.w, tailleBoule.h, 32, 0, 0, 0, 0); dstBoule.x=x- boule->w/2; dstBoule.y=y- boule->h/2; dstBoule.h=boule->h; dstBoule.w=boule->w; // met en tampon le dessous de la boule SDL_BlitSurface(ecran,&dstBoule,tamponBoule,NULL); // et colle la boule sur l'écran SDL_BlitSurface(boule,NULL,ecran,&dstBoule); SDL_Flip(ecran); |
/////////////////////////////// attend un mouvement de souris ou un clic///////////////////////////// int attend=1; int flag=0; SDL_Event event; while (attend) { // met en veille en attendant un événement SDL_WaitEvent(&event); switch(event.type) { case SDL_QUIT : // Si c'est un évènement de type "Quitter" return 0; break; |
////////////////////////////// si c'est un clic, ////////////////////////////////////////////////////// // vérifier d'abord la compatibilité avec les bords // ne poser la boule que dans ce cas case SDL_MOUSEBUTTONDOWN : SDL_GetMouseState(&x,&y); // le pointeur dépasse la bordure if (x <EPAISSEUR+boule->w/2) {break;} if (x+boule->w/2+EPAISSEUR>LARGEUR_ECRAN){break;} if (y <EPAISSEUR+boule->h/2){break;} if (y+boule->h/2+EPAISSEUR>HAUTEUR_ECRAN) {break;} // disjoinction des boules déja placée : de 0 à i-1 // leurs distance doit être de plus de 2 rayons flag=0; coordi.x=x; coordi.y=y; for (j=0;jh)+boule->h; diametreCarre*=diametreCarre/4; if ( int(distCarre(liste[j].coord,coordi)) <diametreCarre) { flag=1; break; // arrete seulement le "for j" } } if (flag) { break; }; |
//////////////////////////////////// si la place est libre ////////////////////////////////////// // repose le tampon // dstBoule continent l'ancienne position SDL_BlitSurface(tamponBoule,NULL,ecran,&dstBoule); // affecte les coordonnées liste[i].coord.x=x; liste[i].coord.y=y; //et pose définitivement la boule sans mettre en tampon dstBoule.x=x-boule->w/2; dstBoule.y=y-boule->h/2; SDL_BlitSurface(boule,NULL,ecran,&dstBoule); SDL_Flip(ecran); attend=0; break; |
//////////////////////////////////// si il y a un mouvement de souris //////////////////////////// case SDL_MOUSEMOTION : // force à rester dans le billard SDL_GetMouseState(&x,&y); if (x- boule->w/2<EPAISSEUR) { x=EPAISSEUR+boule->w/2; } if (x+boule->w/2+EPAISSEUR>LARGEUR_ECRAN) { x=LARGEUR_ECRAN-EPAISSEUR-boule->w/2; } if (y- boule->h/2<EPAISSEUR) { y=EPAISSEUR+boule->h/2; } if (y+boule->h/2+EPAISSEUR>HAUTEUR_ECRAN) { y=HAUTEUR_ECRAN-EPAISSEUR-boule->h/2; } |
////////////////////////////// déplace la boule /////////////////////////////////////////////////// // repose le tampon SDL_BlitSurface(tamponBoule,NULL,ecran,&dstBoule); // met en tampon dstBoule.x=x- boule->w/2; dstBoule.y=y- boule->h/2; dstBoule.h=boule->h; dstBoule.w=boule->w; origine.x=0; origine.y=0; SDL_BlitSurface(ecran,&dstBoule,tamponBoule,&origine); // colle la boule SDL_BlitSurface(boule, NULL, ecran, &dstBoule); SDL_Flip(ecran); break; } } } SDL_FreeSurface(tamponBoule); return 1; } |
///////////////////// dans la fonction main ////////////////////////// while (jouer(liste,NOMBRE_DE_BOULES,ecran,fond)) { ; } ////////////// prend l'impulsion et fais bouger les boules jusqu'à l'arret ///////////////// int jouer(tBoule *liste,int nbrBoules,SDL_Surface *ecran,SDL_Surface *fond) { SDL_Rect origine,dstBoule; int i,j,continuer=1; Uint32 temps=SDL_GetTicks(); // attend une impulsion while (continuer) { continuer=impulsion(liste,nbrBoules,ecran); if (!continuer) { return 0; } |
|
/////////////////////////// déplace les boules jusqu'à l'arret : toutes < V_MINI //////////// // s'arrete si SDL_EXIT while (continuer) { continuer=0; for (i=0;i<NOMBRE_DE_BOULES;i++) { // ralenti la boule boule liste[i].vitesse.x*=VISCOSITE; liste[i].vitesse.y*=VISCOSITE; // test si la vistesse est suffisante if ( fabs(liste[i].vitesse.x)+fabs(liste[i].vitesse.y)>V_MINI ) { continuer=1; } else { liste[i].vitesse.x=0; liste[i].vitesse.y=0; } // déplace la boule deplaceBoule(&(liste[i])); // test et fait rebondir sur les bords et sur toutes les autres boules. rebondBouleBord(liste[i], &liste[i]); for (j=0;j<NOMBRE_DE_BOULES;j++) { if (i!=j) { rebondBouleBoule(liste[i],liste[j],&liste[i],&liste[j]); } }; } //affiche toutes les boules origine.x=0; origine.y=0; SDL_BlitSurface(fond,NULL,ecran,&origine); for (i=0;i<NOMBRE_DE_BOULES;i++) { dstBoule.x=liste[i].coord.x-liste[i].img->h/2; dstBoule.y=liste[i].coord.y-liste[i].img->w/2; SDL_BlitSurface(liste[i].img,NULL,ecran,&dstBoule); } while(SDL_GetTicks()<temps+20){}; temps=SDL_GetTicks(); //printf(" %d\n", temps); SDL_Flip(ecran); }; } } |
//////////////////// attend le clicdown /////////////////////////////////////// int impulsion(tBoule *liste,int nbrBoules,SDL_Surface *ecran) { float vx=0,vy=0,rayonCarre; int x,y,i,numBouleChoisie; vecteur coordSouris; // attend le clic int attend=1; SDL_Event event; while (attend) { SDL_WaitEvent(&event); // Récupèration de l'évènement dans event switch(event.type) // Test du type d'évènement { case SDL_QUIT : // Si c'est un évènement de type "Quitter" return 0; attend=0; break; |
/////////////////// en cas de clic chercher la boule cliquée //////////////////// case SDL_MOUSEBUTTONDOWN : // récupère les coordonnées du point de clic SDL_GetMouseState(&x,&y); coordSouris.x=x; coordSouris.y=y; // cherche si une des boules est à moins d'un rayon du clic for (i=0;i<nbrBoules;i++) { rayonCarre=liste[i].img->h/2; rayonCarre*=rayonCarre; if (distCarre(liste[i].coord,coordSouris)<=rayonCarre) { numBouleChoisie=i; attend=0; break; } } break; } } |
////////////////////// étire (déplace la boule) en attendant le lacher ///////////////// attend=1; SDL_Surface *boule,*tamponBoule; boule=liste[numBouleChoisie].img; SDL_Rect tailleBoule,dstBoule,origine; SDL_GetClipRect(boule, &tailleBoule); tamponBoule = SDL_CreateRGBSurface(SDL_HWSURFACE, tailleBoule.w, tailleBoule.h, 32, 0, 0, 0, 0); // calcule le recangle occupé par la boule dstBoule.x=x- boule->w/2; dstBoule.y=y- boule->h/2; dstBoule.h=boule->h; dstBoule.w=boule->w; // met en tampon le dessous de la boule SDL_BlitSurface(ecran,&dstBoule,tamponBoule,NULL); while (attend) { SDL_WaitEvent(&event); switch (event.type) { case SDL_QUIT: attend=0; return 1; break; |
//////////////////////// déplace la boule en suivant la souris /////////////////////// case SDL_MOUSEMOTION : // force à rester dans le billard // EPAISSEUR est celle de la partie dégradée SDL_GetMouseState(&x,&y); if (x- boule->w/2<EPAISSEUR) { x=EPAISSEUR+boule->w/2; } if (x+boule->w/2+EPAISSEUR>LARGEUR_ECRAN) { x=LARGEUR_ECRAN-EPAISSEUR-boule->w/2; } if (y- boule->h/2<EPAISSEUR) { y=EPAISSEUR+boule->h/2; } if (y+boule->h/2+EPAISSEUR>HAUTEUR_ECRAN) { y=HAUTEUR_ECRAN-EPAISSEUR-boule->h/2; } // repose le tampon SDL_BlitSurface(tamponBoule,NULL,ecran,&dstBoule); // met en tampon dstBoule.x=x- boule->w/2; dstBoule.y=y- boule->h/2; dstBoule.h=boule->h; dstBoule.w=boule->w; origine.x=0; origine.y=0; SDL_BlitSurface(ecran,&dstBoule,tamponBoule,&origine); // colle la boule SDL_BlitSurface(boule, NULL, ecran, &dstBoule); SDL_Flip(ecran); break; |
//////////////////////////// boutton relaché //////////////////////////////////////// case SDL_MOUSEBUTTONUP : SDL_GetMouseState(&x,&y); liste[i].vitesse.x=(liste[i].coord.x-x)*COEF_VITESSE; liste[i].vitesse.y=(liste[i].coord.y-y)*COEF_VITESSE; attend=0; break; } } return 1; }; |
Déplace à chaque étape de la vistesse
Ici, c'est un pointeur tBoule qui est foourni à la fonction.
Pour accèder à ses champs, c'est donc boule->vitesse.x.
void deplaceBoule(tBoule *boule) { boule->coord.x+=boule->vitesse.x; boule->coord.y+=boule->vitesse.y; } |
Il y a rebond sur le bord si, après déplacement, la boule empiète sur la bande de bord.
Il faut alors inverser la composante de la vitesse perpendiculaire au bord en question (réflexion)
void rebondBouleBord(tBoule incidente, tBoule *reflechie) { float r=incidente.img->h/2; copieBoule(incidente,reflechie); // test l'appartenance à un des pièges // rebond à gauche if (incidente.coord.x<EPAISSEUR+r && incidente.vitesse.x<0 ) { reflechie->vitesse.x=-incidente.vitesse.x; } // rebond à droite if (incidente.coord.x+r+EPAISSEUR>LARGEUR_ECRAN && incidente.vitesse.x>0) { reflechie->vitesse.x=-incidente.vitesse.x; } // rebond en haut if (incidente.coord.y<EPAISSEUR+r && incidente.vitesse.y<0) { reflechie->vitesse.y=-incidente.vitesse.y; } // rebond en bas if (incidente.coord.y+r+EPAISSEUR>HAUTEUR_ECRAN && incidente.vitesse.y>0) { reflechie->vitesse.y=-incidente.vitesse.y; } } |
Il y a rebond sur une boule si, après déplacement, la boule empiète sur une autre
(la distance de leurs centres est inférieur à la somme de leurs rayons)
Dans le repère barycentrique (ici, déterminé par le milieu des deux boules), les vitesses seront opposées.
Seule la composante dans l'axe du choc (déterminé par les deux centres des boules) doit être inversée.
Le repère mobile (u,v) normalisé est déterminé :
void rebondBouleBoule(tBoule incidente1, tBoule incidente2, tBoule *reflechie1, tBoule *reflechie2) { // incidente1->img->h est la haurteur=largeur=diametre de la boule if (distCarre(incidente1.coord,incidente2.coord)>(incidente1.img->h)*(incidente1.img->h)) { copieBoule(incidente1,reflechie1); copieBoule(incidente2,reflechie2); } else { // VM : vitesse du (repère) mobile, (u,v) base mobile orthonormal vecteur VM,u,v; VM.x=(incidente1.vitesse.x+incidente2.vitesse.x)/2; VM.y=(incidente1.vitesse.y+incidente2.vitesse.y)/2; // direction de réflexion donné par le centre des boules u.x=(incidente1.coord.x-incidente2.coord.x); u.y=(incidente1.coord.y-incidente2.coord.y); // normalisation de u float r=norme(u); u.x=u.x/r; u.y=u.y/r; // quart de tours pour v v.x=u.y; v.y=-u.x; // vitesse incidente de la boule 1 relative au repere mobile // celle de la boule 2 est l'oppposée. // VIA coordonnées dans le repère absolu // VIM coordonnées dans le repère mobile // N.B. dans le repère mobile, les vitesses des deux boules restent opposées vecteur VIA,VIM; VIA.x=incidente1.vitesse.x-VM.x; VIA.y=incidente1.vitesse.y-VM.y; VIM.x=prodScal(VIA,u); VIM.y=prodScal(VIA,v); // vitesse réfléchie // seule la partie suivant u est inversée // dans le repère mobile, les vitesse des deux boules restent opposées reflechie1->vitesse.x=-VIM.x*u.x+VIM.y*v.x + VM.x; reflechie1->vitesse.y=-VIM.x*u.y+VIM.y*v.y+ VM.y; reflechie1->coord.x=incidente1.coord.x; reflechie1->coord.y=incidente1.coord.y; reflechie1->img=incidente1.img; reflechie2->vitesse.x=VIM.x*u.x-VIM.y*v.x+VM.x; reflechie2->vitesse.y=+VIM.x*u.y-VIM.y*v.y+VM.y; reflechie2->coord.x=incidente2.coord.x; reflechie2->coord.y=incidente2.coord.y; reflechie2->img=incidente2.img; } } |
//////////////// libérer la mémoire /////////////////////////////// int i=0; for (i=0;i<NOMBRE_DE_BOULES;i++) { SDL_FreeSurface(liste[i].img); } SDL_FreeSurface(fond); SDL_FreeSurface(ecran); // all is well ;) printf("Exited cleanly\n"); return 0; SDL_Quit(); |