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

Finalisation du jeu de casse-briques

Gérer un écran de début de partie

Maintenant que ce projet de jeu prend forme, il est temps de l’étoffer en proposant au joueur un petit écran d’accueil qui lui permettra de lancer une partie quand il le souhaite. 

1. Présenter le jeu au joueur et attendre que celui-ci déclenche la partie

Nous devons créer une fonction dont le but principal est l’affichage d’une boîte de message comportant un titre et un corps de texte, tous deux paramétrables, et l’attente d’une action utilisateur. Une fonction est également passée en paramètre pour être exécutée lorsque l’utilisateur valide le message.

Afin de réaliser cette opération, il faut passer par une restructuration du code, pour pouvoir changer la manière dont le jeu démarre. En effet, actuellement, le jeu débute dès la fin du chargement de la page, ce qui n’est pas très pratique du point de vue du joueur, et changer ce mode de fonctionnement ne peut pas se faire sans modifications assez importantes du code.

Tout d’abord, la fonction init() ne doit plus exécuter la fonction JavaScript setInterval() puisque c’est cette fonction qui provoque le déplacement de la balle. Ensuite, la fin de l’affichage du mur de briques doit provoquer la présentation du message d’accueil au lieu d’ajouter une balle dans le jeu. C’est la fonction exécutée au déclenchement par l’utilisateur depuis cet écran d’accueil qui doit à la fois lancer la création de la balle et exécuter l’appel à la fonction setInterval(), et donc le début de la partie.

On nomme cette nouvelle fonction gameMessage() et son code est le suivant :

function gameMessage(title, messageText, messageButton, buttonFunction) 
{ 
   $('body').append('<div class="messageBox"><label class="lblMessage
Title">' + title + '</label><label class="lblMessage">' + messageText + 
'</label><button class="btnMessage">' + messageButton + '</button></div>'); ...

Affiner la détection des collisions avec les briques

Pour le moment, le système de détection de collisions de la balle avec les briques est très sommaire, et quand celles-ci se raréfient, la balle est mal renvoyée, certaines collisions ne sont pas détectées alors que d’autres le sont alors qu’elles ne devraient pas l’être.

Il est maintenant temps d’améliorer le système.

1. Prendre en compte le sens de déplacement de la balle

Pour améliorer la qualité des rebonds des balles en jeu lorsqu’elles heurtent les briques, il faut revoir la procédure de détection des collisions de manière à ce que le sens de déplacement des balles et l’endroit de l’impact avec les briques soient plus finement définis.

Nous devons donc modifier le filtre de sélection des briques. Il s’agit de repérer, pour chacune des balles en jeu, les briques à proximité selon le sens de déplacement de la balle analysée. Lorsqu’un choc est détecté, on ajoute une propriété à la balle concernée par ce choc. Cette nouvelle propriété permet de conserver en mémoire la nouvelle direction à prendre une fois le traitement de la collision terminé. Voici une liste des cas de figure à prendre en considération.

Direction de la balle

Briques à retenir pour une collision

Réaction à avoir

La balle va vers le haut ->vSpeed> 0

Le bord inférieur de la brique est à une distance inférieure ou égale à la position haute de la balle et la position latérale de la balle, augmentée de sa largeur, se trouve entre le bord gauche et le bord droit de la brique.

Inverser le sens de déplacement vertical de la balle

La balle va vers le bas ->vSpeed< 0

Le bord supérieur de la brique est à une distance inférieure ou égale à la position haute de la balle, augmentée de sa hauteur, et la position latérale de la balle se trouve entre le bord gauche et le bord droit de la brique, ce dernier diminué de la largeur de la balle.

Inverser le sens de déplacement vertical de la balle

La balle va vers la droite ->hSpeed> 0

Le bord gauche de la brique est à...

Gérer le score du joueur

Lorsque la balle frappe une brique et la détruit, il faut accorder un gain de points au joueur.

Plus le joueur détruit de balles, plus son score devient élevé.

On définit des règles pour le calcul du score :

  • Une brique a une valeur de base de 500 points.

  • Le score de chaque brique ainsi calculé est multiplié par le numéro du niveau en cours, le premier niveau étant le niveau 1.

1. Animer le déplacement d’un score lorsqu’une brique est touchée

Pour égayer un peu le petit jeu, nous souhaitons qu’à la destruction d’une brique, la valeur de cette brique s’affiche à l’endroit où la balle a détruit la brique et remonte jusqu’en haut de l’aire de jeu, en s’estompant progressivement jusqu’à disparaître.

On peut connaître assez simplement les coordonnées de la brique détruite. On stocke dans chaque brique son identifiant et sa position lors de l’injection des briques dans l’aire de jeu. L’identifiant de la brique a la forme "X-Y", où X est le numéro de la ligne et Y celui de la colonne du mur de briques. La méthode JavaScript split() appliquée à une variable de type String se propose d’extraire sous forme de tableau les sous-chaînes de la variable découpées en tranches par le délimiteur fourni en paramètre.

Si on désire tester cela dans la console, cela donne :

images/13-14-001.png

Pour récupérer la ligne de la brique détruite, il faut donc prendre l’indice 0 du tableau retourné par la méthode split(). De manière identique, on retient l’indice 1 pour le numéro de la colonne.

Dans la fonction touchBrick(), lorsqu’une brique est détectée, il faut calculer les points marqués et ajouter un appel à une nouvelle fonction, baptisée showBrickScore(), comme suit :

                if (nearBricks.length > 0) 
                  { 
                      var points = 500 * (curLevel + 1); 
               ...

Gérer la perte de balle

1. L’utilisateur perd la dernière balle en jeu

Lorsque toutes les balles en jeu sont perdues par le joueur, celui-ci doit perdre une vie. Il faut donc attribuer une propriété au joueur qui indique au système le nombre de vies restantes.

On crée donc une variable racketNumber, au tout début du fichier de script principal scripts/main.js.

On crée également une fonction showRemainingLifes() qui indique à l’écran le nombre de vies restantes après la perte de celle en cours. Cette indication se fait par l’apparition de raquettes miniatures positionnées en haut, au-dessus de l’écran de jeu, à droite du score du joueur. Le nombre de raquettes indique le nombre de vies disponibles après la perte de celle en cours d’utilisation. Par exemple, pour un total de trois vies restantes, il en existe une en jeu et deux à utiliser, soit deux raquettes à afficher.

Le code de cette fonction est le suivant :

function showRemainingLifes() 
{ 
   $('.statusBar .life') 
       .remove(); 
   $('.statusBar') 
       .append('<div class="life"></div>'.repeat(racketNumber - 1)); 
} 

On modifie le code de la fonction init() pour exécuter cette fonction de manière à afficher de suite les vies restantes.

    showRemainingLifes(); 

Le style CSS à appliquer pour visualiser correctement cette nouvelle information est le suivant :

.life  
{ 
   display: inline-block; ...

Gérer le passage au niveau suivant

Lorsque la dernière brique du niveau est détruite, le joueur n’a, pour l’instant, plus rien à faire.

Il faut maintenant gérer le passage au niveau suivant, c’est-à-dire :

  • supprimer les balles en jeu,

  • afficher les briques du niveau suivant avec une animation de changement de niveau, 

  • si le joueur atteint le dernier niveau défini dans le tableau des niveaux, reprendre à partir du niveau d’indice 0 en mémorisant le niveau réel pour l’affichage et le calcul du score,

  • mettre une nouvelle balle en jeu.

Pour implémenter une gestion multiniveau du jeu, nous devons procéder à quelques adaptations du code existant.

1. Préparer la gestion multiniveau

Pour jouer sur plus d’un seul niveau, il faut gérer des données décrivant les niveaux supplémentaires et agrémenter l’animation du chargement du mur de briques d’une autre animation, indiquant au joueur le numéro du niveau réel qu’il s’apprête à affronter. 

Selon le cahier des charges, si le dernier niveau est atteint, il faut reprendre l’affichage des niveaux à partir du niveau d’indice 0, sans perdre la trace du niveau réel atteint par le joueur. En ce sens, on crée une variable realLevel dans laquelle on enregistre ce niveau réel. On la crée au début du fichier de script scripts/main.js, en dessous de la variable curLevel. On l’initialise dans la fonction init() du même fichier.

On modifie les lignes qui calculent les points gagnés par le joueur, dans la fonction touchBrick(), pour qu’elles prennent en compte cette nouvelle variable.

var curLevel; 
var realLevel; 
var bricks = []; 
... 
function init() 
{ 
   curLevel = 0; 
   realLevel = 0; 
   playfieldWidth = $('.playfield').width(); 
... 
function touchBrick() 
{ 
... 
               if (nearBricks.length > 0) 
                { 
                   var touchedColor = levels[curLevel]
[nearBricks[0].id.split('-')[0]][nearBricks[0].id.split('-')[1]]; 
 
                   var points = 500 * (realLevel + 1) * 
(colorArray.indexOf(touchedColor) + 1); 
                   var oldScore = playerScore; 
 
... 

Ces modifications permettent de continuer à jouer même après le gain du dernier niveau physiquement prévu.

2. Afficher et gérer le passage au niveau suivant lorsque toutes les briques du niveau ont été touchées

Étant donné que le système supprime les briques du tableau bricks à chaque fois que l’une d’entre elles est touchée, il est facile de déduire qu’un niveau est terminé lorsque le tableau des briques devient vide. Donc, après avoir supprimé une brique lors d’une collision avec la balle, il suffit de contrôler la propriété length du tableau de briques et de déclencher le processus de changement de niveau lorsque celle-ci atteint la valeur 0. Une fois qu’un niveau est terminé, on procède à la suppression de la balle, de manière à ce qu’elle ne gêne pas l’affichage du niveau suivant.

On crée une fonction cleanLevel() pour réaliser cette tâche :

function cleanLevel() 
{ 
   $('.ball,.brickLine') 
        .remove(); 
   balls = []; 
} 

On commence par stopper le rafraîchissement de la position de la balle, devenu inutile. Ensuite, on procède à la suppression de tous les éléments du DOM possédant la classe CSS ball. Enfin, la procédure se poursuit par la réinitialisation du tableau JavaScript balls.

Le test de détection de la fin du niveau s’effectue dans la fonction touchBrick(), lorsque l’on vient de détruire une brique.

                if (nearBricks.length > 0) 
                 { 
                   var touchedColor = levels[curLevel]
[nearBricks[0].id.split('-')[0]][nearBricks[0].id.split('-')[1]]; ...

Gérer des bonus/malus

Rattraper une balle, c’est un peu monotone et on s’ennuie assez rapidement, à la longue. Pour varier un peu les plaisirs et la difficulté du jeu, on demande au système de laisser tomber des objets pouvant être ramassés par le joueur avec la raquette. Ces objets modifieront alors les propriétés du jeu, le rendant plus ou moins facile pour le joueur.

1. Des objets tombent lorsque certaines briques sont touchées

Lorsqu’une brique est touchée et disparaît du jeu, on teste une valeur aléatoire et, si elle atteint un certain niveau, on décide qu’un bonus ou un malus doit être dispensé. Nous écrivons donc une nouvelle fonction nommée checkForExtra(), qui doit être exécutée depuis la fonction touchBrick() au moment de la suppression d’une brique touchée. Cette fonction décide si un objet (appelé extra dans ce qui suit) doit tomber depuis l’endroit du choc entre la balle et la brique et l’ajoute à la partie. Elle appelle la fonction spécifique nommée chooseExtraType(), qui choisit, de manière aléatoire également, le type de l’extra à générer.

function checkForExtra(position) 
{ 
   if (Math.random() > .9) 
   { 
       var extraId = $('.extra').length + 1; 
       $('.playfield') 
           .append('<div class="extra" data-id="' + extraId + '">' + 
chooseExtraType() + '</div>');             
       $('.extra[data-id="' + extraId+ '"]') 
           .css ( 
               { 
                   left: (position.left + 50) + 'px', 
                   top: (position.top + 34) + 'px' 
               } 
                ) 
           .animate ( 
 ...

Gérer le Game Over

Lorsque le joueur perd la dernière balle en jeu et que son compteur de vies est à 0, il faut déclencher le processus de fin de partie, le célèbre Game Over.

Le processus à mettre en place est tout simple. Il consiste en l’affichage d’un message indiquant la fin de la partie et rappelant le score du joueur.

On crée une fonction showGameOver() pour exécuter cette tâche.

Il nous faut modifier le code qui gère la perte des balles en jeu pour déclencher la procédure de fin de partie lorsqu’une balle est perdue et que le joueur n’a plus de vies.

                if (e.top > racket.top) 
                { 
                   $('.ball[data-id="' + e.id + '"]').remove(); 
                   balls.splice(balls.indexOf(e), 1); 
                   if (balls.length == 0) 
                   { 
                       if (racketNumber > 0) 
                       { 
   ...

Quelques idées pour améliorer le jeu

Le projet est maintenant terminé. Il est bien sûr possible de l’améliorer. Voici quelques pistes de réflexion pour ajouter certaines fonctionnalités.

Bonus/malus

Vous pouvez gérer de nombreux autres types d’extras, par exemple :

  • T (tir) : la raquette se voit agrémentée d’un petit rectangle au centre, légèrement sur le dessus, faisant office de canon et lorsque le joueur clique sur la souris, un obus est propulsé vers le haut de l’écran et détruit la première brique qu’il rencontre. Possibilité de tir en rafale : à chaque clic, un obus. L’option est désactivée lorsqu’un extra est attrapé, quel qu’il soit.

  • M (missile) : la raquette se voit agrémentée d’un rectangle un peu plus long que pour le tir, faisant office de lanceur de missiles et, lorsque le joueur clique sur le bouton de la souris, un missile est libéré. Celui-ci décolle alors et monte jusqu’à disparaître en haut de l’écran en détruisant toutes les briques qu’il rencontre sur son passage. Si le missile n’a pas été tiré lorsqu’un autre extra est rattrapé, quel qu’il soit, il est perdu.

  • C (clignotement) : la raquette clignote. Elle devient...