[JAVA] Snake Game

Pour la plupart des langages que j’ai appris, je me suis dirigé vers le SiteDuZéro qui est un bon site pour débuter, qui traite de presque tout d’une manière divertissante et générale. Malheureusement,  pour certaines notions j’ai eu beaucoup de mal avec leur approche pédagogique. Il en est ainsi pour les notions d’interfaces et de threads en Java. J’ai eu du mal à m’identifier aux exemples, à comprendre vraiment où voulait en venir l’auteur puis j’ai saisi qu’on n’apprenait mieux qu’en s’essayant. J’avais assimilé l’objectif des threads ou plutôt l’inconvénient d’un programme mono-threaded, mais je ne voyais toujours pas le champs d’application puis j’ai eu une petite illumination en pensant au jeu Snake.

Snake GameSnake se porte tout à fait au principe du programme multi-threaded puisqu’il a besoin d’un mouvement perpétuel (avancement d’une case toutes les X secondes) et d’un ordre de direction (la prochaine case pour la tête du serpent). Le mouvement perpétuel nécessite une boucle « infinie » (stoppée quand le serpent entre en collision avec lui-même) et occupe donc tout le thread. On est donc obligé de créer un autre thread pour pouvoir intercepter les ordres de direction (via un KeyListener).

J’ai choisi de construire mon serpent autour d’une liste chaînée (LinkedList) de vertèbres et ai élu le JPanel constituant mon aire de jeu (GamePanel) comme étant l’objet qui va lancer un nouveau thread. J’aurai très bien pu lancer un nouveau thread dans la classe Snake (ce que j’ai fait au début), mais cela posera problème si un jour on décide d’ajouter des serpents pour jouer en multi-joueurs : autant de threads que de joueurs c’est très lourd pour la machine. Un choix justifié même pour deux personnes (voire plus) qui jouent sur un même clavier puisqu’il est pratiquement impossible d’appuyer sur deux touches en même temps avec le temps de latence actuel des claviers.

Le reste est relativement classique. Il se compose de la fenêtre de jeu (à ne pas confondre avec l’aire de jeu sur la fenêtre ; GameWindow) qui contient l’aire de jeu et attend gentiment le code de retour permettant l’arrêt du jeu ou son redémarrage. Il y a ensuite les appâts (Bait) et vertèbres (Vertebra) qui possèdent tous deux la position cartésienne de leur coin gauche supérieur. J’ai choisi ce coin parce qu’il correspond aux coordonnées requises pour dessiner avec l’objet Graphics de la méthode paintComponent appelée par la méthode repaint. Vertebra possède ensuite l’indication de sa prochaine direction et Bait une variable commune à la classe qui compte le nombre d’appâts mangés.

GamePanel est donc constitué d’un thread et de tous les éléments du jeu : un appât (Bait), un serpent (Snake) et un petit JLabel pour tenir compte du score en temps réel. Il possède une méthode go() qui permet de gérer le mouvement perpétuel du serpent tant que ce dernier ne se mange pas lui-même. Quand la boucle est finie, il demande au joueur s’il veut recommencer ou non. GamePanel contient la méthode paintComponent(Graphics g) qui permet de redessiner le serpent ainsi que l’appât toutes les X secondes (à chaque fois que GamePanel.repaint() est appelée). Ici rien de plus basique : on remet l’aire de jeu à zéro (graphiquement uniquement) avec un gros carré blanc allant de l’origine cartésienne (coin supérieur gauche) à la dernière position possible (longueur; largeur). On dessine ensuite chaque vertèbre du serpent en noir suivant la position de chacune et enfin l’appât en vert de la même manière que les vertèbres du serpent.

Snake est de loin la classe la plus marrante, tout en restant extrêmement simple. Pour pouvoir donner la direction au mouvement suivant il faut vérifier tout d’abord qu’on ne lui demande pas d’aller en sens contraire de celui actuel : impossible d’après les règles du jeu ! (Sinon on ignore l’ordre de direction). Pour ajouter une vertèbre, il faut récupérer la direction de la dernière vertèbre et ses coordonnées pour l’ajouter au bon endroit. Si la direction est Ouest alors on place la dernière vertèbre à l’Est de la dernière et on lui donne la direction Ouest.

Nous en voici à la méthode cœur du jeu : nextMove() appelée toutes les X secondes par notre GamePanel. Pour chaque vertèbre, elle édite la position suivante en s’aidant de l’ordre de direction respectif (chaque vertèbre à son ordre de direction suivante). C’est ici que l’on gère le dépassement de l’aire de jeu. Si une vertèbre de notre serpent dépasse l’aire de jeu par l’Est, il faut la replacer à l’Ouest dans la première position possible ! Si l’aire de jeu n’est pas dépassée, on déplace la vertèbre dans la direction demandée. Pour finir cette méthode, on a besoin de faire hériter chaque vertèbre de l’ordre de direction de la vertèbre placée devant elle. C’est à dire que la tête donne son ordre de direction après avoir elle-même bougé à la vertèbre placée après elle (la deuxième) et ainsi de suite. Si aucun nouvel ordre de direction n’est donné, on poursuit avec la direction précédente.

Vous pouvez trouver le code source du jeu sur mon Git. N’hésitez pas à commenter, partager en commentaires 🙂

Publicités

Laisser un commentaire

Entrez vos coordonnées ci-dessous ou cliquez sur une icône pour vous connecter:

Logo WordPress.com

Vous commentez à l'aide de votre compte WordPress.com. Déconnexion / Changer )

Image Twitter

Vous commentez à l'aide de votre compte Twitter. Déconnexion / Changer )

Photo Facebook

Vous commentez à l'aide de votre compte Facebook. Déconnexion / Changer )

Photo Google+

Vous commentez à l'aide de votre compte Google+. Déconnexion / Changer )

Connexion à %s