- Programmation Orientée Objet en Javascript
- Analyse et conception d'un jeu simple
- Découverte des listeners de touches
- Utilisation du canvas pour dessiner
- Utilisation de setTimeout pour gérer le temps
window.onlad = function()
{
// Création du canvas
var canvas = document.createElement('canvas');
// Dimensions
canvas.width = 900;
canvas.height = 600;
// Style
canvas.style.border = "1px solid";
// Ajout du canvas au body
document.body.appendChild(canvas);
// ******** Dessiner dans le canvas
// Récupération du contexte
var ctx = canvas.getContext('2d');
// Couleur de remplissage
ctx.fillStyle = "#ff0000";
// Remplissage avec un rectangle, ses coordonnées et ses dimensions
ctx.fillRect(30, 30, 100, 50);
}
On va organiser le code, déclarer des variables globales et ajouter un timer
window.onload = function()
{
// Variables globales
var canvas, ctx;
var delay = 1000; // Delai en millisecondes
var xCoord = 0;
var yCoord = 0;
// Appel de la fonction init
init();
// Initialisation du canvas
function init()
{
// Création du canvas
canvas = document.createElement('canvas');
// Dimensions
canvas.width = 900;
canvas.height = 600;
// Style
canvas.style.border = "1px solid";
// Ajout du canvas au body
document.body.appendChild(canvas);
// ******** Dessiner dans le canvas
// Récupération du contexte
ctx = canvas.getContext('2d');
// Appel de la fonction de refresh
refreshCanvas();
}
// Refresh du canvas
function refreshCanvas()
{
// Nettoyage du canvas
ctx.clearRect(0, 0, canvas.width, canvas.height);
// Update des coords
xCoord += 2;
yCoord += 2;
// Couleur de remplissage
ctx.fillStyle = "#ff0000";
// Remplissage avec un rectangle, ses coordonnées et ses dimensions
ctx.fillRect(xCoord, yCoord, 100, 50);
// ******* Mise en place d'un timer
setTimeout(refreshCanvas, delay);
}
}
Ici, on va ajouter la capacité du serpent à évoluer dans l'espace.
window.onload = function()
{
// Variables globales
var canvasWidth = 900;
var canvasHeight = 600;
var blocksize = 30;
var canvas, ctx;
var delay = 1000; // Delai en millisecondes
var xCoord = 0;
var yCoord = 0;
var snake;
// Appel de la fonction init
init();
// Initialisation du canvas
function init()
{
// Création du canvas
canvas = document.createElement('canvas');
// Dimensions
canvas.width = canvasWidth;
canvas.height = canvasHeight;
// Style
canvas.style.border = "1px solid";
// Ajout du canvas au body
document.body.appendChild(canvas);
// ******** Dessiner dans le canvas
// Récupération du contexte
ctx = canvas.getContext('2d');
// Construction du serpent
snake = new Snake([ [5, 3], [6, 3], [7,3] ]);
// Appel de la fonction de refresh
refreshCanvas();
}
// Refresh du canvas
function refreshCanvas()
{
// Nettoyage du canvas
ctx.clearRect(0, 0, canvas.width, canvas.height);
// Avancement du serpent
snake.move();
// Dessin du serpent
snake.draw();
// ******* Mise en place d'un timer
setTimeout(refreshCanvas, delay);
}
function drawBlock (ctx, position)
{
// Détermination en pixels de la position
var x = position[0] * blocksize;
var y = position[1] * blocksize;
// Remplissage du block avec les coordonnées calculées
ctx.fillRect(x, y, blocksize, blocksize);
}
// Objet Serpent
function Snake(body)
{
this.body = body;
// Méthode draw
this.draw = function ()
{
// Sauvegarde du contexte
ctx.save();
// Couleur de remplissage
ctx.fillStyle = "#ff0000";
// Boucle
// Le corps du serpent sera un ensembl de petits rectangles
for (var i=0; i < this.body.length; i++)
{
drawBlock(ctx, this.body[i]);
}
ctx.restore();
}
// Méthode pour faire avancer le serpent
this.move = function()
{
// Récupération de la position de la tête du serpent
var nextHeadPosition = this.body[0].slice();
// Avancement de 1 de la tête
nextHeadPosition[0] += 1;
// On ajoute cette nouvelle position au courps du serpent
// unshift ajoute en première case
this.body.unshift( nextHeadPosition );
// Retrait de la queue du serpent
this.body.pop();
}
}
}
Souci si le serpent revient sur ses pas! On va gérer les événements clavier Ajouter la prise en compte du changement de direction
window.onload = function()
{
// Variables globales
var canvasWidth = 900;
var canvasHeight = 600;
var blocksize = 30;
var canvas, ctx;
var delay = 1000; // Delai en millisecondes
var xCoord = 0;
var yCoord = 0;
var snake;
// Appel de la fonction init
init();
// Initialisation du canvas
function init()
{
// Création du canvas
canvas = document.createElement('canvas');
// Dimensions
canvas.width = canvasWidth;
canvas.height = canvasHeight;
// Style
canvas.style.border = "1px solid";
// Ajout du canvas au body
document.body.appendChild(canvas);
// ******** Dessiner dans le canvas
// Récupération du contexte
ctx = canvas.getContext('2d');
// Construction du serpent avec son body, et maintenant sa direction
snake = new Snake([ [5, 3], [6, 3], [7,3] ], "right");
// Appel de la fonction de refresh
refreshCanvas();
}
// Refresh du canvas
function refreshCanvas()
{
// Nettoyage du canvas
ctx.clearRect(0, 0, canvas.width, canvas.height);
// Avancement du serpent
snake.move();
// Dessin du serpent
snake.draw();
// ******* Mise en place d'un timer
setTimeout(refreshCanvas, delay);
}
function drawBlock (ctx, position)
{
// Détermination en pixels de la position
var x = position[0] * blocksize;
var y = position[1] * blocksize;
// Remplissage du block avec les coordonnées calculées
ctx.fillRect(x, y, blocksize, blocksize);
}
// Objet Serpent
function Snake(body, direction)
{
// Propriétés du serpent
this.body = body;
this.direction = direction
// Méthode draw
this.draw = function ()
{
// Sauvegarde du contexte
ctx.save();
// Couleur de remplissage
ctx.fillStyle = "#ff0000";
// Boucle
// Le corps du serpent sera un ensembl de petits rectangles
for (var i=0; i < this.body.length; i++)
{
drawBlock(ctx, this.body[i]);
}
ctx.restore();
}
// Méthode pour faire avancer le serpent
this.move = function()
{
// Récupération de la position de la tête du serpent
var nextHeadPosition = this.body[0].slice();
// Quelle direction?
switch (this.direction)
{
case "left":
// Avancement de -1 de la tête sur l'axe X
nextHeadPosition[0] -= 1;
break;
case "right":
// Avancement de 1 de la tête sur l'axe X
nextHeadPosition[0] += 1;
break;
case "up":
// Avancement de -1 de la tête sur l'axe Y
nextHeadPosition[1] -= 1;
break;
case "down":
// Avancement de 1 de la tête sur l'axe Y
nextHeadPosition[1] += 1;
break;
default:
throw("Invalid direction");
}
// On ajoute cette nouvelle position au courps du serpent
// unshift ajoute en première case
this.body.unshift( nextHeadPosition );
// Retrait de la queue du serpent
this.body.pop();
}
// Méthode piur changer la direction
this.setDirection = function ( newDirection )
{
var allowedDirections;
// Quelle direction sont permises?
switch (this.direction)
{
case "left":
case "right":
allowedDirections = ["up", "down"];
break;
case "up":
case "down":
allowedDirections = ["left", "right"];
break;
default:
throw("Invalid direction");
}
// Avant de changer la direction on regarde le contenu du tableau allowedDirections avec la fonction indexOf
if (allowedDirections.indexOf( newDirection ) > -1)
{
this.direction = newDirection;
}
}
}
// Evénements du clavier
document.addEventListener('keydown', handleKeydown);
function handleKeydown( ev )
{
var key = ev.key;
var newDirection;
switch (key)
{
case "ArrowLeft":
newDirection = "left";
break;
case "ArrowUp":
newDirection = "up";
break;
case "ArrowRight":
newDirection = "right";
break;
case "ArrowDown":
newDirection = "down";
break;
default:
throw("Invalid code");
}
// On applique le mouvement au serpent
snake.setDirection(newDirection);
}
}
Le serpent fonctionne, se déplace en fonction du clavier. On ajoute une pomme dans le canvas.
window.onload = function()
{
// Variables globales
var canvasWidth = 900;
var canvasHeight = 600;
var blocksize = 30;
var canvas, ctx;
var delay = 1000; // Delai en millisecondes
var snake;
var apple;
// Appel de la fonction init
init();
// Initialisation du canvas et des objets
function init()
{
// Création du canvas
canvas = document.createElement('canvas');
// Dimensions
canvas.width = canvasWidth;
canvas.height = canvasHeight;
// Style
canvas.style.border = "1px solid";
// Ajout du canvas au body
document.body.appendChild(canvas);
// ******** Dessiner dans le canvas
// Récupération du contexte
ctx = canvas.getContext('2d');
// Construction du serpent avec son body, et maintenant sa direction
snake = new Snake([ [5, 3], [6, 3], [7,3] ], "right");
apple = new Apple([10,10]);
// Appel de la fonction de refresh
refreshCanvas();
}
// Refresh du canvas
function refreshCanvas()
{
// Nettoyage du canvas
ctx.clearRect(0, 0, canvas.width, canvas.height);
// Avancement du serpent
snake.move();
// Dessin du serpent
snake.draw();
apple.draw();
// ******* Mise en place d'un timer
setTimeout(refreshCanvas, delay);
}
function drawBlock (ctx, position)
{
// Détermination en pixels de la position
var x = position[0] * blocksize;
var y = position[1] * blocksize;
// Remplissage du block avec les coordonnées calculées
ctx.fillRect(x, y, blocksize, blocksize);
}
// Objet Serpent
function Snake(body, direction)
{
// Propriétés du serpent
this.body = body;
this.direction = direction
// Méthode draw
this.draw = function ()
{
// Sauvegarde du contexte
ctx.save();
// Couleur de remplissage
ctx.fillStyle = "#ff0000";
// Boucle
// Le corps du serpent sera un ensembl de petits rectangles
for (var i=0; i < this.body.length; i++)
{
drawBlock(ctx, this.body[i]);
}
ctx.restore();
}
// Méthode pour faire avancer le serpent
this.move = function()
{
// Récupération de la position de la tête du serpent
var nextHeadPosition = this.body[0].slice();
// Quelle direction?
switch (this.direction)
{
case "left":
// Avancement de -1 de la tête sur l'axe X
nextHeadPosition[0] -= 1;
break;
case "right":
// Avancement de 1 de la tête sur l'axe X
nextHeadPosition[0] += 1;
break;
case "up":
// Avancement de -1 de la tête sur l'axe Y
nextHeadPosition[1] -= 1;
break;
case "down":
// Avancement de 1 de la tête sur l'axe Y
nextHeadPosition[1] += 1;
break;
default:
throw("Invalid direction");
}
// On ajoute cette nouvelle position au courps du serpent
// unshift ajoute en première case
this.body.unshift( nextHeadPosition );
// Retrait de la queue du serpent
this.body.pop();
}
// Méthode piur changer la direction
this.setDirection = function ( newDirection )
{
var allowedDirections;
// Quelle direction sont permises?
switch (this.direction)
{
case "left":
case "right":
allowedDirections = ["up", "down"];
break;
case "up":
case "down":
allowedDirections = ["left", "right"];
break;
default:
throw("Invalid direction");
}
// Avant de changer la direction on regarde le contenu du tableau allowedDirections avec la fonction indexOf
if (allowedDirections.indexOf( newDirection ) > -1)
{
this.direction = newDirection;
}
}
}
// Objet Pomme
// La position doit etre le milieu d'un bloc!
function Apple (position)
{
this.position = position;
this.draw = function ()
{
// Sauvegarde du contexte
ctx.save();
// Couleur de remplissage
ctx.fillStyle = "#33cc33";
ctx.beginPath();
// Le rayon correspond à la moitié d'un bloc
var radius = blocksize / 2;
// La position doit etre le milieu d'un bloc!
var x = position[0] * blocksize + radius;
var y = position[1] * blocksize + radius;
// Fonction qui va dessiner le cercle
ctx.arc(x, y, radius, 0, Math.PI *2, true);
ctx.fill();
ctx.restore();
}
}
// Evénements du clavier
document.addEventListener('keydown', handleKeydown);
function handleKeydown( ev )
{
var key = ev.key;
var newDirection;
switch (key)
{
case "ArrowLeft":
newDirection = "left";
break;
case "ArrowUp":
newDirection = "up";
break;
case "ArrowRight":
newDirection = "right";
break;
case "ArrowDown":
newDirection = "down";
break;
default:
throw("Invalid code");
}
// On applique le mouvement au serpent
snake.setDirection(newDirection);
}
}
Le serpent ne peut pas dépasser le mur, s'il touche les bords, c'est perdu!
De même, si le serpent se mange lui même, c'est perdu.
Définition de la largeur en terme de block et non en pixel
On détecte la collision de la tête uniquement contre les murs et contre son propre corps
Pour tester la collision avec le corps, il aura fallu allonger le corps. On peut aussi modifier le délai pour ajuster la vitesse du serpent
window.onload = function()
{
// Variables globales
var canvasWidth = 900;
var canvasHeight = 600;
var blocksize = 30;
var canvas, ctx;
var delay = 100; // Delai en millisecondes
var snake;
var apple;
// Définition de la largeur en terme de block et non en pixel
var widthInBlock = canvasWidth / blocksize;
var heightInBlock = canvasHeight / blocksize;
// Appel de la fonction init
init();
// Initialisation du canvas et des objets
function init()
{
// Création du canvas
canvas = document.createElement('canvas');
// Dimensions
canvas.width = canvasWidth;
canvas.height = canvasHeight;
// Style
canvas.style.border = "1px solid";
// Ajout du canvas au body
document.body.appendChild(canvas);
// ******** Dessiner dans le canvas
// Récupération du contexte
ctx = canvas.getContext('2d');
// Construction du serpent avec son body, et maintenant sa direction
snake = new Snake([ [9, 3], [8, 3], [7, 3], [6, 3], [5, 3], [4, 3], [4,3], [4,3], [1,3] ], "right");
apple = new Apple([10,10]);
// Appel de la fonction de refresh
refreshCanvas();
}
// Refresh du canvas
function refreshCanvas()
{
// Avancement du serpent
snake.move();
// On vérifie la collision
if ( snake.checkCollision() )
{
// Game over
console.log("gameover");
}else{
// Nettoyage du canvas
ctx.clearRect(0, 0, canvas.width, canvas.height);
// Dessin du serpent
snake.draw();
// Dessin de la pomme
apple.draw();
// ******* Mise en place d'un timer
setTimeout(refreshCanvas, delay);
}
}
function drawBlock (ctx, position)
{
// Détermination en pixels de la position
var x = position[0] * blocksize;
var y = position[1] * blocksize;
// Remplissage du block avec les coordonnées calculées
ctx.fillRect(x, y, blocksize, blocksize);
}
// Objet Serpent
function Snake(body, direction)
{
// Propriétés du serpent
this.body = body;
this.direction = direction
// Méthode draw
this.draw = function ()
{
// Sauvegarde du contexte
ctx.save();
// Couleur de remplissage
ctx.fillStyle = "#ff0000";
// Boucle
// Le corps du serpent sera un ensembl de petits rectangles
for (var i=0; i < this.body.length; i++)
{
drawBlock(ctx, this.body[i]);
}
ctx.restore();
}
// Méthode pour faire avancer le serpent
this.move = function()
{
// Récupération de la position de la tête du serpent
var nextHeadPosition = this.body[0].slice();
// Quelle direction?
switch (this.direction)
{
case "left":
// Avancement de -1 de la tête sur l'axe X
nextHeadPosition[0] -= 1;
break;
case "right":
// Avancement de 1 de la tête sur l'axe X
nextHeadPosition[0] += 1;
break;
case "up":
// Avancement de -1 de la tête sur l'axe Y
nextHeadPosition[1] -= 1;
break;
case "down":
// Avancement de 1 de la tête sur l'axe Y
nextHeadPosition[1] += 1;
break;
default:
throw("Invalid direction");
}
// On ajoute cette nouvelle position au courps du serpent
// unshift ajoute en première case
this.body.unshift( nextHeadPosition );
// Retrait de la queue du serpent
this.body.pop();
}
// Méthode pour changer la direction
this.setDirection = function ( newDirection )
{
var allowedDirections;
// Quelle direction sont permises?
switch (this.direction)
{
case "left":
case "right":
allowedDirections = ["up", "down"];
break;
case "up":
case "down":
allowedDirections = ["left", "right"];
break;
default:
throw("Invalid direction");
}
// Avant de changer la direction on regarde le contenu du tableau allowedDirections avec la fonction indexOf
if (allowedDirections.indexOf( newDirection ) > -1)
{
this.direction = newDirection;
}
}
// Méthode pour vérifier les collisions
this.checkCollision = function ()
{
// Collision sur un mur
var wallCollision = false;
// collision sur lui même
var selfCollision = false;
// On a besoin de tester uniquement la tête, c'est la tête qui entre en collision!
var headSnake = this.body[0]; // headSnake est donc un tableau de coordonnées
var bodySnake = this.body.slice(1); // On coupe à partir de la case 1
// Test de la collision avec un mur
if (
// Mur de droite
headSnake[0] >= widthInBlock ||
// Mur de gauche
headSnake[0] < 0 ||
// Mur du haut
headSnake[1] < 0 ||
// Mur du bas
headSnake[1] >= heightInBlock
)
{
console.log("wall");
wallCollision = true;
}
// Test de la collision avec son propre corps
for ( var i=0; i < bodySnake.length; i++ )
{
if (
headSnake[0] === bodySnake[i][0] &&
headSnake[1] === bodySnake[i][1]
)
{
selfCollision = true;
}
}
return wallCollision || selfCollision;
}
}
// Objet Pomme
// La position doit etre le milieu d'un bloc!
function Apple (position)
{
this.position = position;
this.draw = function ()
{
// Sauvegarde du contexte
ctx.save();
// Couleur de remplissage
ctx.fillStyle = "#33cc33";
ctx.beginPath();
// Le rayon correspond à la moitié d'un bloc
var radius = blocksize / 2;
// La position doit etre le milieu d'un bloc!
var x = this.position[0] * blocksize + radius;
var y = this.position[1] * blocksize + radius;
// Fonction qui va dessiner le cercle
ctx.arc(x, y, radius, 0, Math.PI *2, true);
ctx.fill();
ctx.restore();
}
}
// Evénements du clavier
document.addEventListener('keydown', handleKeydown);
function handleKeydown( ev )
{
var key = ev.key;
var newDirection;
switch (key)
{
case "ArrowLeft":
newDirection = "left";
break;
case "ArrowUp":
newDirection = "up";
break;
case "ArrowRight":
newDirection = "right";
break;
case "ArrowDown":
newDirection = "down";
break;
default:
throw("Invalid code");
}
// On applique le mouvement au serpent
snake.setDirection(newDirection);
}
}
Ajustez la longueur du corps, et la vitesse.
On va ajouter une méthode de test de pomme mangée
Et on appelle cette nouvelle methode dans refreshCanvas
Si la pomme est mangée, on va la déplacer! On pourrait aussi ajouter des points, allonger le serpent...
Il faut aussi vérifier que la pomme n'est pas repositionnée sur le corps du serpent
window.onload = function()
{
// Variables globales
var canvasWidth = 900;
var canvasHeight = 600;
var blocksize = 30;
var canvas, ctx;
var delay = 300; // Delai en millisecondes
var snake;
var apple;
// Définition de la largeur en terme de block et non en pixel
var widthInBlock = canvasWidth / blocksize;
var heightInBlock = canvasHeight / blocksize;
// Appel de la fonction init
init();
// Initialisation du canvas et des objets
function init()
{
// Création du canvas
canvas = document.createElement('canvas');
// Dimensions
canvas.width = canvasWidth;
canvas.height = canvasHeight;
// Style
canvas.style.border = "1px solid";
// Ajout du canvas au body
document.body.appendChild(canvas);
// ******** Dessiner dans le canvas
// Récupération du contexte
ctx = canvas.getContext('2d');
// Construction du serpent avec son body, et maintenant sa direction
snake = new Snake([ [9, 3], [8, 3], [7, 3], [6, 3], [5, 3], [4, 3], [4,3], [4,3], [1,3] ], "right");
apple = new Apple([10,10]);
// Appel de la fonction de refresh
refreshCanvas();
}
// Refresh du canvas
function refreshCanvas()
{
// Avancement du serpent
snake.move();
// On vérifie la collision
if ( snake.checkCollision() )
{
// Game over
console.log("gameover");
}else{
// Pomme mangée?
if ( snake.isEatingApple(apple) ){
// La pomme est mangée!
console.log("Pomme mangée");
// On replace la pomme si elle est par hasard sur le corps du serpent
do{
// Déplacement de la pomme
apple.setNewPosition();
}
while( apple.isOnSnake(snake) );
}
// Nettoyage du canvas
ctx.clearRect(0, 0, canvas.width, canvas.height);
// Dessin du serpent
snake.draw();
// Dessin de la pomme
apple.draw();
// ******* Mise en place d'un timer
setTimeout(refreshCanvas, delay);
}
}
function drawBlock (ctx, position)
{
// Détermination en pixels de la position
var x = position[0] * blocksize;
var y = position[1] * blocksize;
// Remplissage du block avec les coordonnées calculées
ctx.fillRect(x, y, blocksize, blocksize);
}
// Objet Serpent
function Snake(body, direction)
{
// Propriétés du serpent
this.body = body;
this.direction = direction
// Méthode draw
this.draw = function ()
{
// Sauvegarde du contexte
ctx.save();
// Couleur de remplissage
ctx.fillStyle = "#ff0000";
// Boucle
// Le corps du serpent sera un ensembl de petits rectangles
for (var i=0; i < this.body.length; i++)
{
drawBlock(ctx, this.body[i]);
}
ctx.restore();
}
// Méthode pour faire avancer le serpent
this.move = function()
{
// Récupération de la position de la tête du serpent
var nextHeadPosition = this.body[0].slice();
// Quelle direction?
switch (this.direction)
{
case "left":
// Avancement de -1 de la tête sur l'axe X
nextHeadPosition[0] -= 1;
break;
case "right":
// Avancement de 1 de la tête sur l'axe X
nextHeadPosition[0] += 1;
break;
case "up":
// Avancement de -1 de la tête sur l'axe Y
nextHeadPosition[1] -= 1;
break;
case "down":
// Avancement de 1 de la tête sur l'axe Y
nextHeadPosition[1] += 1;
break;
default:
throw("Invalid direction");
}
// On ajoute cette nouvelle position au courps du serpent
// unshift ajoute en première case
this.body.unshift( nextHeadPosition );
// Retrait de la queue du serpent
this.body.pop();
}
// Méthode pour changer la direction
this.setDirection = function ( newDirection )
{
var allowedDirections;
// Quelle direction sont permises?
switch (this.direction)
{
case "left":
case "right":
allowedDirections = ["up", "down"];
break;
case "up":
case "down":
allowedDirections = ["left", "right"];
break;
default:
throw("Invalid direction");
}
// Avant de changer la direction on regarde le contenu du tableau allowedDirections avec la fonction indexOf
if (allowedDirections.indexOf( newDirection ) > -1)
{
this.direction = newDirection;
}
}
// Méthode pour vérifier les collisions
this.checkCollision = function ()
{
// Collision sur un mur
var wallCollision = false;
// collision sur lui même
var selfCollision = false;
// On a besoin de tester uniquement la tête, c'est la tête qui entre en collision!
var headSnake = this.body[0]; // headSnake est donc un tableau de coordonnées
var bodySnake = this.body.slice(1); // On coupe à partir de la case 1
// Test de la collision avec un mur
if (
// Mur de droite
headSnake[0] >= widthInBlock ||
// Mur de gauche
headSnake[0] < 0 ||
// Mur du haut
headSnake[1] < 0 ||
// Mur du bas
headSnake[1] >= heightInBlock
)
{
console.log("wall");
wallCollision = true;
}
// Test de la collision avec son propre corps
for ( var i=0; i < bodySnake.length; i++ )
{
if (
headSnake[0] === bodySnake[i][0] &&
headSnake[1] === bodySnake[i][1]
)
{
selfCollision = true;
}
}
return wallCollision || selfCollision;
}
// Méthode de détection de pomme mangée
this.isEatingApple = function ( apple )
{
// Récupération de la tête du serpent
var headSnake = this.body[0];
// Test de correspondance des coordonnées de la pomme et de la tête
if ( headSnake[0] == apple.position[0] // Coordonnées X
&&
headSnake[1] == apple.position[1] // Coordonnées Y
){
return true;
}else{
return false;
}
}
}
// Objet Pomme
// La position doit etre le milieu d'un bloc!
function Apple (position)
{
this.position = position;
this.draw = function ()
{
// Sauvegarde du contexte
ctx.save();
// Couleur de remplissage
ctx.fillStyle = "#33cc33";
ctx.beginPath();
// Le rayon correspond à la moitié d'un bloc
var radius = blocksize / 2;
// La position doit etre le milieu d'un bloc!
var x = this.position[0] * blocksize + radius;
var y = this.position[1] * blocksize + radius;
// Fonction qui va dessiner le cercle
ctx.arc(x, y, radius, 0, Math.PI *2, true);
ctx.fill();
ctx.restore();
}
// Méthode pour déplacer la pomme
this.setNewPosition = function ()
{
// Nombre ENTIER au hasard entre 0 et widthInBlock -1, soit 29
var newX = Math.round(Math.random() * (widthInBlock -1));
var newY = Math.round(Math.random() * (heightInBlock -1));
// Modification de la position de la pomme
this.position = [newX, newY];
}
// Méthode pour vérifier que la pomme n'a pas été placée sur le corps du serpent!
this.isOnSnake = function ( snake )
{
// Par défaut, à non
var isOnSnake = false;
// Parcours du corps du serpent
for (var i=0; i<snake.body.length; i++)
{
// Test des positions
if (
this.position[0] == snake.body[i][0] // Test du X
&&
this.position[1] == snake.body[i][1] // Test du Y
)
{
isOnSnake = true;
}
}
return isOnSnake;
}
}
// Evénements du clavier
document.addEventListener('keydown', handleKeydown);
function handleKeydown( ev )
{
var key = ev.key;
var newDirection;
switch (key)
{
case "ArrowLeft":
newDirection = "left";
break;
case "ArrowUp":
newDirection = "up";
break;
case "ArrowRight":
newDirection = "right";
break;
case "ArrowDown":
newDirection = "down";
break;
default:
throw("Invalid code");
}
// On applique le mouvement au serpent
snake.setDirection(newDirection);
}
}
Si je mange une pomme, on allonge le serpent
Ca va se placer vers:
// Retrait de la queue du serpent
this.body.pop();
On va dire au serpent de ne pas retirer sa dernière case UNIQUEMENT si une pomme a été mangée!
On modifie donc uniquement la classe serpent:
// Objet Serpent
function Snake(body, direction)
{
// Propriétés du serpent
this.body = body;
this.direction = direction
this.ateApple = false; // Au cas où le serpent viet de manger une pomme
// Méthode draw
this.draw = function ()
{
// Sauvegarde du contexte
ctx.save();
// Couleur de remplissage
ctx.fillStyle = "#ff0000";
// Boucle
// Le corps du serpent sera un ensembl de petits rectangles
for (var i=0; i < this.body.length; i++)
{
drawBlock(ctx, this.body[i]);
}
ctx.restore();
}
// Méthode pour faire avancer le serpent
this.move = function()
{
// Récupération de la position de la tête du serpent
var nextHeadPosition = this.body[0].slice();
// Quelle direction?
switch (this.direction)
{
case "left":
// Avancement de -1 de la tête sur l'axe X
nextHeadPosition[0] -= 1;
break;
case "right":
// Avancement de 1 de la tête sur l'axe X
nextHeadPosition[0] += 1;
break;
case "up":
// Avancement de -1 de la tête sur l'axe Y
nextHeadPosition[1] -= 1;
break;
case "down":
// Avancement de 1 de la tête sur l'axe Y
nextHeadPosition[1] += 1;
break;
default:
throw("Invalid direction");
}
// On ajoute cette nouvelle position au courps du serpent
// unshift ajoute en première case
this.body.unshift( nextHeadPosition );
// Retrait de la queue du serpent UNIQUEMENT si une pomme n'a pas été mangée
if (!this.ateApple)
{
this.body.pop();
}else{
console.log("Allongement du serpent");
// Réinitialisation
this.ateApple = false;
}
}
// Méthode pour changer la direction
this.setDirection = function ( newDirection )
{
var allowedDirections;
// Quelle direction sont permises?
switch (this.direction)
{
case "left":
case "right":
allowedDirections = ["up", "down"];
break;
case "up":
case "down":
allowedDirections = ["left", "right"];
break;
default:
throw("Invalid direction");
}
// Avant de changer la direction on regarde le contenu du tableau allowedDirections avec la fonction indexOf
if (allowedDirections.indexOf( newDirection ) > -1)
{
this.direction = newDirection;
}
}
// Méthode pour vérifier les collisions
this.checkCollision = function ()
{
// Collision sur un mur
var wallCollision = false;
// collision sur lui même
var selfCollision = false;
// On a besoin de tester uniquement la tête, c'est la tête qui entre en collision!
var headSnake = this.body[0]; // headSnake est donc un tableau de coordonnées
var bodySnake = this.body.slice(1); // On coupe à partir de la case 1
// Test de la collision avec un mur
if (
// Mur de droite
headSnake[0] >= widthInBlock ||
// Mur de gauche
headSnake[0] < 0 ||
// Mur du haut
headSnake[1] < 0 ||
// Mur du bas
headSnake[1] >= heightInBlock
)
{
console.log("wall");
wallCollision = true;
}
// Test de la collision avec son propre corps
for ( var i=0; i < bodySnake.length; i++ )
{
if (
headSnake[0] === bodySnake[i][0] &&
headSnake[1] === bodySnake[i][1]
)
{
selfCollision = true;
}
}
return wallCollision || selfCollision;
}
// Méthode de détection de pomme mangée
this.isEatingApple = function ( apple )
{
// Récupération de la tête du serpent
var headSnake = this.body[0];
// Test de correspondance des coordonnées de la pomme et de la tête
if ( headSnake[0] == apple.position[0] // Coordonnées X
&&
headSnake[1] == apple.position[1] // Coordonnées Y
){
// On définit cette propriété à true pour indiquer que le serpent vient de manger une pomme
this.ateApple = true;
return true;
}else{
return false;
}
}
}
On va créer une fonction GameOver. Elle va afficher à l'écran GameOver et un message pour relancer le jeu
// Fonction d'affichage de fin de jeu
function gameOver()
{
ctx.save();
ctx.fillText("GAME OVER", canvasWidth /2 - 20, canvasHeight /2 -10);
ctx.fillText("Press START to play again", canvasWidth /2 -20, canvasHeight /2);
ctx.restore();
}
Fonction de relance du jeu
// Fonction relance le jeu appelée quand on appuie sur espace
function resetGame()
{
// Recrée les objets serpent et pomme
// Construction du serpent avec son body, et maintenant sa direction
snake = new Snake([ [9, 3], [8, 3], [7, 3], [6, 3], [5, 3], [4, 3], [4,3], [4,3], [1,3] ], "right");
apple = new Apple([10,10]);
// Appel de la fonction de refresh
refreshCanvas();
}
Modification de la prise en compte de la touche Espace
// Evénements du clavier
document.addEventListener('keydown', handleKeydown);
function handleKeydown( ev )
{
var key = ev.keyCode;
//console.log(key);
var newDirection;
// https://keyevents.netlify.app/
switch (key)
{
case 37:
newDirection = "left";
break;
case 38:
newDirection = "up";
break;
case 39:
newDirection = "right";
break;
case 40:
newDirection = "down";
break;
case 32:
resetGame();
break;
default:
throw("Invalid code");
}
// On applique le mouvement au serpent
snake.setDirection(newDirection);
}
Vitesse du serpent, enregistrement des scores en localStorage... Jeu multijoueur
Modification des mots clés var en let ou const Fonctions fléchées...