Blog ENI : Toute la veille numérique !
🐠 -25€ dès 75€ 
+ 7 jours d'accès à la Bibliothèque Numérique ENI. Cliquez ici
Accès illimité 24h/24 à tous nos livres & vidéos ! 
Découvrez la Bibliothèque Numérique ENI. Cliquez ici
  1. Livres et vidéos
  2. JavaScript et jQuery
  3. Création d'un jeu de casse
Extrait - JavaScript et jQuery La programmation web par la pratique
Extraits du livre
JavaScript et jQuery La programmation web par la pratique Revenir à la page d'achat du livre

Création d'un jeu de casse-briques

Présentation du projet

1. Description du projet

L’objet de ce projet est de concevoir et de réaliser de façon simple et assez sommaire, un jeu de casse-briques. Dans un jeu de ce genre, une aire de jeu contient des briques de couleur dans sa partie supérieure et une balle y circule. Celle-ci change de direction lorsqu’elle heurte les murs latéraux et lorsqu’elle casse une brique en la frappant.

Le joueur dispose d’un outil, conventionnellement appelé "raquette", placé en bas de la zone de jeu. Il peut contrôler la position horizontale de cette raquette en manipulant sa souris ou bien les touches fléchées de son clavier. Lorsque la balle touche la raquette, elle est renvoyée vers le haut de l’écran et donc vers les briques restantes. Si toutes les briques du niveau sont détruites, le niveau suivant est dessiné et la partie continue. Lorsqu’une brique est touchée par la balle, et donc supprimée du niveau courant, un bonus/malus peut apparaître, symbolisé par un objet tombant vers le bas de la zone de jeu. Si le joueur attrape le bonus/malus avec la raquette, ses effets lui sont attribués.

Si le joueur manque la balle, celle-ci sort du jeu et provoque la destruction de la raquette. Au bout de trois raquettes détruites, le joueur a perdu et la partie est terminée. Le score du joueur et ses raquettes...

Gérer une balle

1. Afficher une balle dans la page web

Avant de pouvoir exécuter du code JavaScript dans une page web, il est nécessaire de s’assurer que celle-ci est bien complètement chargée. Dans le cas contraire, le risque serait, par exemple, de tenter d’accéder par programmation à des objets du DOM non encore en mémoire.

Lorsque le chargement d’une page implémentant la bibliothèque jQuery est terminé, un évènement ready est déclenché. Une fonction JavaScript, nommée par exemple init(), est créée et elle a en charge de préparer le système. La page HTML du jeu est programmée pour que cette fonction soit exécutée lorsque cet évènement ready survient. 

Techniquement, une balle est un élément dont le système doit connaître certaines propriétés, a minima : son identifiant, sa position horizontale, sa position verticale, sa vitesse et son sens de déplacement sur l’axe horizontal et sa vitesse et son sens de déplacement sur l’axe vertical. Un objet possédant ces caractéristiques est créé et stocké dans un tableau de portée globale. Ce tableau est nommé balls dans le projet et est initialisé à vide, comme suit :

var balls = []; 

Pour afficher une balle dans l’aire de jeu, on procède à ce qui s’appelle de l’injection de code : le code HTML nécessaire pour permettre l’affichage est introduit dans le DOM par l’exécution de code JavaScript.

Pour créer une balle, il faut injecter du code HTML permettant de représenter cet objet à l’écran et créer en parallèle un objet JavaScript implémenté et stocké dans le tableau balls.

Voici la fonction effectuant ces tâches :

function addBall() 
{ 
    var idBall = createId(); 
   $('.playfield') 
       .prepend('<div class="ball" data-id="' + idBall + '"></div>'); 
   balls 
       .push     ( 
               { 
 ...

Dessiner le niveau courant

Il est maintenant temps de prendre en compte les briques à casser. En effet, le jeu doit afficher un ensemble de briques que le joueur doit détruire avec la balle afin d’accéder au niveau suivant.

1. Concevoir un système de configuration de niveaux

Une brique, pour un jeu de ce type, est matérialisée, techniquement, par un simple rectangle de couleur.

Les briques sont rangées sur plusieurs lignes dans le haut de l’aire de jeu. Le meilleur moyen de stocker des informations de ce genre, à savoir une suite homogène d’éléments de même nature, consiste en un tableau. Pour implémenter la notion de ligne, on utilise en fait un tableau (le niveau) de tableaux (les lignes du niveau) de briques.

De manière à rendre le jeu facilement éditable et ses méthodes facilement portables, un fichier externe est créé pour contenir la définition des niveaux. Il est constitué d’une simple affectation du tableau nommé levels qui contient dans un premier temps, un seul niveau, composé seulement du nom de la couleur de chaque brique, ligne par ligne :

var levels = [ 
           [ 
               [ 
                   "blue", "blue", "blue", "blue", "blue", "blue", 
"blue", "blue", "blue", "blue", 
               ], 
               [ 
                   "blue", "blue", "blue", "blue", "blue", "blue", 
"blue", "blue", "blue", "blue", 
               ], 
               [ 
                   "red", "red", "red", "red", "red", "red", "red", 
"red", "red", "red", 
               ], 
     ...

Gérer les collisions de la balle avec les briques

Il y a au moins trois problèmes à résoudre.

Tout d’abord, la balle se met à se déplacer alors que l’animation des briques n’est pas terminée. De plus, sa position de départ n’est pas bonne, située beaucoup trop haut. Enfin, une fois le rebond du bas effectué, elle remonte et passe sous les briques, sans être affectée le moins du monde par leur présence.

1. Attendre la fin des animations des briques pour lancer la balle

L’animation de la balle et l’animation de l’affichage du mur de briques sont exécutées toutes les deux depuis la fonction init(), c’est pour cela qu’elles se déroulent simultanément.

Pour pallier cet état de fait, il faut rendre l’une dépendante de la fin de l’exécution de l’autre.

Comme précisé précédemment, la fonction animate() de la bibliothèque jQuery accepte optionnellement une fonction comme paramètre. Celle-ci est exécutée une fois l’animation terminée.

Nous allons donc suivre cette piste et demander au système de ne lancer la balle qu’une fois l’animation de la dernière brique du mur terminée.

Pour cela, on modifie le code de la fonction qui crée la balle et de celle qui affiche le mur de briques. Tout d’abord, dans la fonction init(), on retire l’appel à la méthode addBall().

function init() 
{ 
   curLevel = 0; 
   playfieldWidth = $('.playfield').width(); 
   playfieldHeight = $('.playfield').height(); 
   drawPlayfield(); 
   gameRefresh = setInterval(drawBalls, 10); 
} 

Ensuite, on le place dans la fonction de la boucle de la deuxième animation figurant dans la fonction drawPlayfield(), qui se trouve du coup modifiée de la manière suivante :

    bricks.forEach(function (e, i) 
                     { 
                       $('.brick[data-id="' + e.id + '"]') 
                   ...

Ajouter une raquette et gérer ses déplacements

Actuellement, la balle rebondit à n’en plus finir, éternellement, dans les quatre coins de l’aire de jeu. Il est temps d’ajouter une touche d’interaction avec l’utilisateur, en gérant la raquette.

1. Insérer une raquette dans l’aire de jeu

Pour insérer celle-ci, il faut modifier le fichier main.html, en ajoutant une balise div à l’intérieur de l’aire de jeu, comme suit :

<!DOCTYPE html> 
<html lang="fr-FR"> 
   <head> 
       <title>Casse-briques</title> 
       <meta charset="utf-8"> 
       <link rel="stylesheet" type="text/css" href="styles/reset.css" /> 
       <link rel="stylesheet" type="text/css" href="styles/main.css" /> 
       <script src="scripts/jquery-3.1.1.min.js"></script> 
       <script src="data/levelMaps.json"></script>         
        <script src="scripts/main.js"></script> 
   </head> 
   <body> 
       <div class="playfield"> ...

Gérer les collisions de la balle avec la raquette

Le jeu est presque fonctionnel ! Il reste encore à gérer la rencontre de la balle avec la raquette.

Nous modifions de nouveau la procédure qui gère les déplacements de la balle de manière à prendre en compte la position de la raquette et, si la raquette se trouve sous la balle, on renvoie celle-ci vers les briques. Dans le cas contraire, on la considère comme perdue et on la retire de l’aire de jeu.

Nous ajoutons une propriété top à la variable racket, dans la fonction init(). Dans cette variable, on stocke la position verticale, puisqu’il s’agit en fait de la limite que la balle ne doit en aucun cas dépasser et que cette valeur ne change pas pendant la partie.

Afin d’obtenir cette position, nous utilisons un sélecteur jQuery pour sélectionner la raquette et exécuter la méthode jQuery offset() pour obtenir la valeur de sa propriété top. Cette méthode renvoie un objet composé de deux propriétés left et top qui correspondent aux coordonnées de l’objet renvoyé par le sélecteur.

Ces coordonnées sont calculées par le système par rapport au coin supérieur gauche de la page web. Il faut donc procéder à un transfert de coordonnées. En effet, l’aire de jeu n’étant pas située tout en haut de la page web, il est nécessaire de lire sa position aussi pour la retrancher de la position de la raquette. Cette gymnastique a pour but d’obtenir la position de la raquette par rapport au coin...

Améliorer le code de la fonction drawBalls()

La fonction drawBalls() comporte maintenant beaucoup de lignes.

Il est temps, comme conseillé dans le chapitre dédié aux bonnes pratiques, de procéder à un "refactoring", de manière à clarifier le code de cette fonction appelée à s’étoffer. Ce genre de modification est dite technique, car elle n’apporte aucun changement (et ne doit surtout pas en apporter) dans ce que le code réalise du côté métier du développement. Par contre, et c’est son but principal, elle aide grandement à organiser le code et améliore donc l’aspect technique du programme, en termes de lisibilité, de robustesse et de maintenance, et parfois, de performances.

Nous remplaçons donc notre code par des appels à des fonctions plus petites.

Nous devons repérer, dans la fonction à réagencer, les blocs de code en fonction de ce qu’ils réalisent, puis créer de petites fonctions dans lesquelles on placera le code de ces blocs et remplacer les blocs originaux par des appels vers ces nouvelles petites fonctions. 

On analyse ce qui se passe dans la fonction drawBalls(). Pour chaque balle, le code est exécuté, itéré. Le premier bloc, à l’intérieur de l’itération, déplace la balle en cours d’itération. Le suivant récupère d’éventuelles briques touchées par la balle. Le bloc d’après détruit la brique touchée, le cas échéant. Ensuite, un bloc détecte les rebonds sur les bords...