Exercice pratique : le FROGGER
Bonjour,
L'exercice du jour sera un FOGGER … crôa !
Pour ceux qui nous rejoindraient à ce stade, je vous engage à lire les exercices précédents (voir pré-requis) afin de comprendre la différence que je fais entre exercice et tutorial. Pour les autres, vous connaissez le principe alors on entre tout de suite dans le vif du sujet.
Jouons un peu pour voir le résultat
Utilisez les flèches de votre clavier.
Les sources sont disponibles en fin d'exercice.
Etude préliminaire
Tout d'abord le FROGGER c'est quoi ? (merci Wikipedia)
Frogger un jeu d'arcade développé par Konami et sorti en 1981. Il est généralement considéré comme un classique. Le but du jeu est de diriger des grenouilles jusqu'à leurs maisons. Pour cela, le joueur doit d'abord traverser une route en évitant les voitures puis une rivière en passant d'objets en objets.
Le record sur borne arcade est détenu par l'Américain Pat Laffaye le 5 janvier 2010, avec un score de 896,9801.
Cool, ça parait simple dit comme ça, voyons voir ça….
Les pré-requis
Je vous recommande fortement d'avoir au moins lu les exercices suivants : PONG, SNAKE, TAQUIN Les exercices sont courts mais de nombreuses astuces y sont proposées, je ne les expliquerai pas à chaque nouvel exercice.
Pour ce programme vous devez connaître :
Variables et types : http://help.adobe.com/fr_FR/FlashPlatform/reference/actionscript/3/statements.html#var
Fonctions et paramètres : http://help.adobe.com/fr_FR/FlashPlatform/reference/actionscript/3/statements.html#function
Manipulation de tableaux : http://help.adobe.com/fr_FR/FlashPlatform/reference/actionscript/3/Array.html
Ecouteurs d'événements : http://help.adobe.com/fr_FR/FlashPlatform/reference/actionscript/3/flash/events/package-detail.html
Filtres graphiques : http://help.adobe.com/fr_FR/FlashPlatform/reference/actionscript/3/flash/filters/package-detail.html
Exercice PONG : http://forums.mediabox.fr/wiki/tutoriaux/flashplatform/jeux/exercice_-_le_pong
Exercice TAQUIN : http://forums.mediabox.fr/wiki/tutoriaux/flashplatform/jeux/exercice_-_le_taquin
Exercice SNAKE : http://forums.mediabox.fr/wiki/tutoriaux/flashplatform/jeux/exercice_le_snake
Si vous souhaitez plus de précisions sur ces points, je vous encourage à parcourir le Wiki de Mediabox où vous trouverez de nombreux tutoriaux détaillés.
Le code
On se regarde tout ça d'un bloc avant de se lancer dans les explications.
// Objets var frog:Frog = new Frog(); var splat:Splat = new Splat(); var coule:Coule = new Coule(); var panneaux:Panneaux = new Panneaux(); // sons var sonFroggy:SonFroggy = new SonFroggy(); var sonPlouf:SonPlouf = new SonPlouf(); var sonEcrase:SonEcrase = new SonEcrase(); var sonSlot:SonSlot = new SonSlot(); // Variables var T:int = 32; var F:int = T*.5; var C:int = 15; var W:int = stage.stageWidth; var H:int = stage.stageHeight; var vies:int; var stock:Array; var box:Array; var lives:Array; // Initialisation de base du jeu addChild(new Bitmap(new Fond(0,0))); addChild(panneaux); panneaux.addEventListener(MouseEvent.CLICK, init); panneaux.buttonMode = true; // Effets graphiques var dropShadow:DropShadowFilter = new DropShadowFilter(2); frog.filters = new Array(dropShadow); // initialisation function init(e:Event):void{ stock = []; box = [1,3,5,7,9,11,13]; vies = 3; addChild(splat); addChild(coule); creeLignesObjets(); addChild(frog); initFrog(); stage.addEventListener(KeyboardEvent.KEY_DOWN, appuie); stage.addEventListener(Event.ENTER_FRAME, bougeObjets); removeChild(panneaux); lives = [new Lifes(),new Lifes(),new Lifes()]; for (var i:int=0;i<lives.length;i++) { lives[i].x = i*T+F; lives[i].filters = new Array(dropShadow); addChild(lives[i]); } } // Création des lignes d'objets function creeLignesObjets():void{ var i:int; // voitures for (i=0; i<3; i++) creeObjets(i*175, 13, 1, 2, new Voitures()); for (i=0; i<3; i++) creeObjets(i*175, 12, 2,-3, new Voitures()); for (i=0; i<2; i++) creeObjets(i*262, 11, 3, 4, new Voitures()); for (i=0; i<3; i++) creeObjets(i*175, 10, 4,-5, new Voitures()); for (i=0; i<2; i++) creeObjets(i*262, 09, 1, 4, new Voitures()); // flotteurs for (i=0; i<3; i++) creeObjets(i*175, 02, 4,-3, new Flotteurs()); for (i=0; i<3; i++) creeObjets(i*175, 03, 3, 2, new Flotteurs()); for (i=0; i<3; i++) creeObjets(i*175, 04, 2,-4, new Flotteurs()); for (i=0; i<2; i++) creeObjets(i*262, 05, 3, 3, new Flotteurs()); for (i=0; i<3; i++) creeObjets(i*175, 06, 2,-2, new Flotteurs()); for (i=0; i<6; i++) creeObjets(i*096, 07, 4, 1, new Flotteurs()); } // création des objets function creeObjets(X:int,Y:int,N:int,V:int,E:MovieClip):void{ E.x = X; E.y = T*Y; E.gotoAndStop(N); E.vit = V; if(E.vit<0) E.scaleX = -1; stock.push(E); E.filters = new Array(dropShadow); addChild(E); } // Initialisation de la grenouille function initFrog():void{ frog.x = W*.5; frog.y = H-F; frog.rotation = 0; if(vies==0) finPartie(2); } // Gestion des contrôles function appuie(e:KeyboardEvent):void { var k:int = e.keyCode; bougeFrog(int(k==39)-int(k==37),int(k==40)-int(k==38)); } // Déplacement de la grenouille function bougeFrog(X:int, Y:int):void{ frog.x += X*T; frog.y += Y*T; frog.rotation = X*90+(Y+Math.abs(Y))*90; frog.play(); sonFroggy.play(); limiteFrog(); placeFrog(); } // Déplacement des objets function bougeObjets(e:Event):void{ var eau:Boolean = true; for (var i:int=0; i<stock.length; i++){ with(stock[i]){ x += vit; if (x<0 && vit<0) x = W+width; if (x>W && vit>0) x = -width; if(hitTestPoint(frog.x, frog.y)){ if(i<13) effet(splat, sonEcrase) else frog.x += vit; eau = false; } } } if(eau && frog.y<T*8 && frog.y>T*2) effet(coule, sonPlouf); } // Limites du terrain function limiteFrog():void{ with(frog){ if (x>W-F) x = W-F; if (x<F) x = F; if (y>H-F) y = H-F; if (y<F) y = F; } } // Placement des slots de victoire function placeFrog():void{ if(frog.y<T*2) { var P:int = frog.x/T; for (var i:int=0;i<box.length;i++){ if(P==box[i]){ var G:Frog = new Frog(); G.x = P*T+F; G.y = T+F; G.rotation = 180; addChild(G); initFrog(); box.splice(i,1); sonSlot.play(); if(box.length==0) finPartie(3); break; } else { frog.y = T*2+F; } } } } // Effets et mort de la grenouille function effet(E:MovieClip, S:Sound):void{ E.x = frog.x; E.y = frog.y; E.gotoAndPlay(1); S.play(); initFrog(); vies--; if(lives.length){ removeChild(lives[vies]) lives.splice(vies,1); } } // Affiche le panneau de fin de partie function finPartie(end:int):void{ while(numChildren>1) removeChildAt(1); addChild(panneaux); panneaux.gotoAndStop(end) stage.removeEventListener(KeyboardEvent.KEY_DOWN, appuie); stage.removeEventListener(Event.ENTER_FRAME, bougeObjets); }
Etude du programme
Puisque nous travaillons avec Flash j'utilise la bibliothèque et quelques astuces de base qui me permettent d'alléger mon code. Pour ceux qui n'utilisent pas Flash voici un rapide rappel des bidouilles utilisées que vous devrez adapter dans votre programme (par exemple si vous utilisez les spritesheets).
Grenouille :
- le repère de position est au centre de l'objet
- l'animation du saut est faite sur 5 frames et lancée par un simple “play”
Flotteurs :
- tous les flotteurs sont réunis dans le même clip conteneur
- chaque flotteur occupe une frame différente
Voitures :
- toutes les voitures sont réunies dans un même clip conteneur
- chaque voiture occupe une frame différente
Effets :
- “coule” et “splat” sont des clips contenant une animation
- la première frame de l'animation est vide et contient un “stop”
- le repère de position est au centre de l'objet
Panneaux :
- tous les panneaux sont réunis dans un même clip conteneur
- chaque panneau occupe une frame différente
Pour tout le reste il s'agit d'objets exportés pour AS depuis la bibliothèque, ils n'ont pas subit de manipulation particulière, vous pouvez donc les gérer tout simplement via des ressources externes.
Allez jetons nous dans l'étude du programme proprement dit…
// Objets var frog:Frog = new Frog(); var splat:Splat = new Splat(); var coule:Coule = new Coule(); var panneaux:Panneaux = new Panneaux(); // sons var sonFroggy:SonFroggy = new SonFroggy(); var sonPlouf:SonPlouf = new SonPlouf(); var sonEcrase:SonEcrase = new SonEcrase(); var sonSlot:SonSlot = new SonSlot();
Rien de compliqué si vous avez les pré-requis, on crée nos objets et nos sons utiles pour le programme.
// Variables var T:int = 32; var F:int = T*.5; var C:int = 15; var W:int = stage.stageWidth; var H:int = stage.stageHeight; var vies:int; var stock:Array; var box:Array; var lives:Array;
On déclare les variables globales, vous noterez que j'ai volontairement utilisé des initiales pour les noms des variables les plus courantes, ceci permet de simplifier la lecture des formules. Pour information :
T = largeur d'une tuile ou case de la grille
F = largeur d'une demi tuile ou case
C = nombre de colonnes et de lignes de la grille
W = largeur de la zone de jeu
H = hauteur de la zone de jeu
vies = le nombre de vies
stock = le stockage des objets du jeu
box = le stockage des slots d'arrivée
lives = le stockage des vies pour l'affichage
// Initialisation de base du jeu addChild(new Bitmap(new Fond(0,0))); addChild(panneaux); panneaux.addEventListener(MouseEvent.CLICK, init); panneaux.buttonMode = true;
Ha première chose encore inconnue si vous avez suivit les exercices précédents, ici on initialise les paramètres de base du jeu, et on commence par ajouter un fond :
addChild(new Bitmap(new Fond(0,0)));
Je ne vais jamais toucher au fond du jeu et il est présent tout le temps, je n'ai donc pas besoin de le déclarer à l'aide d'une variable réutilisable, je peux ajouter directement à la liste d'affichage un nouveau bitmap composé de l'image de fond (exportée pour AS depuis la bibliothèque).
On ajoute ensuite le panneau de départ et son interactivité.
// Effets graphiques var dropShadow:DropShadowFilter = new DropShadowFilter(2); frog.filters = new Array(dropShadow);
Je crée un nouveau filtre graphique (une ombre portée), que je vais utiliser pour plusieurs objets et je commence par l'ajouter tout de suite à la grenouille.
// initialisation function init(e:Event):void{ stock = []; box = [1,3,5,7,9,11,13]; vies = 3; addChild(splat); addChild(coule); creeLignesObjets(); addChild(frog); initFrog(); stage.addEventListener(KeyboardEvent.KEY_DOWN, appuie); stage.addEventListener(Event.ENTER_FRAME, bougeObjets); removeChild(panneaux); lives = [new Lifes(),new Lifes(),new Lifes()]; for (var i:int=0;i<lives.length;i++) { lives[i].x = i*T+F; lives[i].filters = new Array(dropShadow); addChild(lives[i]); } }
Lorsqu'on cliques sur le panneau de départ on lance l'initialisation, notez que tous les panneaux lancent l'initialisation pour relancer le jeu, c'est important pour la suite.
Voyons en détail les choses importantes :
box = [1,3,5,7,9,11,13];
Ici je détermine quels sont les slots libres pour l'arrivée de la grenouille.
Ce jeu fonctionne à l'aide d'une grille, un tableau à deux dimensions où chaque ligne est composée d'un certain nombre de colonnes. Je numérote mes colonnes à partir de 0, j'indique donc dans mon tableau que les slots impairs sont libres, la grenouille peut donc s'y loger, nous verrons comment par la suite.
addChild(splat); addChild(coule);
Ces deux effets sont appelés une seule fois à la fois, il est donc inutile de les supprimer et les réafficher à chaque fois que l'on en a besoin, ils sont donc affichés une seule fois en début de partie, et on les place ensuite à l'endroit voulu. J'ai utilisé une petite astuce pour qu'on ne les voie que lorsqu'on les joues, la première frame de leur animation est une frame vide avec un code “stop”.
creeLignesObjets();
On crée d'un coup toutes les lignes des objets, nous y reviendrons par la suite.
lives = [new Lifes(),new Lifes(),new Lifes()];
Je n'ai pas besoin de créer une variable par vie affichée car elle ne servent qu'à l'affichage.
Il me suffit de stocker dans un tableau trois objets de base “Lifes” (le graphisme des vies), notez simplement qu'il n'est pas toujours utile de s'encombrer de variables pour créer des objets, on peut le faire directement si les conditions s'y prêtent.
for (var i:int=0;i<lives.length;i++) { lives[i].x = i*T+F; lives[i].filters = new Array(dropShadow); addChild(lives[i]); }
Là je vais placer toutes les occurrences de “Lifes” de mon tableau, et je leur ajoute une ombre portée. Comme vous pouvez le constater, je peux tout à fait cibler mes occurrences même si elles ne sont pas déclarées dans des variables, elles sont dans un tableau de stockage accessible via le code.
// Création des lignes d'objets function creeLignesObjets():void{ var i:int; // voitures for (i=0; i<3; i++) creeObjets(i*175, 13, 1, 2, new Voitures()); for (i=0; i<3; i++) creeObjets(i*175, 12, 2,-3, new Voitures()); for (i=0; i<2; i++) creeObjets(i*262, 11, 3, 4, new Voitures()); for (i=0; i<3; i++) creeObjets(i*175, 10, 4,-5, new Voitures()); for (i=0; i<2; i++) creeObjets(i*262, 09, 1, 4, new Voitures()); // flotteurs for (i=0; i<3; i++) creeObjets(i*175, 02, 4,-3, new Flotteurs()); for (i=0; i<3; i++) creeObjets(i*175, 03, 3, 2, new Flotteurs()); for (i=0; i<3; i++) creeObjets(i*175, 04, 2,-4, new Flotteurs()); for (i=0; i<2; i++) creeObjets(i*262, 05, 3, 3, new Flotteurs()); for (i=0; i<3; i++) creeObjets(i*175, 06, 2,-2, new Flotteurs()); for (i=0; i<6; i++) creeObjets(i*096, 07, 4, 1, new Flotteurs()); }
On entre tout de suite dans le vif du sujet pour cet exercice, à savoir la création des différents objets.
Le jeu est conçut sous la forme d'une grille ou d'un tableau, on travaille donc avec des cases, des lignes et des colonnes.
J'ai 11 lignes d'objets en tout, 5 pour les voitures, et 6 pour les flotteurs.
Pour chaque ligne je fais une petite boucle sur le nombre d'objets souhaités dans la ligne.
Pour chaque itération de chaque boucle je crée un nouvel objet auquel je passe les paramètres suivants :
[position X, position Y, frame affichée, vitesse, type d'objet à créer]
Intéressons nous à la “position X”, vous remarquerez que j'utilise un nombre que je multiplie par “i” le pointeur de la boucle. Ceci me permet d'espacer mes objets dans la ligne, il faut bien faire attention à la taille de vos objets si vous souhaitez obtenir un résultat cohérent, c'est pourquoi je préfère utiliser des nombres plutôt que des calculs normés en cases du tableau.
Tous mes objets sont stockés dans des tableaux, de fait là encore je n'ai pas besoin de me servir d'une variable pour les déclarer, il me suffit de parcourir le tableau de stockage pour retrouver mon objet, c'est pourquoi je crée directement un objet du bon type en fin de chaque ligne, que je passerai en paramètre de la fonction de création des objets, que nous allons étudier tout de suite.
// création des objets function creeObjets(X:int,Y:int,N:int,V:int,E:MovieClip):void{ E.x = X; E.y = T*Y; E.gotoAndStop(N); E.vit = V; if(E.vit<0) E.scaleX = -1; stock.push(E); E.filters = new Array(dropShadow); addChild(E); }
Rien de bien compliqué pour le début, on positionne l'objet, on affiche la bonne frame du conteneur, on lui affecte un paramètre correspondant à sa vitesse propre.
if(E.vit<0) E.scaleX = -1;
Ici j'oriente mon objet en fonction du sens de déplacement, par exemple pour une voiture, si elle se déplace vers la gauche je la met dans le bon sens, c'est une astuce graphique qui me permet d'éviter de créer un objet différent par direction.
stock.push(E);
J'ajoute mon nouvel objet au stock, TOUS les objets sont donc stockés dans le même tableau, que ce soient les voitures ou les flotteurs, malgré que leur comportement soient différents. Là encore nous allons utiliser une petite astuce que nous détaillerons dans la gestion des objets.
Bien, tous nos objets sont créés, intéressons-nous un peu à notre grenouille.
// Initialisation de la grenouille function initFrog():void{ frog.x = W*.5; frog.y = H-F; frog.rotation = 0; if(vies==0) finPartie(2); }
J'utilise une fonction générique pour initialiser la grenouille, tout simplement parce que je vais avoir besoin de le faire dans plusieurs cas de figure, départ du jeu, mort de la grenouille, placement correct dans un slot, etc… Comme lorsque la grenouille est tuée on la réinitialise, c'est également à cet endroit que je vérifie si la partie est perdue, auquel cas j'appelle le bon panneau d'affichage.
// Gestion des contrôles function appuie(e:KeyboardEvent):void { var k:int = e.keyCode; bougeFrog(int(k==39)-int(k==37),int(k==40)-int(k==38)); }
Nous avons vus dans les exercices précédent pourquoi j'utilise une conversion en entier d'une condition (qui renvoie généralement un Boolean), et à quoi sert la soustraction entre les deux entiers. Vous noterez ici qu'on ne se sert pas de variables globales pour indiquer la direction (haut, bas, droite, gauche) et que je renvoie directement la valeur à la fonction de déplacement de la grenouille. C'est parce que je suis un peu fainéant que j'ai fait ceci, dans le jeu original la grenouille se déplace de case en case dans le tableau et non de manière fluide. En AS3 la répétition des touches est conditionnée par le temps d'appuis sur la touche, ainsi lorsqu'on appuie sur une touche il faut un petit moment avant que le programme ne comprenne qu'on souhaite effectuer une répétition, ce qui nous convient tout à fait puisque cela impose un déplacement unique lors de l'appui sur la touche, cependant pour bien faire et imiter correctement le comportement du jeu d'origine il faudrait empêcher le programme de comprendre la répétition de la touche au bout d'un certain temps. J'ai jugé que ce n'était pas utile pour cet exercice, avec les pré-requis vous êtes en mesure d'affiner si vous le souhaitez, je n'utilise donc qu'un seul écouteur pour la gestion des contrôles.
Voyons comment la grenouille va réagir aux commandes.
// Déplacement de la grenouille function bougeFrog(X:int, Y:int):void{ frog.x += X*T; frog.y += Y*T; frog.rotation = X*90+(Y+Math.abs(Y))*90; frog.play(); sonFroggy.play(); limiteFrog(); placeFrog(); }
La grenouille se déplace de case en case, joue un petit son, et sa propre animation de saut, ça c'est pour les trucs classiques, voyons de plus près les petites choses intéressantes.
frog.rotation = X*90+(Y+Math.abs(Y))*90;
Ce petit calcul me permet d'orienter la grenouille dans la direction voulue, il est assez simple mais torturé, donc pas facile à comprendre.
X*90 me renvoie -90, 0 ou 90 selon si on se déplace à gauche, à droite ou pas du tout sur l'axe X.
(Y+Math.abs(Y))*90 me renvoie 0 ou 180 selon si on se déplace en haut, en bas ou pas du tout sur l'axe Y.
Le résultat du calcul me renvoie donc :
Droite = 90 + 0
Gauche = -90 + 0
Haut = 0 + 0
Bas = 0 + 180
A la fin de la fonction on trouve :
limiteFrog(); placeFrog();
On limite le déplacement de la grenouille à la taille du tableau, c'est à ça que sert la fonction “limiteFrog()” que je vous colle tout de suite comme ça c'est fait :
// Limites du terrain function limiteFrog():void{ with(frog){ if (x>W-F) x = W-F; if (x<F) x = F; if (y>H-F) y = H-F; if (y<F) y = F; } }
Rien de bien compliqué, on impose juste à la grenouille de ne jamais sortir du terrain de jeu.
Revenons à notre fonction de déplacement de la grenouille pour jeter un oeil à la dernière instruction.
placeFrog();
Cette fonction va servir à placer les grenouilles sur leur slots de fin, nous allons l'étudier tout de suite aussi même si elle n'apparaît que plus tard dans le programme, au moins on se sera débarrassé d'une grosse partie de la gestion de la grenouille.
// Placement des slots de victoire function placeFrog():void{ if(frog.y<T*2) { var P:int = frog.x/T; for (var i:int=0;i<box.length;i++){ if(P==box[i]){ var G:Frog = new Frog(); G.x = P*T+F; G.y = T+F; G.rotation = 180; addChild(G); initFrog(); box.splice(i,1); sonSlot.play(); if(box.length==0) finPartie(3); break; } else { frog.y = T*2+F; } } } }
Vous-vous rappelez que nous travaillons avec une grille.
On commence donc par regarder si la grenouille se trouve sur la ligne où se trouvent les slots de fin.
On parcours le tableau de stockage des slots pour vérifier que la grenouille est bien en face d'un slot disponible, si c'est le cas on crée une nouvelle instance de la grenouille que l'on place sur le slot et on réinitialise la grenouille pour la replacer en bas du tableau. Attention, le slot est à présent occupé par une nouvelle grenouille, il faut donc retirer du tableau de stockage des slots celui qui est occupé, ainsi la grenouille ne pourra plus s'y placer. Si tous les slots sont remplis la partie est gagnée, on le vérifie donc ici. Et enfin, si la grenouille n'est pas en face d'un slot libre on lui interdit d'aller plus haut, elle reprend donc sa ligne d'origine.
Cool, on viens de se débarrasser d'une grosse partie du programme, on va pouvoir se concentrer sur la gestion des objets.
// Déplacement des objets function bougeObjets(e:Event):void{ var eau:Boolean = true; for (var i:int=0; i<stock.length; i++){ with(stock[i]){ x += vit; if (x<0 && vit<0) x = W+width; if (x>W && vit>0) x = -width; if(hitTestPoint(frog.x, frog.y)){ if(i<13) effet(splat, sonEcrase) else frog.x += vit; eau = false; } } } if(eau && frog.y<T*8 && frog.y>T*2) effet(coule, sonPlouf); }
Cette fonction est appelée par un ENTER_FRAME, rappelez-vous les exercices précédents, ENTER_FRAME n'est pas une valeur sûre, mais elle suffit pour nos exercices, si vous voulez quelque chose de plus précis basez vous sur le temps réel qui passe.
Ici on va s'attaquer à la gestion de tous les objets, que ce soient les voitures ou les flotteurs, et bien sur chaque objet à une influence directe sur la grenouille, dans le cas d'une voiture un contact la tue, dans le cas d'un flotteur un contact lui permet d'éviter de tomber dans l'eau et la déplace à la vitesse du flotteur.
var eau:Boolean = true;
On commence par déclarer une petite variable qui indique si la grenouille tombe dans l'eau ou pas, par défaut on dit que oui, mais on va vérifier ça au cours de la fonction.
Je crée une boucle sur tous les objets présents dans le stock, et pour chaque objet que je trouve (l'utilisation de “with()” à été expliquée lors des précédents exercices), je le déplace sur l'axe X à sa propre vitesse, si il sort du tableau je le replace de l'autre côté de la ligne, et si il touche la grenouille :
if(hitTestPoint(frog.x, frog.y)){ if(i<13) effet(splat, sonEcrase) else frog.x += vit; eau = false; }
Pour déterminer si un objet touche la grenouille je me sert d'un point et non de la surface de l'objet, ceci pour permettre un peu plus de souplesse au joueur, rappelez-vous que le repère de position de la grenouille se trouve au centre de l'objet, il n'y a donc contact que lorsqu'un objet touche le centre de la grenouille, c'est un peu plus réaliste.
Dans mon stock j'ai tous les objets, indifféremment de leur type, flotteur ou voiture, mais chaque type d'objet entraîne un comportement différent pour la grenouille, il me faut donc savoir quel type d'objet touche la grenouille et agir en conséquence. Lorsque j'ai rempli mon tableau de stockage, j'ai fait en sorte de créer d'abord toutes les voitures puis tous les flotteurs. Je sais que j'ai un certain nombre de voitures de créées, il suffit de compter le nombre d'itération des boucles de chaque ligne concernant les voitures pour connaître le nombre total de voitures présentes dans le tableau. Sachant que le stock est rempli de façon linéaire, tous les objets dont l'index se trouve sous le nombre total de voiture est une voiture et tous les objets se trouvant au delà de cet index sont des flotteurs. Ici j'ai 13 voitures donc dans ma boucle :
si “i” est inférieur à 13 = c'est une voiture
Sinon = c'est un flotteur
Si le contact se fait avec un flotteur on déplace la grenouille sur X à la vitesse du flotteur.
Si le contact se fait avec une voiture on lance un petit effet d'écrasement (on l'étudiera plus tard car je voudrai qu'on finisse d'étudier la fonction avant que vous perdiez le fil).
Enfin, si il y a contact que ce soit avec une voiture ou un flotteur, la grenouille ne touche pas l'eau, on passe donc la variable à false.
if(eau && frog.y<T*8 && frog.y>T*2) effet(coule, sonPlouf);
Une fois qu'on a vérifié le contact entre la grenouille et les objets, on vérifie si la grenouille touche l'eau. Pour cela, si il n'y a pas de contact, on regarde si la grenouille se trouve sur une ligne où il y a de l'eau, si c'est le cas, elle coule.
Ce qui me permet de faire la transition avec la fonction “effet()”, qui comprend deux paramètres, l'effet et le son joué par l'effet.
// Effets et mort de la grenouille function effet(E:MovieClip, S:Sound):void{ E.x = frog.x; E.y = frog.y; E.gotoAndPlay(1); S.play(); initFrog(); vies--; if(lives.length){ removeChild(lives[vies]) lives.splice(vies,1); } }
On commence par placer le clip de l'effet sur la position de la grenouille et on lui demande de jouer son animation, puis on joue le son associé, jusque là rien de compliqué. On initialise d'abord la grenouille avant de supprimer une vie, rappelez-vous que c'est dans l'initialisation de la grenouille que nous vérifions si la partie est perdu, ceci permet de d'avoir 4 vies et non 3 car généralement dans les jeux on compte 3 vies plus la vie en cours. Si vous souhaitez que le nombre de vies affichées corresponde au nombre de vie réel placez l'appel de l'initialisation de la grenouille après le décompte des vies.
vies--; if(lives.length){ removeChild(lives[vies]) lives.splice(vies,1); }
On décrémente le nombre de vies, puis on vérifie que le tableau des vies contient encore une vie, si c'est le cas on la retire du tableau et de l'affichage.
C'est presque fini, il nous reste à voir ce qu'il se passe lorsqu'une partie est terminée.
// Affiche le panneau de fin de partie function finPartie(end:int):void{ while(numChildren>1) removeChildAt(1); addChild(panneaux); panneaux.gotoAndStop(end) stage.removeEventListener(KeyboardEvent.KEY_DOWN, appuie); stage.removeEventListener(Event.ENTER_FRAME, bougeObjets); }
Tous les panneaux sont regroupés dans un seul conteneur, c'est pourquoi la fonction prend en paramètre une variable nommé “end”, c'est elle qui indique quel panneau afficher.
while(numChildren>1) removeChildAt(1);
Tous les objets du jeu sont affichés au même endroit, le scénario principal, il me suffit donc de regarder le nombre d'objets présent à cet endroit et de tous les retirer jusqu'au dernier (que je conserve puisqu'il s'agit du fond du jeu que je souhaite garder tout le temps affiché).
J'affiche ensuite le panneau de fin, et je retire les écouteurs. Les tableaux, variables, écouteurs et objets sont redéfinis lorsque j'initialise le jeu. Attention, cela fonctionne car on n'a pas beaucoup de choses à gérer pour ce jeu, mais normalement si vous souhaitez libérer de la mémoire en fin de partie il faut penser à retirer proprement tous vos objets et écouteurs et à réinitialiser les tableaux à ce moment là.
Et voilà c'est terminé
Conclusion
Dans ce petit programme on commence à travailler de manière abstraite, c'est à dire avec un tableau qu'on ne vois pas réellement, il faut garder à l'esprit qu'on travaille toujours avec une grille virtuelle qu'on a jamais réellement définie. On effectue aussi une gestion des objets en masse, et plus au cas par cas, tout ceci nous mène progressivement vers des astuces de programmation qui seront de plus en plus abstraites mais aussi plus efficaces, elle rendent la rédaction du programme plus courte, mais imposent des efforts pour se rappeler de la structure.
Les sources
