Pong en javascript – raquettes et balle

La saga Pong javascript html5 continue avec les raquettes et la balle….Dans cet épisode, nous allons terminer la mise en place de l’environnement du jeu à proprement parler avant de donner vie au jeu en animant le tout : les raquettes et la balle, ainsi que le score.

 

Prérequis pour les raquettes et la balle

Lire l’épisode 1 de ce tuto.

Rappel de l’épisode 1

Souvenez vous, ce qui est créé dans l’épisode 1. Un objet Layer (comparables aux layers flash) dédié au terrain : un fond noir et un filet.

Deux autres doivent être maintenant créés :
– un dédié à l’affichage du score;
– le dernier dédié à l’animation des raquettes et de la balle.

Les 3 layers

Tout comme pour le terrain, nous créons :
– un layer (canvas html5) pour le score;
– et un layer (canvas html5) pour les raquettes et la balle.

Pourquoi utiliser 3 layer au lieu d’un seul ?
– la première raison est l’optimisation. Avec un seul layer, il va falloir rafraîchir tous les éléments du jeu que sont les raquettes, la balle, le score, et le filet à chaque rafraîchissement;
– la seconde raison est de l’ordre purement conceptuel. On regroupe des éléments de même nature dans un layer dédié.

Les layer sont superposables par le biais de la propriété zIndex. Il est donc permis de superposer nos 3 layer html5 . Il reste à déterminer dans quel ordre. Un layer avec un zIndex sera toujours placé devant un layer html5 avec un zIndex plus faible.

Le layer html5 terrain n’a pas vocation à changer : aucune animation. Il semble être de bon sens de le placer dans la couche la plus basse, en dessous de toutes les autres.

Le layer html5 dédié aux raquettes et à la balle sera en perpétuel changement du fait de l’animation des raquettes et de la balle.

Le dernier layer html5 dédié au score changera uniquement lorsqu’il y aura perte de balle.

Pour résumer :
– layer terrain : le plus bas;
– layer raquettes : le plus haut;
– layer score : entre les 2 précédents.

Les 3 layer html5 ont les mêmes dimensions. Toutefois et pour éviter le rafraîchissement perpétuel du terrain et du score, les 2 layers supérieurs (dédiés aux raquettes et au score) sont transparents de manière à laisser apparaître le layer terrain.

La création des 3 layer html5 dans la fonction init du namespace javascript game :

.....
 groundColor : "#000000",
 netColor : "#FFFFFF",
 groundLayer : null,  
 scoreLayer : null,
 playersBallLayer : null,
 
 init : function() {
  this.groundLayer = game.display.createLayer("terrain", this.groundWidth, this.groundHeight, undefined, 0, "#000000", 0, 0);
  game.display.drawRectangleInLayer(this.groundLayer, this.netWidth, this.groundHeight, this.netColor, this.groundWidth/2 - this.netWidth/2, 0);
  this.scoreLayer = game.display.createLayer("score", this.groundWidth, this.groundHeight, undefined, 1, undefined, 0, 0);
  this.playersBallLayer = game.display.createLayer("joueursetballe", this.groundWidth, this.groundHeight, undefined, 2, undefined, 0, 0); 
  }
.....

A l’exécution, les 2 nouveaux layer html5 sont invisibles. Pour voir qu’ils sont bien créés et existent, ajoutons une fonction d’affichage de texte au namespace javascript game.display.

....
 drawTextInLayer : function(targetLayer, text, font, color, x, y) {
  targetLayer.context2D.font = font;
  targetLayer.context2D.fillStyle = color;
  targetLayer.context2D.fillText(text, x, y);
 }
.....

Puis appelons les pour afficher du texte en rouge dans les 2 nouveaux layer html5.

.....
init : function() {
 this.groundLayer = game.display.createLayer("terrain", this.groundWidth, this.groundHeight, undefined, 0, "#000000", 0, 0); 
 
 game.display.drawRectangleInLayer(this.groundLayer, this.netWidth, this.groundHeight, this.netColor, this.groundWidth/2 - this.netWidth/2, 0);
 this.scoreLayer = game.display.createLayer("score", this.groundWidth, this.groundHeight, undefined, 1, undefined, 0, 0);
 
 game.display.drawTextInLayer(this.scoreLayer, "SCORE", "10px Arial", "#FF0000", 10, 10);
 this.playersBallLayer = game.display.createLayer("joueursetballe", this.groundWidth, this.groundHeight, undefined, 2, undefined, 0, 0);   
 
 game.display.drawTextInLayer(this.playersBallLayer, "JOUEURSETBALLE", "10px Arial", "#FF0000", 100, 100);
}
.....

Voici ce que ça donne à l’exécution.

L’affichage du score

Pour assurer l’affichage du score, nous réutilisons la fonction drawTextInLayer créée précédemment.

Toutefois, pour des raisons de clarté du code et en faciliter la relecture, la fonction est encapsulée dans une autre dédiée au jeu vidéo html5 pong avec un nom très parlant.

Ceci engendre une spécificité propre au jeu vidéo html5 pong et donc non réutilisable.
En conséquence, nous l’intégrons dans le namespace game.

Cette fonction prend 2 paramètres que sont les scores des 2 joueurs.

const game = {
 .....
 displayScore : function(scorePlayer1, scorePlayer2) {
 }
}

Puis elle affiche les scores au centre sur le haut du terrain en n’oubliant pas de cibler le bon layer html5. Il faut au préalable déterminer les positions des 2 scores.

La position du score du joueur 1 est à 300 pixels du bord gauche du canvas html5.
La position du score du joueur 2 est à 365 pixels du bord gauche du canvas html5.

Nous définissons 2 nouvelles variables dédiées dans le namespace game.

const game = {
 .....
 scorePosPlayer1 : 300,
 scorePosPlayer2 : 365,
 .....
 displayScore : function(scorePlayer1, scorePlayer2) {
  game.display.drawTextInLayer(this.scoreLayer, scorePlayer1, "60px Arial", "#FFFFFF", this.scorePosPlayer1, 55);
  game.display.drawTextInLayer(this.scoreLayer, scorePlayer2, "60px Arial", "#FFFFFF", this.scorePosPlayer2, 55);
 }
}

Voici ce que ça donne à l’exécution.

Le tracé des raquettes et de la balle

Au même titre que le score, nous ajoutons 2 fonctions dédiées respectivement à l’affichage de la balle et des raquettes :
– displayBall pour la balle;
– displayPlayers pour les raquettes.

Fonctions dédiées donc à ajouter au namespace javascript game.

Pour la balle, nous avons besoin de définir :
– sa taille en pixels (longueur et largeur);
– sa couleur;
– et sa position (coordonnées abscisse (x) et ordonnée (y)).

Nous ajoutons donc autant de propriétés au namespace javascript game.

Encore mieux, nous encapsulons ces propriétés dans un objet ball.

const game = {
 .....
 ball : {
  width : 10,
  height : 10,
  color : "#FFFFFF",
  posX : 200,
  posY : 200
 },
 
 init : function() {
 .....

Il reste à la dessiner avec un simple carré qui prendra les dimensions et couleur définis dans l’objet. Nous réutilisons la fonction drawRectangleInLayer que nous avons définie dans l’article précédent.

displayBall : function() {
 game.display.drawRectangleInLayer(this.playersBallLayer, this.ball.width, this.ball.height, this.ball.color, this.ball.posX, this.ball.posY);
} 

Il ne reste plus qu’à l’appeler depuis la fonction init pour voir le résultat.

.....
init : function() {
.....
 this.displayScore(0,0);
 this.displayBall(200,200);
},
......

Le résultat.

Passons aux raquettes pour lesquelles nous avons besoin de définir :
– la taille en pixels (longueur et largeur);
– la couleur;
– la position (coordonnées abscisse (x) et ordonnée (y)).

Reprenons la même méthode, à savoir un objet dédié pour chacune des raquettes.

const game = {
 .....
 playerOne : {
  width : 10,
  height : 10,
  color : "#FFFFFF",
  posX : 10,
  posY : 200
 },
   
 playerTwo : {
  width : 10,
  height : 10,
  color : "#FFFFFF",
  posX : 600,
  posY : 200
 },
 
 init : function() {
 .....

Très proche de l’objet ball, alors pourquoi un deux objets supplémentaires identiques. Simple et bonne raison : une sémantique permettant une relecture facile du code.

Nous définissons ensuite la fonction d’affichage des raquettes.

displayPlayers : function() {
 game.display.drawRectangleInLayer(this.playersBallLayer, this.playerOne.width, this.playerOne.height, this.playerOne.color, this.playerOne.posX, this.playerOne.posY);
 game.display.drawRectangleInLayer(this.playersBallLayer, this.playerTwo.width, this.playerTwo.height, this.playerTwo.color, this.playerTwo.posX, this.playerTwo.posY);
} 

Et l’appelons depuis la fonction init.

.....  
init : function() {
 this.terrainLayer= game.display.createLayer("terrain", this.groundWidth, this.groundHeight, undefined, 0, "#000000", 0, 0); 
 game.display.drawRectangleInLayer(this.groundLayer, this.netWidth, this.groundHeight, this.netColor, this.groundWidth/2 - this.netWidth/2, 0);
 
 this.scoreLayer = game.display.createLayer("score", this.groundWidth, this.groundHeight, undefined, 1, undefined, 0, 0);
 game.display.drawTextInLayer(this.scoreLayer , "SCORE", "10px Arial", "#FF0000", 10, 10);
   
 this.playersBallLayer = game.display.createLayer("joueursetballe", this.groundWidth, this.groundHeight, undefined, 2, undefined, 0, 0); 
 game.display.drawTextInLayer(this.playersBallLayer, "JOUEURSETBALLE", "10px Arial", "#FF0000", 100, 100);
   
 this.displayScore(0,0);
 this.displayBall();
 this.displayPlayers();
},
.....  

Le résultat.

La prochaine étape de cette série va consister à animer la balle en la faisant rebondir sur les murs à suivre ici.

Explore l'univers de la création de jeux vidéo

Saisis ton prénom et ton e-mail pour recevoir ton livre GRATUIT et commence ton voyage ludique dès maintenant.

Bravo, jette un œil à ta boite mail pour télécharger ton guide.