Cahier de vacances HTML 5–Développer son propre jeu de casse-briques en HTML 5 avec Canvas et SVG - Eternal Coding - HTML5 / Windows / Kinect / 3D development - Site Home - MSDN Blogs

Cahier de vacances HTML 5–Développer son propre jeu de casse-briques en HTML 5 avec Canvas et SVG


 

Cahier de vacances HTML 5–Développer son propre jeu de casse-briques en HTML 5 avec Canvas et SVG

  • Comments 1

Je l’avais déjà annoncé sur ce blog, nous avons réalisé avec mes comparses Stanislas Quastana et David Rousset une série de tutoriaux autour de HTML 5 : Les cahiers de vacances HTML 5.

Couverture Cahier HTML5

C’est au donc au tour de mon sujet sur le Canvas et le SVG de paraitre! Vous pourrez retrouver tout le contenu (au format Word et PDF) sur le site du MSDN ci-dessus.

Le starter et la solution complète sont disponibles ici.

Introduction

L’objectif de ce tutoriel est de découvrir le développement graphique avec SVG et Canvas qui constituent deux technologies importantes de HTML5.

Pour ce faire nous allons tout au long de ce cahier développer un petit jeu de casse-briques (à la Arkanoïd pour les vétérans du fond de la classe).

Le jeu proprement dit sera construit grâce à de SVG et le fond d’écran (qui n’est que décoratif) sera fait avec un canvas.

La version finale peut être découverte ici : http://www.catuhe.com/ms/index.htm

Prérequis

Mise en place du fond d’écran

L’objectif ici est de découvrir l’utilisation du canvas qui permet de faire du dessin au pixel dans une zone définie. De ce fait le fond d’écran, afin de donner une dimension spatiale, va dessiner un wormhole ou trou de ver (moyen de transport bien connu des fans de Stargate). Ce trou de ver sera affiché lorsque l’utilisateur cliquera sur le bouton « Mode Trou de Ver »

Ce dernier ressemblera à ceci :

clip_image003

Nous en profiterons pour rajouter un petit compteur de performances pour jauger l’accélération matérielle.

Mettre en place la page

A partir du fichier index.htm, nous allons rajouter notre canvas sous le div dont l’id est « gameZone » :

  1. <canvas id="Canvas1">
  2.     Votre navigateur ne supporte pas HTML 5. Vous pouvez telecharger Internet Explorer 9 pour acceder a cette technologie :
  3.     <br />
  4.     <a href="http://www.microsoft.com/france/windows/internet-explorer/telecharger-ie9.aspx">http://www.microsoft.com/france/windows/internet-explorer/telecharger-ie9.aspx</a>
  5. </canvas>

C’est ce canvas qui servira de support pour notre dessin.

Rajout du fichier .js

Au niveau de notre projet, nous allons faire bouton droit sur le projet et choisir le menu « Ajouter un nouvel élément ». Ce dernier sera de type « Fichier Javascript » et se nommera « background.js ».

Au niveau de la page index.htm, nous allons rajouter l’appel à ce fichier pour en exécuter le contenu. Cet appel sera mis juste avant la balise de fin de body (</body>) :



Définition des constantes

Tout d’abord nous allons rajouter à notre fichier background.js un ensemble de variables qui joueront le rôle de constantes de configuration :

  1. var circlesCount = 100; // Nombre de cercles dans le wormhole
  2. var offsetX = 70; // D?calage du wormhole sur X
  3. var offsetY = 40; // D?calage du wormhole sur Y
  4. var maxDepth = 1.5; // Distance maximale d'un cercle
  5. var circleDiameter = 10.0; // Diametre d'un cercle
  6. var depthSpeed = 0.001; // Vitesse de rapprochement d'un cercle
  7. var angleSpeed = 0.05; // Vitesse de rotation d'un cercle

Ces constantes vont être utilisées dans le code par la suite et peuvent bien sûr être modifiées pour obtenir de nouveaux effets sur le trou de ver.

Accès aux éléments

Pour travailler nous devons accéder aux éléments de la page et stocker des références vers le canvas et l’élément pour les statistiques de performances:

  1. var canvas = document.getElementById("backgroundCanvas");
  2. var context = canvas.getContext("2d");
  3. var stats = document.getElementById("stats");

Au passage, on récupère aussi le contexte du canvas qui sert à faire le dessin.

Définition de la fonction cercle

Notre trou de ver va être composé d’une suite de cercles de plus en plus proches et qui se décalent autour d’un centre.

Pour dessiner chaque cercle nous allons définir une fonction circle qui acceptera en paramètre une profondeur, un angle de rotation et une intensité :

  1. function Circle(initialDepth, initialAngle, intensity) {
  2. }

Cette fonction va stocker des variables locales avec l’angle et l’intensité ainsi qu’une variable publique pour la profondeur (afin d’y accéder depuis l’extérieur) :

  1. function Circle(initialDepth, initialAngle, intensity) {
  2.  
  3.     var angle = initialAngle;
  4.     this.depth = initialDepth;
  5.     var color = intensity;
  6. }

Pour finir cette fonction Circle va définir une fonction draw pour dessiner le cercle et faire progresser la profondeur et l’angle. Le but va donc être de définir la position dans l’espace du cercle avec un x,y et depth.
Le x et le y sont écartés du centre de l’écran en fonction d’un angle sur une simple équation de cercle :

  1. var x = offsetX * Math.cos(angle);
  2. var y = offsetY * Math.sin(angle);

Par la suite, il faut projeter ces coordonnées sur l’écran grâce à la fonction perspective :

  1. function perspective(fov, aspectRatio, x, y) {
  2.     var yScale = Math.pow(Math.tan(fov / 2.0), -1);
  3.     var xScale = yScale / aspectRatio;
  4.  
  5.     var M11 = xScale;
  6.     var M22 = yScale;
  7.  
  8.     var outx = x * M11 + canvas.width / 2.0;
  9.     var outy = y * M22 + canvas.height / 2.0;
  10.  
  11.     return { x: outx, y: outy };
  12. }

Nous obtenons donc le code suivant pour obtenir les coordonnées écrans du centre de notre cercle :

  1. var x = offsetX * Math.cos(angle);
  2. var y = offsetY * Math.sin(angle);
  3.  
  4. var project = perspective(0.9, canvas.width / canvas.height, x, y);
  5. var diameter = circleDiameter / this.depth;
  6.  
  7. var ploX = project.x - diameter / 2.0;
  8. var ploY = project.y - diameter / 2.0;

Pour finir il faut dessiner le cercle dans le canvas grâce à un path et un arc :

  1. context.beginPath();
  2. context.arc(ploX, ploY, diameter, 0, 2 * Math.PI, false);
  3. context.closePath();
  4.  
  5. var opacity = 1.0 - this.depth / maxDepth;
  6. context.strokeStyle = "rgba(" + color + "," + color + "," + color + "," + opacity + ")";
  7. context.lineWidth = 4;
  8.  
  9. context.stroke();

A noter que l’opacité est proportionnelle à la distance pour faire disparaitre les cercles au loin.

En conclusion la fonction Circle s’écrit ainsi :

  1. function Circle(initialDepth, initialAngle, intensity) {
  2.     
  3.     var angle = initialAngle;
  4.     this.depth = initialDepth;
  5.     var color = intensity;
  6.  
  7.     this.draw = function () {
  8.         var x = offsetX * Math.cos(angle);
  9.         var y = offsetY * Math.sin(angle);
  10.  
  11.         var project = perspective(0.9, canvas.width / canvas.height, x, y);
  12.         var diameter = circleDiameter / this.depth;
  13.  
  14.         var ploX = project.x - diameter / 2.0;
  15.         var ploY = project.y - diameter / 2.0;
  16.  
  17.         context.beginPath();
  18.         context.arc(ploX, ploY, diameter, 0, 2 * Math.PI, false);
  19.         context.closePath();
  20.  
  21.         var opacity = 1.0 - this.depth / maxDepth;
  22.         context.strokeStyle = "rgba(" + color + "," + color + "," + color + "," + opacity + ")";
  23.         context.lineWidth = 4;
  24.  
  25.         context.stroke();
  26.  
  27.         this.depth -= depthSpeed;
  28.         angle += angleSpeed;
  29.  
  30.         if (this.depth < 0) {
  31.             this.depth = maxDepth + this.depth;
  32.         }
  33.     };
  34. };


Initialisation

Grâce à notre fonction Circle nous allons pouvoir faire un tableau de cercles que l’on va initialiser de plus en plus proches de nous avec un léger décalage de l’angle à chaque fois :

  1. // Initialisation
  2. var circles = [];
  3.  
  4. var angle = Math.random() * Math.PI * 2.0;
  5.  
  6. var depth = maxDepth;
  7. var depthStep = maxDepth / circlesCount;
  8. var angleStep = (Math.PI * 2.0) / circlesCount;
  9. for (var index = 0; index < circlesCount; index++) {
  10.     circles[index] = new Circle(depth, angle, index % 5 == 0 ? 200 : 255);
  11.  
  12.     depth -= depthStep;
  13.     angle -= angleStep;
  14. }


Calcul du nombre d’images par seconde

Le calcul du nombre d’images par seconde est basé sur le temps passé entre deux appels à la fonction computeFPS. Cette dernière doit donc stocker le temps mis en millisecondes lors de chacun des 60 derniers appels (grâce à la fonction Date). Elle va ensuite faire la moyenne du temps passé par image et finalement diviser 1000 par ce chiffre pour obtenir le nombre d’images potentiel par seconde :

  1. // Calcul du FPS
  2. var previous = [];
  3. function computeFPS() {
  4.     if (previous.length > 60) {
  5.         previous.splice(0, 1);
  6.     }
  7.     var start = (new Date).getTime();
  8.     previous.push(start);
  9.     var sum = 0;
  10.  
  11.     for (var id = 0; id < previous.length - 1; id++) {
  12.         sum += previous[id + 1] - previous[id];
  13.     }
  14.  
  15.     var diff = 1000.0 / (sum / previous.length);
  16.  
  17.     stats.innerHTML = diff.toFixed() + " fps";
  18. }


Dessin et animations

Comme le canvas est en mode direct, il nous faut redessiner toute l’image dès que l’on veut faire bouger un élément. Nous devons donc faire une fonction qui vide l’écran avant de lancer le dessin proprement dit. Pour ce faire, nous allons juste dessiner un rectangle noir sur toute la surface du canvas :

  1. // Dessin & Animation
  2. function clearCanvas() {
  3.     context.fillStyle = "rgb(0, 0, 0)";
  4.     context.fillRect(0, 0, canvas.width, canvas.height);
  5. }

Nous allons développer une fonction wormHole qui doit effacer le canvas, vérifier qu’il occupe bien toute la surface de son contenant et parcourir tous les cercles afin d’appeler la fonction draw() sur chacun :

  1. function wormHole() {
  2.     computeFPS();
  3.     canvas.width = window.innerWidth;
  4.     canvas.height = window.innerHeight - 130 - 40;
  5.     clearCanvas();
  6.     for (var index = 0; index < circlesCount; index++) {
  7.         circles[index].draw();
  8.     }
  9. }

Finalement, pour éviter d’avoir de mauvais recouvrement il faut toujours dessiner les cercles du fond vers le devant. Il faut donc trier les cercles après chaque passe (car ces deniers sont renvoyés vers le fond quand ils sortent de l’écran :

  1. function wormHole() {
  2.     computeFPS();
  3.     canvas.width = window.innerWidth;
  4.     canvas.height = window.innerHeight - 130 - 40;
  5.     clearCanvas();
  6.     for (var index = 0; index < circlesCount; index++) {
  7.         circles[index].draw();
  8.     }
  9.  
  10.     circles.sort(function (a, b) {
  11.         if (a.depth > b.depth)
  12.             return -1;
  13.         if (a.depth < b.depth)
  14.             return 1;
  15.         return 0;
  16.     });
  17. }


Branchement sur le bouton “Mode trou de ver”

Pour finir nous allons nous brancher sur le bouton « Mode Trou de Ver » pour activer ou désactiver le trou de ver sur demande.

Pour cela nous devons définir une fonction startWormHole qui lancera la fonction wormHole 60 fois par seconde avec un setInterval. Elle se chargera de modifier le texte du bouton pour indiquer « Mode Standard ». De la même manière nous allons créer une fonction stopWormHole qui arrêtera le setInterval et qui remettra le texte du bouton sur « Mode Trou de Ver ». De plus ces deux fonctions vont alternativement mettre l’évènement onclick du bouton sur l’autre fonction pour gérer l’alternance :

  1. var wormHoleIntervalID = -1;
  2.  
  3. function startWormHole() {
  4.     if (wormHoleIntervalID > -1)
  5.         clearInterval(wormHoleIntervalID);
  6.  
  7.     wormHoleIntervalID = setInterval(wormHole, 16);
  8.  
  9.     document.getElementById("wormHole").onclick = stopWormHole;
  10.     document.getElementById("wormHole").innerHTML = "Mode Standard";
  11. }
  12.  
  13. function stopWormHole() {
  14.     if (wormHoleIntervalID > -1)
  15.         clearInterval(wormHoleIntervalID);
  16.  
  17.     clearCanvas();
  18.     document.getElementById("wormHole").onclick = startWormHole;
  19.     document.getElementById("wormHole").innerHTML = "Mode Trou de Ver";
  20. }
  21.  
  22. stopWormHole();

Notre fond d’écran est donc terminé. Nous allons pouvoir nous attaquer au jeu en lui-même.

Mise en place du casse-briques

Pour essayer de ne pas faire un exercice trop long la gestion de la souris est déjà présent dans la solution via le fichier mouse.js. N’hésitez pas à y jeter un coup d’œil.

clip_image005


Rajout du fichier JavaScript pour le jeu

Au niveau de notre projet, nous allons à nouveau faire bouton droit sur le projet et choisir le menu « Ajouter un nouvel élément ». Ce dernier sera de type « Fichier Javascript » et se nommera « game.js ».

Au niveau de la page index.htm, nous allons rajouter l’appel à ce fichier pour en exécuter le contenu. Cet appel sera mis juste avant la balise de fin de body (</body>) et donc après les autres fichiers JavaScript déjà référencés :

  1. <script type="text/javascript" src="game.js"></script>


Modification de la page HTML pour le jeu

Pour dessiner notre jeu, nous allons utiliser SVG (Scalable Vector Graphics, une technologie rajoutée par HTML 5) qui permet de dessiner des objets vectoriels en mode retenu (c’est-à-dire que l’on n’a pas besoin contrairement au canvas de redessiner tout si l’on veut changer quelque chose).

Il faut donc modifier la page HTML pour rajouter ce code après le canvas :

  1. <svg id="svgRoot">
  2.     <circle cx="100" cy="100" r="10" id="ball" />
  3.     <rect id="pad" height="15px" width="150px" x="200" y="200" rx="10" ry="20"/>
  4. </svg>

Notre svg contient déjà deux objets vectoriels : un cercle pour la balle et un rectangle arrondi pour la raquette.

Définition des constantes

Dans notre fichier game.js, nous allons tout d’abord récupérer les éléments de la page qui nous intéresse :


  1. // Recuperation des elements
  2. var pad = document.getElementById("pad");
  3. var ball = document.getElementById("ball");
  4. var svg = document.getElementById("svgRoot");
  5. var message = document.getElementById("message");

Pour le jeu en lui-même, nous allons avoir une balle, une raquette et un groupe de briques.

La balle sera définie par sa position, son rayon, sa vitesse, sa direction ainsi que sa précédente position (que l’on conserve pour revenir à elle lors d’une collision) :

  1. // Balle
  2. var ballRadius = ball.r.baseVal.value;
  3. var ballX;
  4. var ballY;
  5. var previousBallPosition = { x: 0, y: 0 };
  6. var ballDirectionX;
  7. var ballDirectionY;
  8. var ballSpeed = 10;

La raquette est définie par sa largeur, sa hauteur, sa position, sa vitesse et un coefficient d’inertie (pour faire « smooth » Sourire) :

  1. // Raquette
  2. var padWidth = pad.width.baseVal.value;
  3. var padHeight = pad.height.baseVal.value;
  4. var padX;
  5. var padY;
  6. var padSpeed = 0;
  7. var inertia = 0.80;

Les briques seront stockées dans un tableau et seront définies par leur largeur, leur hauteur, la marge entre elles, le nombre de lignes et de colonnes à afficher. De plus nous rajouterons un décalage par rapport au haut de l’écran et une variable pour compter combien de briques sont déjà détruites (pour savoir si le joueur a gagné) :

  1. // Briques
  2. var bricks = [];
  3. var destroyedBricksCount;
  4. var brickWidth = 50;
  5. var brickHeight = 20;
  6. var bricksRows = 5;
  7. var bricksCols = 20;
  8. var bricksMargin = 15;
  9. var bricksTop = 20;

Pour finir nous allons avoir besoin de quelques variables pour définir les limites de la zone de jeu et pour stocker l’heure de démarrage de la partie pour qu’en cas de victoire nous puissions afficher la durée de la partie :

  1. // Divers
  2. var minX = ballRadius;
  3. var minY = ballRadius;
  4. var maxX;
  5. var maxY;
  6. var startDate;

Gestion d’une brique

Nous allons construire une fonction qui va se charger de gérer une brique. Elle devra à sa construction ajouter un nouvel élément au svg (un rectangle). Cet élément sera configuré avec une position définie et une couleur verte plus ou moins intense :

  1. var rect = document.createElementNS("http://www.w3.org/2000/svg", "rect");
  2. svg.appendChild(rect);
  3.  
  4. rect.setAttribute("width", brickWidth);
  5. rect.setAttribute("height", brickHeight);
  6.  
  7. // Couleur verte al?atoire
  8. var chars = "456789abcdef";
  9. var color = "";
  10. for (var i = 0; i < 2; i++) {
  11.     var rnd = Math.floor(chars.length * Math.random());
  12.     color += chars.charAt(rnd);
  13. }
  14. rect.setAttribute("fill", "#00" + color + "00");

De plus, elle fournira une fonction drawAndCollide qui permettra de dessiner la brique et de tester si elle n’est pas en collision avec la balle :

  1. this.drawAndCollide = function () {
  2.     if (isDead)
  3.         return;
  4.     // Dessin
  5.     rect.setAttribute("x", position.x);
  6.     rect.setAttribute("y", position.y);
  7.  
  8.     // Collision
  9.     if (ballX + ballRadius < position.x || ballX - ballRadius > position.x + brickWidth)
  10.         return;
  11.  
  12.     if (ballY + ballRadius < position.y || ballY - ballRadius > position.y + brickHeight)
  13.         return;
  14.  
  15.     // Suppression
  16.     this.remove();
  17.     isDead = true;
  18.     destroyedBricksCount++;
  19.  
  20.     // Changement de direction et retour ? la derni?re position
  21.     ballX = previousBallPosition.x;
  22.     ballY = previousBallPosition.y;
  23.  
  24.     ballDirectionY *= -1.0;
  25. };

On peut voir que l’on utilise la fonction setAttribute des éléments SVG pour définir leurs propriétés.

Une dernière méthode est nécessaire. En effet, il faut pouvoir retirer notre élément du svg pour le faire disparaitre. Au final notre fonction Brick donne donc :

  1. // Fonction de nos briques
  2. function Brick(x, y) {
  3.     var isDead = false;
  4.     var position = { x: x, y: y };
  5.  
  6.     var rect = document.createElementNS("http://www.w3.org/2000/svg", "rect");
  7.     svg.appendChild(rect);
  8.  
  9.     rect.setAttribute("width", brickWidth);
  10.     rect.setAttribute("height", brickHeight);
  11.  
  12.     // Couleur verte al?atoire
  13.     var chars = "456789abcdef";
  14.     var color = "";
  15.     for (var i = 0; i < 2; i++) {
  16.         var rnd = Math.floor(chars.length * Math.random());
  17.         color += chars.charAt(rnd);
  18.     }
  19.     rect.setAttribute("fill", "#00" + color + "00");
  20.  
  21.     this.drawAndCollide = function () {
  22.         if (isDead)
  23.             return;
  24.         // Dessin
  25.         rect.setAttribute("x", position.x);
  26.         rect.setAttribute("y", position.y);
  27.  
  28.         // Collision
  29.         if (ballX + ballRadius < position.x || ballX - ballRadius > position.x + brickWidth)
  30.             return;
  31.  
  32.         if (ballY + ballRadius < position.y || ballY - ballRadius > position.y + brickHeight)
  33.             return;
  34.  
  35.         // Suppression
  36.         this.remove();
  37.         isDead = true;
  38.         destroyedBricksCount++;
  39.  
  40.         // Changement de direction et retour ? la derni?re position
  41.         ballX = previousBallPosition.x;
  42.         ballY = previousBallPosition.y;
  43.  
  44.         ballDirectionY *= -1.0;
  45.     };
  46.  
  47.     // Suppression de la brique
  48.     this.remove = function () {
  49.         if (isDead)
  50.             return;
  51.         svg.removeChild(rect);
  52.     };
  53. }


Collisions avec la raquette et la zone de jeu

Nous allons fournir deux fonctions pour gérer les collisions de la balle (en plus de celle présente dans notre fonction Brick) :

  1. // Collisions
  2. function collideWithWindow() {
  3.     if (ballX < minX) {
  4.         ballX = minX;
  5.         ballDirectionX *= -1.0;
  6.     }
  7.     else if (ballX > maxX) {
  8.         ballX = maxX;
  9.         ballDirectionX *= -1.0;
  10.     }
  11.  
  12.     if (ballY < minY) {
  13.         ballY = minY;
  14.         ballDirectionY *= -1.0;
  15.     }
  16.     else if (ballY > maxY) {
  17.         ballY = maxY;
  18.         ballDirectionY *= -1.0;
  19.         lost();
  20.     }
  21. }
  22.  
  23. function collideWithPad() {
  24.     if (ballX + ballRadius < padX || ballX - ballRadius > padX + padWidth)
  25.         return;
  26.  
  27.     if (ballY + ballRadius < padY)
  28.         return;
  29.  
  30.     ballX = previousBallPosition.x;
  31.     ballY = previousBallPosition.y;
  32.     ballDirectionY *= -1.0;
  33.  
  34.     // Modification de la trajectoire de la balle en fonction de la distance au centre de la raquette
  35.     var dist = ballX - (padX + padWidth / 2);
  36.  
  37.     ballDirectionX = 2.0 * dist / padWidth;
  38.  
  39.     var square = Math.sqrt(ballDirectionX * ballDirectionX + ballDirectionY * ballDirectionY);
  40.     ballDirectionX /= square;
  41.     ballDirectionY /= square;
  42. }

La fonction collideWithWindow se charge de gérer les bords de la zone de jeu et appelle une fonction lost (que l’on définira plus bas) lorsque la partie basse est touchée par la balle.

La fonction collideWithPad quant à elle se charge de la collision avec la raquette avec la subtilité que l’on change la direction horizontale de la balle selon la distance de la zone de contact avec le centre de la raquette (en gros si on touche à gauche on renvoie la balle vers la gauche et inversement à droite).

Déplacement de la raquette

La raquette peut être contrôlée avec le clavier (flèche gauche et droite du curseur) ou avec la souris (si le bouton gauche est appuyé). C’est la méthode movePad qui est en charge du déplacement de la raquette en fonction de sa vitesse et de l’inertie. Ainsi l’utilisateur ne va pas directement contrôlé la position de la raquette mais sa vitesse (pour pimenter le tout) :


  1. // Deplacement de la raquette
  2. function movePad() {
  3.     padX += padSpeed;
  4.  
  5.     padSpeed *= inertia;
  6.  
  7.     if (padX < minX)
  8.         padX = minX;
  9.  
  10.     if (padX + padWidth > maxX)
  11.         padX = maxX - padWidth;
  12. }

Pour contrôler la variable padSpeed nous allons donc nous brancher sur la souris (grâce à notre fichier mouse.js) et sur le clavier :

  1. registerMouseMove(document.getElementById("gameZone"), function (posx, posy, previousX, previousY) {
  2.     padSpeed += (posx - previousX) * 0.2;
  3. });
  4.  
  5. window.addEventListener('keydown', function (evt) {
  6.     switch (evt.keyCode) {
  7.         // Fleche gauche
  8.         case 37:
  9.             padSpeed -= 10;
  10.             break;
  11.         // Fleche droite    
  12.         case 39:
  13.             padSpeed += 10;
  14.             break;
  15.     }
  16. }, true);


Boucle de jeu

Nous allons maintenant ajouter une fonction qui va gérer les constantes de taille de la fenêtre de jeu et qui sera appelée par la boucle de jeu :

  1. function checkWindow() {
  2.     maxX = window.innerWidth - minX;
  3.     maxY = window.innerHeight - 130 - 40 - minY;
  4.     padY = maxY - 30;
  5. }

La boucle de jeu est donc en charge d’appeler checkWindow ainsi que movePad. Elle bougera également la balle, appellera la vérification des collisions, affichera les briques et mettra à jour la balle et la raquette :

  1. function gameLoop() {
  2.     checkWindow();
  3.  
  4.     movePad();
  5.  
  6.     // D?placement
  7.     previousBallPosition.x = ballX;
  8.     previousBallPosition.y = ballY;
  9.     ballX += ballDirectionX * ballSpeed;
  10.     ballY += ballDirectionY * ballSpeed;
  11.  
  12.     // Collisions
  13.     collideWithWindow();
  14.     collideWithPad();
  15.  
  16.     // Briques
  17.     for (var index = 0; index < bricks.length; index++) {
  18.         bricks[index].drawAndCollide();
  19.     }
  20.  
  21.     // Balle
  22.     ball.setAttribute("cx", ballX);
  23.     ball.setAttribute("cy", ballY);
  24.  
  25.     // Raquette
  26.     pad.setAttribute("x", padX);
  27.     pad.setAttribute("y", padY);
  28.     
  29.     // Victoire ?
  30.     if (destroyedBricksCount == bricks.length) {
  31.         win();
  32.     }
  33. }

Initialisation et gestion de la victoire

La phase d’initialisation va d’abord devoir créer la liste des briques :

  1. function generateBricks() {
  2.     // Suppression des pr?c?dentes
  3.     for (var index = 0; index < bricks.length; index++) {
  4.         bricks[index].remove();
  5.     }
  6.  
  7.     // Cr?ation des briques
  8.     var brickID = 0;
  9.  
  10.     var offset = (window.innerWidth - bricksCols * (brickWidth + bricksMargin)) / 2.0;
  11.  
  12.     for (var x = 0; x < bricksCols; x++) {
  13.         for (var y = 0; y < bricksRows; y++) {
  14.             bricks[brickID++] = new Brick(offset + x * (brickWidth + bricksMargin), y * (brickHeight + bricksMargin) + bricksTop);
  15.         }
  16.     }
  17. }

Puis nous allons fournir une fonction initGame qui se charge de régler l’affichage et de mettre toutes les variables à leur valeur de base (en plus de générer les briques):

  1. function initGame() {
  2.     message.style.visibility = "hidden";
  3.  
  4.     checkWindow();
  5.     
  6.     padX = (window.innerWidth - padWidth) / 2.0;
  7.  
  8.     ballX = window.innerWidth / 2.0;
  9.     ballY = maxY - 60;
  10.  
  11.     previousBallPosition.x = ballX;
  12.     previousBallPosition.y = ballY;
  13.     
  14.     padSpeed = 0;
  15.  
  16.     ballDirectionX = Math.random();
  17.     ballDirectionY = -1.0;
  18.  
  19.     generateBricks();
  20.     gameLoop();
  21. }

Nous allons notamment brancher cette fonction sur l’évènement onresize de la fenêtre :

  1. window.onresize = initGame;

Puis nous allons brancher une fonction sur le onclick du bouton « Nouvelle Partie » qui va exécuter l’initialisation du jeu et lancer un timer sur la boucle de jeu :

  1. var gameIntervalID = -1;
  2. function startGame() {
  3.     initGame();
  4.  
  5.     destroyedBricksCount = 0;
  6.  
  7.     if (gameIntervalID > -1)
  8.         clearInterval(gameIntervalID);
  9.  
  10.     startDate = (new Date()).getTime(); ;
  11.     gameIntervalID = setInterval(gameLoop, 16);
  12. }
  13.  
  14. document.getElementById("newGame").onclick = startGame;

Pour finir nous allons ajouter deux fonctions lost et win pour gérer la fin de partie:

  1. function lost() {
  2.     clearInterval(gameIntervalID);
  3.     gameIntervalID = -1;
  4.     
  5.     message.innerHTML = "Game over !";
  6.     message.style.visibility = "visible";
  7. }
  8.  
  9. function win() {
  10.     clearInterval(gameIntervalID);
  11.     gameIntervalID = -1;
  12.  
  13.     var end = (new Date).getTime();
  14.  
  15.     message.innerHTML = "Victoire ! (en " + Math.round((end - startDate) / 1000) + "s)";
  16.     message.style.visibility = "visible";
  17. }

Ces deux fonctions font apparaitre le bandeau de message en changeant le texte selon le contexte. Elles arrêtent aussi le timer du jeu.

La dernière chose à faire pour que notre jeu soit complet est juste de lancer une première fois initGame.


Conclusion

Nous avons donc développé un jeu fonctionnel avec en plus des effets spéciaux Sourire

Grâce à des navigateurs comme Internet Explorer 9 qui peuvent utiliser l’accélération matérielle pour le SVG et le canvas, nous obtenons une grande fluidité malgré parfois beaucoup d’animations et de rendus à l’écran.

clip_image007

A vous de jouer maintenant pour enrichir ce petit jeu pour en faire le blockbuster de demain !

Ressources complémentaires

Leave a Comment
  • Please add 6 and 7 and type the answer here:
  • Post
Page 1 of 1 (1 items)