Snake
Un jeu simple, ancien, et instructif à programmer
Tout d'abord: le jeu. Histoire de s'amuser un peu, et de tester les différents éléments.Le contrôle des mouvements se fait avec les flèches, simples ou du pavé numérique.
fsde
Le code javascript complet, détaillé
Le serpent du jeu est représenté par une liste de points, c'est-à-dire par une liste de couples de coordonnées.C'est le tableau
sn
, initialisé par: var sn=new Array;
sn=[[4,4],[5,4],[6,4],[7,4]];
Ainsi, tout au long de l'exécution on aura la longueur du serpent donnée par
Ls=sn.length;
, et
sn[i]
les coordonnées du i-ème point du serpent:
sn[i][0]
: la 1ère coordonnée (en abscisse, ou horizontalement)sn[i][1]
: la 2ème coordonnée (en ordonnée, ou verticalement)sn[Ls-1]
: contient, et de même que ci-dessus, la tête du serpent, pour la quelle on doit vérifier qu'elle ne rencontre pas un bord du canvas, ni un autre point du serpent (c'est-à-dire de devient pas égal à unsn[i]
pour uni
compris entre0
etLs
)
Les différentes fonctions:
KeyDrive
: l'écoute des événements clavier, et leur pilotage.
Ici, la touche entrer (keyCode==13
) nous intéresse pour lancer et mettre en pause le jeu. On (re)lance le jeu avecsetInterval
; on l'arrête (ou le pause) en stopant justement ceci avecclearInterval
.
Lorsqu'on n'est pas en pause (GameState=="playing"
), on écoute aussi les flèches du pavé numérique pour diriger le serpent,keyCode= 98, 100, 102 et 104
, et en stockant la direction correspondante dans la variableDir
qui est alors utilisée directement (plus précisément toutes 100ms=0,1s dusetInterval
) dans la fonctionMoveSnake
:
A noter: Au tout début du script, on commence par désactiver le comportement par défaut des flèches du clavier, dans la fonctionNoDefaultArrow
appelée à travers tout le document par ledocument.addEventListener("keydown",…
.
Le paramètrefalse
en fin deaddEventListener
MoveSnake
: LA fonction principale.
La direction de déplacement estDir
.
On vérifie si l'éventuelle nouvelle tête du serpent, dont les coordonnées seraientsn[Ls-1]
en se déplaçant dans la directionDir
:- si
Dir=="right"
: déplacement vers le haut, la nouvelle position de la tête estsn[Ls][0]=sn[Ls-1][0]+1
(+1 en abscisse) etsn[Ls][0]=sn[Ls-1][0]
(inchangé en ordonnée) - si
Dir=="up"
: déplacement vers le haut, idem mais l'abscisse est cette fois inchangée, et "-1" en ordonnée () - …
sn[0]
: chaquesn[i]
prend les valeurs desn[i+1]
, tandis que la têtesn[Ls-1]
est calculée comme précédemment:- si
Dir=="right"
: déplacement vers le haut, la nouvelle position de la tête estsn[Ls-1][0]=sn[Ls-2][0]+1
(+1 en abscisse) etsn[Ls-1][0]=sn[Ls-2][0]
(inchangé en ordonnée) - …
Ls=sn.length
est ici inchangée.- si
Draw
: fonction qui, comme son nom l'indique, dessine ce qu'il y a à dessiner, le serpent et le fruit.
Pour chaque point du serpent, donc nombre entieri
entre0
etLs-1
on trace le pixel de coordonnéessn[i][0]
etsn[i][1]
, qui, en utilisant le facteur d'échellezoom
, est un carré de côtézoom
justement:fillRect( , ,zoom,zoom)
.GameOver
: fonction au nom assez explicite… Cette fonction change ici simplement la couleur d'affichage en virant au rouge, et surtout arrête le déroulement dusetInterval
, c'est-à-dire stop le déroulement automatique du jeu, donc animation…
On pourrait bien sûr faire ici une fonction plus complète, qui affiche que le jeu est fini, compte d'une certaine façon le nombre, ou encore affiche le temps que le joueur à résisté au jeu…
- Code: Select all
-
<canvas id="snake" width="400" height="300" style="border:1px solid black"></canvas>
<script>
// On désactive le comportement par défaut des flèches du clavier (défilement dans la page...)
function NoDefaultArrow(event) {
if ((event.keyCode==37)||(event.keyCode==38)||(event.keyCode==39)||(event.keyCode==40)) {
event.stopPropagation();
event.preventDefault();
}
document.addEventListener("keydown", NoDefaultArrow, false);
// et puis on s'occupe du canvas et du jeu:
canvas = document.getElementById("snake");
ctx = canvas.getContext("2d");
ctx.fillStyle = "black";
Width=document.getElementById("snake").width;
Height=document.getElementById("snake").height;
zoom=10; // pixels size inside canvas
// Initialisation
var sn=new Array;
sn=[[4,4],[5,4],[6,4],[7,4]];
Ls=sn.length; // Taille initiale du serpent
GameState="paused";Dir="right";
Fruit=[Math.floor(Width/zoom*Math.random()),Math.floor(Height/zoom*Math.random())];
ctx.font="15pt Calibri,Geneva,Arial";ctx.fillText("Press Enter to start...",50,100);
Draw();
// et on lance l'écoute des touches du clavier
document.onkeydown = KeyDrive;
function Draw() {//Trace le serpent et le fruit
ctx.clearRect(0,0,Width,Height);
for (i=0;i<Ls;i++) {ctx.fillRect(zoom*sn[i][0],zoom*sn[i][1],zoom,zoom);}
ctx.fillStyle="green";
ctx.fillRect(zoom*Fruit[0],zoom*Fruit[1],zoom,zoom);
ctx.fillStyle="black";
}
function GameOver() {//Comme son non l'indique...
ctx.fillStyle="red";clearInterval(sI);
}
function MoveSnake() {/*LA fonction principal qui calcule le nouveau tableau sn des positions du serpent,
si il ne se mord pas, ne rencontre pas un mur, et qui grandit quand il prend un fruit*/
if (// Si la tête du serpent rencontre le fruit
(Dir=="up" && sn[Ls-1][0]==Fruit[0] && sn[Ls-1][1]==Fruit[1]+1)
|| (Dir=="down" && sn[Ls-1][0]==Fruit[0] && sn[Ls-1][1]==Fruit[1]-1)
|| (Dir=="right" && sn[Ls-1][0]==Fruit[0]-1 && sn[Ls-1][1]==Fruit[1])
|| (Dir=="left" && sn[Ls-1][0]==Fruit[0]+1 && sn[Ls-1][1]==Fruit[1])) {
if (Dir=="up") {sn[Ls]=[sn[Ls-1][0],sn[Ls-1][1]-1];}
if (Dir=="down") {sn[Ls]=[sn[Ls-1][0],sn[Ls-1][1]+1];}
if (Dir=="right") {sn[Ls]=[sn[Ls-1][0]+1,sn[Ls-1][1]];}
if (Dir=="left") {sn[Ls]=[sn[Ls-1][0]-1,sn[Ls-1][1]];}
Ls++;// Incrémente la longueur du serpent et on génère une nouvelle position aléatoire de fruit
Fruit=[Math.floor(Width/zoom*Math.random()),Math.floor(Height/zoom*Math.random())];
}
else {// Sinon un déplacement simple dans la direction "Dir"
for (i=0;i<Ls-1;i++) {sn[i]=sn[i+1];}
if (Dir=="up") {sn[Ls-1]=[sn[Ls-2][0],sn[Ls-2][1]-1];}
if (Dir=="down") {sn[Ls-1]=[sn[Ls-2][0],sn[Ls-2][1]+1];}
if (Dir=="right") {sn[Ls-1]=[sn[Ls-2][0]+1,sn[Ls-2][1]];}
if (Dir=="left") {sn[Ls-1]=[sn[Ls-2][0]-1,sn[Ls-2][1]];}
}
// On vérifie que le serpent ne déborde pas du canvas
if (zoom*sn[Ls-1][0]==Width || sn[Ls-1][0]==-1 || sn[Ls-1][1]==-1 || zoom*sn[Ls-1][1]==Height) {GameOver();}
// On vérifie que le serpent ne se mord pas:
for (i=0;i<Ls-1;i++) {if (sn[Ls-1][0]==sn[i][0] && sn[Ls-1][1]==sn[i][1]) {GameOver();}}
// et dans tous les cas, on retrace
Draw();
}
function KeyDrive(e) {// Pilotage au clavier
if(e.keyCode==13) {
if (GameState=="paused") {GameState="playing";sI=setInterval('MoveSnake()',100);}
else if (GameState=="playing") {GameState="paused";clearInterval(sI);
ctx.font="15pt Calibri,Geneva,Arial";ctx.fillText("Pause... Press space to resume...",50,100);}
}
if (GameState=="playing") {//Codes des touches du pavé numérique
if(e.keyCode==104) {Dir="up";}
else if(e.keyCode==98) {Dir="down";}
else if(e.keyCode==102) {Dir="right";}
else if(e.keyCode==100) {Dir="left";}
MoveSnake();
}
}
</script>
Voir aussi: