Vous êtes sur la page 1sur 12

Version en ligne (../../../www.siteduzero.

com/informatique/tutoriels/tp-zozor-dans-son-enclos)

Tutoriel : [TP] Zozor dans son


enclos
Table des matières
[TP] Zozor dans son enclos
Le projet
La solution

[TP] Zozor dans son enclos


Amis de la SPA (Société Protectrice des Animaux), ce tuto n'est pas pour vous :p ! En e!et, il
consiste purement et simplement à confiner notre cher Zozor (la mascotte du Site du Zér0)
comme on le fait de nos jours pour de vulgaires volailles (clin d'oeil à la crise de la grippe
aviaire :lol: ). Mais cette séquestration ne sera pas sans but, elle vous permettra au moins
d'entretenir vos connaissances en C ! Alors, pourquoi pas ? ^^

À la base, j'avais créé ce petit programme, certes inutile mais assez marrant, pour
entretenir mes connaissances après la lecture du tuto de M@teo jusqu'au chapitre des
évènements. Ceci détermine d'ailleurs le niveau requis pour bien comprendre mon tuto.
Donc, si vous n'avez pas encore lu le tuto de M@teo, je vous recommande de le faire
jusqu'au chapitre des évènements.

Le projet

Mettre Zozor dans son enclos o_O ? Je ne comprends pas trop où tu veux en venir :o !

C'est simple : dans ce TP, nous allons ouvrir une fenêtre dans laquelle le curseur est remplacé par une
image de Zozor (à fond transparent). On placera dans cette fenêtre, à 40 pixels de chaque bord, une
clôture de 10 px d'épaisseur (représentée par un simple rectangle noir). Les mouvements de Zozor
(dirigés par la souris) seront limités à l'intérieur des "clôtures" (autrement dit on ne pourra pas
approcher la souris à moins de 50 pixels du bord de la fenêtre). :)

Par où commencer ? :euh:

Je vais vous aider en vous donnant la structure du code ainsi que quelques informations ;) . Ah oui, je
vous fournis aussi l'image de Zozor ;) (clic droit + Enregistrer l'image sous... pour la télécharger) :
:
1. Les variables
Je ne m'étends pas là-dessus, vous verrez bien au fur et à mesure de quelles variables & pointeurs
vous aurez besoin. Par contre, je précise que vous n'aurez pas besoin de 4 surfaces pour les
"clôtures". En e!et, une surface pour les verticales (gauche / droite) et une autre pour les horizontales
(haut / bas) su!isent. Celles-ci seront blittées 2 fois chacune à deux endroits di!érents. ;)

2. La fenêtre
Petite précision : la fenêtre fera 640 px de large sur 480 px de haut et sera en 32 bits. N'oubliez pas le
double bu!ering. Je vous laisse choisir le titre et la couleur de fond (bien qu'un vert imitation herbe
serait pas mal :p )!

3. Les "clôtures"
Dans cette section, vous allez déterminer la dimension des clôtures, de préférence par rapport à la
largeur / hauteur de la fenêtre. Il su!it de faire une soustraction entre la hauteur / largeur de la
fenêtre (ecran->w ou ecran->h) et les deux "marges" à laisser entre le bord de celle-ci et la "clôture".
N'oubliez pas ce que j'ai dit au début : 40 px de "marge" (que l'on peut considérer comme l'extérieur
de l'enclos) + 10 px d'épaisseur de "clôture" pour un total de 50 px de chaque bord dans lesquels
Zozor ne pourra pas gambader ;) . Oups! J'en ai déjà trop dit :-° :p ...

Pour vous aider, voici un schéma qui vous permettra de mieux comprendre ce que j'attends de vous.

4. Placement initial de Zozor


On charge bien sûr l'image de Zozor dans sa surface et on la rend transparente, mais nous allons
aussi nous occuper de deux petites choses qui feront la di!érence. Premièrement, il faut faire
disparaître le curseur pour que l'utilisateur ait l'impression de faire bouger Zozor et non pas un
:
curseur avec notre cher animal derrière car, comme je l'ai dit au début, l'image du mulet suivra le
déplacement de la souris. En second lieu, il faut placer initialement Zozor (et donc le curseur) au
centre de la fenêtre.

J'ai bien dit placer Zozor au centre. Il faut donc que l'image soit centrée mais si vous vous
contentez de placer le point curseur au milieu de la fenêtre, l'image ne le sera pas tout à fait,
car le curseur se situe dans le bord supérieur gauche de celle-ci, et non pas en son centre. À
vous de réfléchir (utilisez les dimensions de l'image) ;) !

5. La boucle des évènements


Voici venir... la boucle!!! :D
En bref, quand on clique sur la croix en haut à droite, le programme doit se fermer, idem quand on
appuie sur n'importe quelle touche du clavier. Jusque-là, c'est simple. Ensuite vient le moment de
limiter les déplacements de la souris (de Zozor) grâce au type d'évènement SDL_MOUSEMOTION. À
l'aide de 4 conditions if (une pour chaque bord), vous allez vérifier si le curseur a dépassé une
certaine abcisse (x) ou une certaine ordonnée (y). Si tel est le cas, il faudra le déplacer à nouveau à
l'intérieur de l'enclos. (Non pas au milieu mais contre le bord, ce qui donnera à l'utilisateur
l'impression que Zozor se heurte à la clôture et ne peut la franchir :lol: . Je suis sadique, mouahaha :p
.) N'oubliez quand même pas qu'après il faut mettre à jour la position de la surface de Zozor selon les
coordonnées de l'emplacement du curseur (pour créer l'impression que Zozor suit le déplacement de
la souris).
Pour terminer, à chaque passage dans la boucle (et donc à chaque changement de position de
Zozor), il faut e!acer la fenêtre et blitter à nouveau l'ensemble des surfaces (attention au SDL_Flip() ;)
).

6. On libère la mémoire
Juste pour rappel, il n'y a que la surface principale (ecran) qui ne doit pas être libérée par la fonction
SDL_FreeSurface() : il y en a donc quelques-uns à faire ^^ .

La solution
Alors... vous avez réussi ? :) J'espère que oui. Mais si ce n'est pas le cas, pas d'inquiétude ; on
n'apprend pas qu'en réussissant ;) mais aussi en se trompant (à condition de se corriger). Voici donc
la solution présentée de la même manière que le projet.

(0. Les includes)


Je les mets pour être sûr que vous n'en oublierez aucun ;) .
:
#include <stdlib.h>
#include <stdio.h>
#include <SDL/SDL.h>

int main(int argc, char *argv[])


{

J'espère que vous n'aviez pas oublié la SDL (sinon honte à vous, mouahaha :p ).

1. Les variables
/*
1. Déclaration des variables
*/
SDL_Surface *ecran = NULL, *zozor = NULL;
SDL_Surface *clotureVerticale = NULL, *clotureHorizontale = NULL;
SDL_Event event;
SDL_Rect positionZozor;
SDL_Rect positionClotureGH, positionClotureD, positionClotureB;
int continuer = 1;

Alors, dans l'ordre on a :

2 surfaces (la principale et celle de Zozor) ;

encore 2 surfaces (pour les 2 types de clôtures : verticales & horizontales) ;

une variable pour stocker les évènements ;

la variable du positionnement de Zozor ;

3 variables de positionnement pour les clôtures ;

1 booléen pour la boucle des évènements.

Petite précision à propos des variables de positionnement des clôtures : la lettre en majuscule à la fin
du nom de la variable signifie son emplacement (G pour gauche, H pour haut, D pour droite et B pour
bas).

On peut s'apercevoir que la clôture gauche et celle du haut auront la même position. Si vous
ne comprenez pas pourquoi maintenant, patientez un peu, je vous le montrerai lors de leur
blittage.

2. La fenêtre
:
/*
2. On s'occupe de la fenêtre
*/
SDL_Init(SDL_INIT_VIDEO);

ecran = SDL_SetVideoMode(640, 480, 32, SDL_HWSURFACE | SDL_DOUBLEBUF);


SDL_WM_SetCaption("Pauvre Zozor.", NULL);
//On colore le fond en vert (imitation herbe... :lol: )
SDL_FillRect(ecran, NULL, SDL_MapRGB(ecran->format, 10,255,10));

Pas besoin d'explications, je présume. Il ne fallait pas oublier de mettre le double bu!ering.

3. Les clôtures
Ici, j'a!iche les réponses en plusieurs fois.

/*
3. Les clôtures
*/
// On détermine les dimensions des clôtures verticales et horizontales
clotureVerticale = SDL_CreateRGBSurface(SDL_HWSURFACE, 10, ecran->h - 2*40, 3
2, 0, 0, 0, 0);
clotureHorizontale = SDL_CreateRGBSurface(SDL_HWSURFACE, ecran->w - 2*40, 10,
32, 0, 0, 0, 0);

Commençons par le début : la dimension des fameuses barrières.


Le calcul des dimensions que j'ai mis dans la fonction SDL_CreateRGBSurface est assez logique.
Parlons de la clôture verticale. Pour la largeur, nous nous étions mis d'accord (ou plutôt je vous ai dit
:p ) de mettre 10 px. Pour la hauteur, c'est plus compliqué. On commence par prendre la hauteur de la
fenetre (ecran->h) à laquelle on doit soustraire 2 * 40 px (ce sont les marges de part et d'autre de
l'extérieur de l'enclos). Et voilà, c'est tout ^^ ! Pour la clôture horizontale, il su!it d'intervertir ces
deux données (la largeur devient la hauteur et vice-versa). On continue :

// On détermine la position des 4 clôtures


// Celle de gauche et celle du haut (elles ont la même origine)
positionClotureGH.x = 40;
positionClotureGH.y = 40;
// Celle de droite
positionClotureD.x = ecran->w - 50;
positionClotureD.y = 40;
// Celle du bas
positionClotureB.x = 40;
positionClotureB.y = ecran->h -50;
:
C'est ici que c'est un peu casse-tête (pour ne pas dire casse-gu*** :-° ). En e!et, il faut bien
comprendre que le positionnement d'une surface est déterminé par son coin supérieur gauche (c'est
son origine). À partir de ce moment-là, on peut s'aider d'un petit schéma :

Grâce aux mesures que j'ai a!ichées en dessous, on peut maintenant facilement déterminer les
coordonnées de chaque origine et s'apercevoir qu'il faut, pour obtenir l'ordonnée de la clôture du
bas, faire "la hauteur de la fenêtre moins 50 px" (soit ecran->h - 50 pour l'ordonnée) alors que pour
obtenir l'abcisse de la clôture de droite, on fait "la largeur de l'écran moins 50 px" (soit ecran->w - 50
pour l'abcisse). Compris ? :p

Si vous n'aviez pas compris tout à l'heure pourquoi la clôture du haut et celle de gauche
avaient la même position, vous en avez maintenant l'explication : les points G et H se
superposent ^^ .

Fiou, il reste plus qu'à blitter tout ça :D :

// On blitte les 4 clôtures aux bons endroits


//Celle de gauche
SDL_BlitSurface(clotureVerticale, NULL, ecran, &positionClotureGH);
//Celle de droite
SDL_BlitSurface(clotureVerticale, NULL, ecran, &positionClotureD);
//Celle du haut
SDL_BlitSurface(clotureHorizontale, NULL, ecran, &positionClotureGH);
//Celle du bas
SDL_BlitSurface(clotureHorizontale, NULL, ecran, &positionClotureB);
:
Et voilà, on en a fini avec les clôtures.

4. Placement initial de Zozor


/*
4. Placement initial de Zozor
*/
//On charge l'image de Zozor dans la surface et on rend le fond transparent
zozor = SDL_LoadBMP("zozor.bmp");
SDL_SetColorKey(zozor, SDL_SRCCOLORKEY, SDL_MapRGB(zozor->format, 0, 0, 255));

// On rend le curseur invisible et on le centre (Zozor devient le curseur)


SDL_ShowCursor(SDL_DISABLE);
SDL_WarpMouse((ecran->w / 2 - zozor->w / 2), (ecran->h / 2 - zozor->h / 2));

Hormis le fait qu'on charge l'image de Zozor dans sa surface et qu'on rend le fond transparent (je
suppose que ça, c'était OK de votre côté), il faut rendre le curseur invisible grâce à la fonction
SDL_ShowCursor(). De plus, on doit déplacer Zozor au milieu de la fenêtre. Mais il ne faut pas perdre
de vue que, lorsque la boucle d'évènements fera que Zozor suit les déplacements de la souris, le
curseur sera dans le coin supérieur gauche de l'image (testez en n'e!açant pas le curseur et vous
verrez). De ce fait, il ne su!it pas de faire :

SDL_WarpMouse(ecran->w / 2, ecran->h / 2);

En e!et, il faut faire une soustraction entre la largeur / hauteur de la fenêtre et la largeur / hauteur de
Zozor pour obtenir les bonnes coordonnées (comme dans la solution ci-dessus). Vous ne comprenez
pas ? Pas de bol, c'est comme ça, les maths :lol: !

5. La boucle principale
/*
5. La boucle principale
*/
while(continuer)
{
SDL_WaitEvent(&event);

switch(event.type)
{
case SDL_QUIT:
continuer = 0;
break;

// En appuyant sur une touche, on quitte le programme


case SDL_KEYDOWN:
:
continuer = 0;
break;

// On limite les déplacements de Zozor à son enclos (mouahaha)


case SDL_MOUSEMOTION:
// Empêcher le dépassement vers la droite
if(event.motion.x > ecran->w - zozor->w - 50)
SDL_WarpMouse(ecran->w - zozor->w - 50, event.motion.y);

// Empêcher le dépassement vers la gauche


if(event.motion.x < 50)
SDL_WarpMouse(50, event.motion.y);

// Empêcher le dépassement vers le haut


if(event.motion.y < 50)
SDL_WarpMouse(event.motion.x, 50);

// Empêcher le dépassement vers le bas


if(event.motion.y > ecran->h - zozor->h - 50)
SDL_WarpMouse(event.motion.x, ecran->h - zozor->h - 50);

positionZozor.x = event.motion.x;
positionZozor.y = event.motion.y;
break;
}

// On efface et on blitte à nouveau toutes les surfaces aux éventuels nouv


eaux emplacements
SDL_FillRect(ecran, NULL, SDL_MapRGB(ecran->format, 0, 255, 0));
// Zozor
SDL_BlitSurface(zozor, NULL, ecran, &positionZozor);
// Les clôtures
SDL_BlitSurface(clotureVerticale, NULL, ecran, &positionClotureGH);
SDL_BlitSurface(clotureVerticale, NULL, ecran, &positionClotureD);
SDL_BlitSurface(clotureHorizontale, NULL, ecran, &positionClotureGH);
SDL_BlitSurface(clotureHorizontale, NULL, ecran, &positionClotureB);

SDL_Flip(ecran);
}

Parlons un peu du traitement des évènements. En dehors de l'habituelle fermeture en cliquant sur la
croix, on retrouve un autre moyen de fermeture en appuyant sur une touche du clavier. J'ai fait cela
pour permettre de quitter plus facilement le programme car, comme la souris est coincée dans
:
l'enclos, il n'est pas possible d'atteindre la croix (sauf en faisant un mouvement rapide ;) ). Ensuite, en
cas d'évènement MOUSEMOTION (quand la souris bouge), à l'aide des conditions if, on teste si Zozor
ne sort pas de ses frontières :p . Si c'est le cas, on déplace la souris.
Lors du même évènement il faut modifier le positionnement de Zozor en remplaçant l'initial par les
coordonnées de la souris.
Pour terminer, on e!ace et on blitte à nouveau toutes les surfaces aux éventuels nouveaux
emplacements :D (sans oublier le SDL_Flip() ;) ).

6. On libère la mémoire
/*
6. Pour terminer
*/
SDL_Flip(ecran);
SDL_FreeSurface(zozor);
SDL_FreeSurface(clotureVerticale);
SDL_FreeSurface(clotureHorizontale);
SDL_Quit();

return EXIT_SUCCESS;
}

Aucune explication n'est nécessaire, on libère seulement la mémoire utilisée pour stocker les
surfaces ^^ .

Le code en entier
#include <stdlib.h>
#include <stdio.h>
#include <SDL/SDL.h>

int main(int argc, char *argv[])


{
/*
1. Déclaration des variables
*/
SDL_Surface *ecran = NULL, *zozor = NULL;
SDL_Surface *clotureVerticale = NULL, *clotureHorizontale = NULL;
SDL_Event event;
SDL_Rect positionZozor;
SDL_Rect positionClotureGH, positionClotureD, positionClotureB;
int continuer = 1;
:
/*
2. On s'occupe de la fenêtre
*/
SDL_Init(SDL_INIT_VIDEO);

ecran = SDL_SetVideoMode(640, 480, 32, SDL_HWSURFACE | SDL_DOUBLEBUF);


SDL_WM_SetCaption("Pauvre Zozor.", NULL);
//On colore le fond en vert (imitation herbe... :lol: )
SDL_FillRect(ecran, NULL, SDL_MapRGB(ecran->format, 10,255,10));

/*
3. Les clôtures
*/
// On détermine les dimensions des clôtures verticales et horizontales
clotureVerticale = SDL_CreateRGBSurface(SDL_HWSURFACE, 10, ecran->h - 2*4
0, 32, 0, 0, 0, 0);
clotureHorizontale = SDL_CreateRGBSurface(SDL_HWSURFACE, ecran->w - 2*40,
10, 32, 0, 0, 0, 0);

// On détermine la position des 4 clôtures


// Celle de gauche et celle du haut (elles ont la même origine)
positionClotureGH.x = 40;
positionClotureGH.y = 40;
// Celle de droite
positionClotureD.x = ecran->w - 50;
positionClotureD.y = 40;
// Celle du bas
positionClotureB.x = 40;
positionClotureB.y = ecran->h -50;

// On blitte les 4 clôtures aux bons endroits


//Celle de gauche
SDL_BlitSurface(clotureVerticale, NULL, ecran, &positionClotureGH);
//Celle de droite
SDL_BlitSurface(clotureVerticale, NULL, ecran, &positionClotureD);
//Celle du haut
SDL_BlitSurface(clotureHorizontale, NULL, ecran, &positionClotureGH);
//Celle du bas
SDL_BlitSurface(clotureHorizontale, NULL, ecran, &positionClotureB);

/*
4. Placement initial de Zozor
*/
:
//On charge l'image de Zozor dans la surface et on rend le fond transparen
t
zozor = SDL_LoadBMP("zozor.bmp");
SDL_SetColorKey(zozor, SDL_SRCCOLORKEY, SDL_MapRGB(zozor->format, 0, 0, 25
5));

// On rend le curseur invisible et on le centre (Zozor devient le curseur)


SDL_ShowCursor(SDL_DISABLE);
SDL_WarpMouse((ecran->w / 2 - zozor->w / 2), (ecran->h / 2 - zozor->h /
2));

/*
5. La boucle principale
*/
while(continuer)
{

SDL_WaitEvent(&event);

switch(event.type)
{
case SDL_QUIT:
continuer = 0;
break;

// En appuyant sur une touche, on quitte le programme


case SDL_KEYDOWN:
continuer = 0;
break;

// On limite les déplacements de Zozor à son enclos (mouahaha)


case SDL_MOUSEMOTION:
// Empêcher le dépassement vers la droite
if(event.motion.x > ecran->w - zozor->w - 50)
SDL_WarpMouse(ecran->w - zozor->w - 50, event.motion.y);

// Empêcher le dépassement vers la gauche


if(event.motion.x < 50)
SDL_WarpMouse(50, event.motion.y);

// Empêcher le dépassement vers le haut


if(event.motion.y < 50)
SDL_WarpMouse(event.motion.x, 50);
:
// Empêcher le dépassement vers le bas
if(event.motion.y > ecran->h - zozor->h - 50)
SDL_WarpMouse(event.motion.x, ecran->h - zozor->h - 50);

positionZozor.x = event.motion.x;
positionZozor.y = event.motion.y;
break;
}

// On efface et on blitte à nouveau toutes les surfaces aux éventuels


nouveaux emplacements
SDL_FillRect(ecran, NULL, SDL_MapRGB(ecran->format, 0, 255, 0));
// Zozor
SDL_BlitSurface(zozor, NULL, ecran, &positionZozor);
// Les clotures
SDL_BlitSurface(clotureVerticale, NULL, ecran, &positionClotureGH);
SDL_BlitSurface(clotureVerticale, NULL, ecran, &positionClotureD);
SDL_BlitSurface(clotureHorizontale, NULL, ecran, &positionClotureGH);
SDL_BlitSurface(clotureHorizontale, NULL, ecran, &positionClotureB);

SDL_Flip(ecran);
}

/*
6. Pour terminer
*/
SDL_Flip(ecran);
SDL_FreeSurface(zozor);
SDL_FreeSurface(clotureVerticale);
SDL_FreeSurface(clotureHorizontale);
SDL_Quit();

return EXIT_SUCCESS;
}

J'espère que ce TP vous a plu et surtout vous a permis de bien ancrer une partie de ce que vous avez
appris jusqu'ici.

Il y a bien sûr moyen de mettre des images à la place des clôtures noires toutes moches. On peut
aussi envisager de mettre une image d'herbe de 640 * 480 à la place du fond vert pour un meilleur
e!et. Si vous le faites, je serais très intéressé de voir le résultat :) .

Je terminerai en disant qu'aucun animal (en l'occurrence Zozor) n'a sou!ert pendant le codage de ce
programme :lol: !
:

Vous aimerez peut-être aussi