2019-10-16
Après une première partie dédiée à la création et à l'affichage des éléments graphiques, et une seconde partie dédiée aux déplacements et aux collisions, on va enfin s'attaquer au cœur du gameplay : casser des briques !
Je vais donc vous montrer cette fois-ci comment casser des briques... mais également comment rendre le jeu vraiment jouable, en permettant au joueur de perdre s'il rate la balle ou de gagner s'il casse toutes les briques. Je vais également vous donner quelques pistes sur les améliorations qu'il faudrait apporter pour que le jeu soit réellement fun à jouer.
--------------------------------------------------------------------------------
📝️ Note:
--------------------------------------------------------------------------------
Cet article fait partie d'une série sur le développement GameBoy en C avec le compilateur SDCC et la bibliothèque gbdk-n. Cette série est toujours en cours et de nouveaux articles paraissent de temps à autre.
Articles de la série :
--------------------------------------------------------------------------------
On va enfin pouvoir casser des briques... c'est cool... mais en fait vous allez vite être déçus, car en fait, c'est extrêmement simple : lorsque l'on vérifie si la balle rentre en collision avec l'environnement, il suffit de regarder au passage s'il ne s'agirait pas d'une tuile représentant une brique, et si c'est le cas, on la supprime. Bon il y a quand même une petite subtilité : les briques sont composées de deux tuiles, donc on se retrouve dans deux cas :
Eh oui, il n'y a pas plus de difficulté que ça. Je vais donc créer une petite fonction qui supprime une brique si on lui donne les coordonnées de la tuile la plus à gauche :
#define TILE_EMPTY 128 void remove_brick(UINT8 x, UINT8 y) { UINT8 cells[2] = {TILE_EMPTY, TILE_EMPTY}; set_bkg_tiles(x, y, 2, 1, cells); }
et je vais ajouter le code suivant dans la fonction check_ball_collide() :
#define TILE_BRICK_L 136 #define TILE_BRICK_R 137 UINT8 check_ball_collide(INT8 delta_x, INT8 delta_y) { // ... // remove bricks switch (next_cell[0]) { case TILE_BRICK_L: remove_brick(ball_next_cell_x, ball_next_cell_y); break; case TILE_BRICK_R: remove_brick(ball_next_cell_x - 1, ball_next_cell_y); break; } // ... }
--------------------------------------------------------------------------------
📝️ Note:
--------------------------------------------------------------------------------
NOTE : ce n'est pas forcément très « propre » (d'un point de vue organisation du code) de supprimer les briques au milieu du code d'une fonction censée seulement vérifier les collisions, mais le but ici est d'arriver rapidement à un résultat. Il faudra améliorer ça plus tard si on dépasse le stade du PoC. 😉️
--------------------------------------------------------------------------------
Si on exécute le programme, cette fois-ci ça ressemble vraiment à un casse-briques parfaitement jouable !
Capture vidéo du cassage de brique
Un jeu où on ne peut pas perdre n'est pas amusant, je vais donc implémenter le game over pas plus tard que tout de suite 😛️. Pour perdre la partie, la condition est simple : il suffit que la balle touche le bas de l'écran. On peut donc écrire la fonction suivante :
UINT8 check_gameover() { return BALL_Y + BALL_WIDTH >= 18 * 8 + 16; // 18 * 8 -> hauteur de l'écran en pixels // 16 -> offset de la position des sprites sur l'axe y }
Et maintenant, je n'ai plus qu'un petit test à rajouter dans ma boucle principale :
void main() { // ... while (1) { // ... if (check_gameover()) { break; } // ... } }
Bon normalement il faudrait afficher un joli écran de game over et tout, mais pour le moment on va faire au plus simple, sinon cet article va finir par être beaucoup trop long. On va donc pour le moment simplement couper la boucle, ce qui aura pour effet de terminer le jeu (par contre il faudra redémarrer la console pour rejouer).
Je ne vous mets pas de GIF cette fois-ci, parce que visuellement la fin de la partie se matérialise juste par un écran figé.
Et maintenant qu'on peut perdre, il faudrait aussi pouvoir gagner... Ça ne devrait pas être trop dur à faire : il suffit de compter le nombre de briques cassées pour savoir quand le joueur a terminé le niveau.
Je vais donc créer une variable globale qui va stocker le nombre de briques qu'il reste :
UINT8 REMAINING_BRICKS = 39; // il y a 39 briques dans le level01
puis je modifie la fonction remove_brick() pour qu'elle décompte les briques cassées :
void remove_brick(UINT8 x, UINT8 y) { // ... REMAINING_BRICKS -= 1; }
et enfin, dans la fonction main(), je remplace la boucle infinie par une boucle qui tourne tant qu'il reste des briques :
void main() { // ... while (REMAINING_BRICKS) { // ... } }
Et voilà, le jeu s'arrêtera de lui-même lorsqu'il ne restera plus de brique à casser !
Comme pour la section précédente, je ne vous mets pas de GIF ici non plus, car cette fois encore, le jeu va simplement se figer lorsque la partie sera terminée.
On est à présent en possession d'un prototype de casse-briques qui fonctionne... et je vais m'arrêter là pour cet article car ça va commencer à devenir trop long sinon ! Mais pour bien faire les choses, il y aurait encore pas mal de boulot.
Déjà il faudrait commencer par afficher un écran de félicitations lorsque le joueur gagne et un écran de game over lorsqu'il perd. Et tant qu'on y est, on pourrait rajouter un écran de titre. Il faudrait bien sûr également permettre au joueur de relancer une partie sans redémarrer la console.
Enfin, il faudrait améliorer le gameplay :
Je vous laisse donc le soin de poursuivre le développement du jeu, si vous en avez l'envie bien sûr 😉️. Et si vous le faites, n'hésitez pas à poster un lien vers votre projet dans les commentaires ou à m'envoyer un mail ! Qui sait, si je reçois quelques propositions sympas je pourrais peut-être vous les présenter ici-même ! 😁️
--------------------------------------------------------------------------------
📝️ Note:
--------------------------------------------------------------------------------
Comme à chaque fin d'article de cette série, vous trouverez les sources complètes du projet sur Github :
github.com/flozz/gameboy-examples
--------------------------------------------------------------------------------
À bientôt pour de nouvelles aventures vidéoludiques ! 😃️
--------------------------------------------------------------------------------