Chapitre 34
Améliorons nos menus et notre jeu !
Tutoriel présenté par : Jérémie F. Bellanger (Jay)
Dernière mise à jour : 22 juillet 2014
Dans ce nouveau chapitre, nous allons encore fignoler notre jeu, en améliorant tout d'abord les menus. En effet, le menu actuel nous fait systématiquement commencer au niveau 1, ce qui est relativement classique.
Mais, voilà, si vous voulez créer 99 levels, voire même 999 ! En recommençant à 0 à chaque fois, et sans sauvegarde, ça va être difficile !...
C'est pourquoi, nous allons d'abord rajouter la possibilité de choisir son niveau de 1 à LevelMax au menu de l'écran-titre.
Ensuite, nous améliorerons un peu l'écran Pause en lui donnant la possibilité, soit de continuer le jeu, soit de retourner à l'écran-titre (pour changer de niveau par exemple ).
Et enfin, notre dernière amélioration, concernera le déplacement des zombies. En effet, vous aurez peut-être remarqué que nos zombies n'aiment pas, pour l'instant, se déplacer sur des plateformes traversables par le bas (pour le joueur), car le jeu les considère comme du vide (avec la variable BLANK_TILE). Nous verrons comment leur rétablir la possibilité de marcher sur les ponts et les toits des maisons et éviter des bugs, si vous essayez de les placer sur ces tiles.
Allez, on est parti !
Un nouveau menu d'écran-titre
Et voici donc pour commencer les deux nouvelles fonctions permettant de changer l'écran-titre.
Le plus simple, si vous mettez à jour votre projet au fur et à mesure, c'est de supprimer les 2 anciennes fonctions et de les remplacer par celles-ci.
Fichier : menu.c
void updateStartMenu(void) { //Si on appuie sur BAS if(input.down == 1) { //Si choice = O (il est sur start), on le met à 1 (quit) if(jeu.choice == 0) jeu.choice++; input.down = 0; } //Si on appuie sur HAUT if(input.up == 1) { //Si choice = 1 (il est sur Quit), on le met à 0 (Start) if(jeu.choice == 1) jeu.choice--; input.up = 0; } //Choix du level if(input.right == 1) { //Si choice = 1 (il est sur Quit), on le met à 0 (Start) if(jeu.level > = LEVEL_MAX) jeu.level = 1; else jeu.level++; input.right = 0; } if(input.left == 1) { //Si choice = 1 (il est sur Quit), on le met à 0 (Start) if(jeu.level < = 1) jeu.level = LEVEL_MAX; else jeu.level--; input.left = 0; } //Si on appuie sur Enter ou A (manette Xbox 360) et qu'on est sur Start, on recharge le jeu et on quitte l'état menu if(input.enter || input.jump) { if(jeu.choice == 0) { player.checkpointActif = 0; //jeu.level = 1; initializePlayer(); changeLevel(); /* On réinitialise les variables du jeu */ jeu.vies = 3; jeu.etoiles = 0; jeu.onMenu = 0; } //Sinon, on quitte le jeu else if(jeu.choice == 1) { exit(0); } input.enter = 0; input.jump = 0; } }
void drawStartMenu(void) { //On crée une variable qui contiendra notre texte char text[200]; //On affiche l'écran-titre drawImage(map.titlescreen, 0, 0); //Le titre //sprintf(text, "Rabidja's Revenge"); //drawString(text, 280, 50, font, 0, 0, 0, 255); //Si l'option n'est pas en surbrillance, on l'affiche normalement if(jeu.choice != 0) { sprintf(text, "START: Lvl %d", jeu.level); //Ombrage en noir drawString(text, 375, 252, font, 0, 0, 0, 255); drawString(text, 373, 250, font, 255, 255, 255, 255); } if(jeu.choice != 1) { sprintf(text, "QUIT"); //Ombrage en noir drawString(text, 425, 292, font, 0, 0, 0, 255); drawString(text, 422, 290, font, 255, 255, 255, 255); } //Si l'option est en surbrillance, on change la couleur if(jeu.choice == 0) { sprintf(text, "START: Lvl %d", jeu.level); //Ombrage en noir drawString(text, 375, 252, font, 0, 0, 0, 255); drawString(text, 373, 250, font, 255, 255, 0, 255); } else if(jeu.choice == 1) { sprintf(text, "QUIT"); //Ombrage en noir drawString(text, 425, 292, font, 0, 0, 0, 255); drawString(text, 422, 290, font, 255, 255, 0, 255); } //Le nom du studio //sprintf(text, "Meruvia Game Studio, 2014"); //drawString(text, 230, 420, font, 0, 0, 0, 255); }
Et voilà, le code n'est pas bien compliqué comme vous pouvez le voir.
On change simplement la valeur de jeu.level en appuyant sur Gauche/Droite à la 1ère ligne du menu, et on l'affiche. Le jeu démarrera ainsi à ce niveau-là. Vous remarquerez d'ailleurs que j'ai mis en commentaire l'ancienne ligne jeu.level = 1; . En effet, on ne veut plus démarrer toujours du niveau 1, mais du niveau choisi !
Attention : Pour éviter un problème avec le surligneur syntaxique Geshi, j'ai dû séparer les chevrons >= et <= par un espace. N'oubliez pas de le supprimer, si vous faites du copier/coller.
Un nouveau menu Pause
Passons maintenant aux deux nouvelles fonctions permettant de changer le menu de Pause.
Là encore, le plus simple sera de supprimer les 2 anciennes fonctions et de les remplacer par celles-ci.
Fichier : menu.c
void updatePauseMenu(void) { //Si on appuie sur BAS if(input.down == 1) { //Si choice = O (il est sur start), on le met à 1 (quit) if(jeu.choice == 0) jeu.choice++; input.down = 0; } //Si on appuie sur HAUT if(input.up == 1) { //Si choice = 1 (il est sur Quit), on le met à 0 (Start) if(jeu.choice == 1) jeu.choice--; input.up = 0; } //Si on appuie sur Enter ou A (manette Xbox 360) et qu'on est sur Start, on recharge le jeu et on quitte l'état menu if(input.enter || input.jump) { if(jeu.choice == 0) { //Si on appuie sur Enter on quitte l'état menu jeu.onMenu = 0; } //Sinon, on quitte le jeu else if(jeu.choice == 1) { jeu.choice = 0; jeu.menuType = START; } input.enter = 0; input.jump = 0; } }
void drawPauseMenu(void) { char text[200]; //On écrit PAUSE sprintf(text, "** PAUSE **"); drawString(text, 322, 200, font, 0, 0, 0, 255); drawString(text, 320, 198, font, 255, 255, 255, 255); //Si l'option n'est pas en surbrillance, on l'affiche normalement if(jeu.choice != 0) { sprintf(text, "Continue"); //Ombrage en noir drawString(text, 346, 252, font, 0, 0, 0, 255); drawString(text, 344, 250, font, 255, 255, 255, 255); } if(jeu.choice != 1) { sprintf(text, "Exit"); //Ombrage en noir drawString(text, 386, 292, font, 0, 0, 0, 255); drawString(text, 384, 290, font, 255, 255, 255, 255); } //Si l'option est en surbrillance, on change la couleur if(jeu.choice == 0) { sprintf(text, "Continue"); //Ombrage en noir drawString(text, 346, 252, font, 0, 0, 0, 255); drawString(text, 344, 250, font, 255, 255, 0, 255); } else if(jeu.choice == 1) { sprintf(text, "Exit"); //Ombrage en noir drawString(text, 386, 292, font, 0, 0, 0, 255); drawString(text, 384, 290, font, 255, 255, 0, 255); } }
Là encore, le code n'est pas bien compliqué puisqu'il reprend grosso modo celui de l'écran-titre avec deux possibilités.
Soit on reprend le jeu (comme dans le menu Pause précédent), soit on retourne à l'écran-titre en changeant simplement le menuType.
Vous remarquerez aussi qu'on remet systématiquement les inputs à 0, pour une raison simple : sinon on valide tous les menus à la suite, et on passe de l'un à l'autre en un éclair (vous pouvez essayer, pour voir ). Grâce à cette technique, le joueur sera obligé de lâcher le bouton et de réappuyer s'il veut valider une seconde fois.
Des zombies plus intelligents !
Oui, oui, je sais, c'est pas commun pour un zombie ! Mais heureusement, ça va être plus facile à programmer que de dresser de vrais zombies (si, si ) !
Tout va se passer ici dans la fonction checkFall() du fichier monster.c, et en fait, il va nous suffire de rajouter 3 fois rien, comme vous allez le voir !
Changez simplement ces deux lignes-là :
Fichier : monster.c
//On teste si la tile sous le monstre est traversable (du vide quoi...). //Si c'est le cas, on renvoie 1, sinon 0. if (map.tile[y + 1][x] < BLANK_TILE - 20)
Et :
if (map.tile[y + 1][x] < BLANK_TILE - 20) return 1;
Et voilà la fonction complète :
int checkFall(GameObject monster) { int x, y; //Fonction qui teste s'il y a du sol sous un monstre //Retourne 1 s'il doit tomber, 0 sinon //On teste la direction, pour savoir si on doit prendre en compte x ou x + w (cf. schéma) if (monster.direction == LEFT) { //On va à gauche : on calcule là où devrait se trouver le monstre après déplacement. //S'il sort de la map, on met à jour x et y pour éviter de sortir de notre tableau //(source d'erreur possible qui peut planter notre jeu...). x = (int)(monster.x + monster.dirX) / TILE_SIZE; y = (int)(monster.y + monster.h - 1) / TILE_SIZE; if (y < 0) y = 1; if (y > MAX_MAP_Y) y = MAX_MAP_Y; if (x < 0) x = 1; if (x > MAX_MAP_X) x = MAX_MAP_X; //On teste si la tile sous le monstre est traversable (du vide quoi...). //Si c'est le cas, on renvoie 1, sinon 0. if (map.tile[y + 1][x] < BLANK_TILE - 20) return 1; else return 0; } else { //Même chose quand on va à droite x = (int)(monster.x + monster.w + monster.dirX) / TILE_SIZE; y = (int)(monster.y + monster.h - 1) / TILE_SIZE; if (y < = 0) y = 1; if (y > = MAX_MAP_Y) y = MAX_MAP_Y - 1; if (x < = 0) x = 1; if (x > = MAX_MAP_X) x = MAX_MAP_X - 1; if (map.tile[y + 1][x] < BLANK_TILE - 20) return 1; else return 0; } }
En fait, il suffit de retrancher 20 à la valeur de BLANK_TILE, qui vaut 89, soit le numéro de la première tile solide. Ainsi, on va englober toutes les tiles plateformes dans les tiles solides, et nos monstres pourront marcher dessus, tout simplement !
Attention : Là encore, pour éviter un problème avec le surligneur syntaxique Geshi, j'ai dû séparer les chevrons >= et <= par un espace. N'oubliez pas de les supprimer, si vous faites du copier/coller.
Eh voilà pour ce nouveau chapitre qui nous permet une fois encore de peaufiner notre jeu !
Comme vous pouvez le voir, ce qui différencie un bon jeu d'un autre, c'est avant tout le souci du détail, pour simplifier au maximum la vie du joueur et donner l'apparence d'un jeu fini, qui ne laisse rien au hasard.
Sur ce, je vous dis @ bientôt sur Meruvia !