Accélérer un jeu vidéo avec plusieurs canvas




 

Lorsque vous utilisez une seul et unique canvas pour l’affichage d’un jeu vidéo, vous rafraichissez la totalité du canvas en effaçant et réaffichant chacun des éléments à chaque itération d’affichage.

En html5, plus les images sont grandes, plus elles sont couteuses à afficher. Vous devez donc éviter au maximum l’affichage itératif de grandes images, sans quoi la vitesse du jeu s’en ressentira.

Si vous développez un jeu vidéo comportant un arrière plan statique affichant une image de sa taille, il semble de bon augure de ne pas réafficher cet arrière plan à chaque itération d’affichage.

L’optimisation consiste donc à empiler deux canvas empilés l’un sur l’autre : l’un dédié au premier plan à fond transparent, l’autre dédié à l’arrière plan comportant l’image statique. Puis à ne rafraichir que le premier plan.

Ne pas retracer l’arrière plan fait gagner un temps machine précieux.

 

Prérequis

– Savoir ce qu’est html et connaître la balise canvas;
– savoir ce qu’est un style et savoir l’utiliser;
– avoir des connaissances de base en javascript;
– avoir lu l’article Initialiser le développement d’un jeu vidéo

 

L’exemple

Dans cet exemple, vous allez afficher une image de fond statique qui n’a pas besoin d’être rafraichie, et une image animée au premier plan correspondant à un sprite.

Pour le projet, reprenez l’arborescence suivante :

Arborescence Projet
Reprenez le code de base suivant (les explications dans l’article Initialiser le développement d’un jeu vidéo) :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<script>
// début du code isolé
(function () {
 var requestAnimId;
 
 var initialisation = function() {
   // le code de l'initialisation 
   requestAnimId = window.requestAnimationFrame(principale);
 }
 
 var principale = function() {
   // le code du jeu
   requestAnimId = window.requestAnimationFrame(principale);
 }
 
 window.onload = initialisation; // appel de la fonction initialisation au chargement de la page
})();
// fin du code isolé
</script>

var initialisation = function() {
// le code de l’initialisation
requestAnimId = window.requestAnimationFrame(principale);
}

var principale = function() {
// le code du jeu
requestAnimId = window.requestAnimationFrame(principale);
}

window.onload = initialisation; // appel de la fonction initialisation au chargement de la page
})();
// fin du code isolé
</script>

 

Initialisation

Vous allez dans un premier temps créer deux canvas empilés.

C’est la propriété z-index du style qui permet de maitriser l’empilement : le canvas ayant le z-index le plus faible étant le canvas le plus en arrière plan.

Par le biais de la propriété id, nommez :
– background le canvas ayant le z-index à 0;
– foreground le canvas ayant le z-index à 1.

Le code source des deux canvas :

1
2
3
4
<canvas id="background" width="1000" height="600" style="position: absolute; z-index: 0">
</canvas>
<canvas id="foreground" width="1000" height="600" style="position: absolute; z-index: 1">
</canvas>

 

La fonction d’initialisation

Vous allez créer deux variables relatives aux contextes des deux canvas. Puis dans le canvas du fond d’écran (background), vous affichez l’image de fond. Enfin, dans le canvas de premier plan (foreground), vous affichez l’image d’un sprite.

La fonction initialisation prend la forme suivante :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
var canvasContextBg;
var imgBg;
var canvasContextFg;
var imgFg;
 
var initialisation = function() {
  canvasContextBg = document.querySelector('#background').getContext('2d');
  canvasContextFg = document.querySelector('#foreground').getContext('2d');
 
  imgBg = new Image();
  imgBg.src = 'http://www.developper-jeux-video.com/jsperf/background.jpg';
  canvasContextBg.drawImage(imgBg,0,0);
 
  imgFg = new Image();
  imgFg.src = 'http://www.developper-jeux-video.com/jsperf/module.png';
  canvasContextFg.drawImage(imgFg,100,100);
 
  requestAnimId = window.requestAnimationFrame(principal);
};

var initialisation = function() {
canvasContextBg = document.querySelector(‘#background’).getContext(‘2d’);
canvasContextFg = document.querySelector(‘#foreground’).getContext(‘2d’);

imgBg = new Image();
imgBg.src = ‘http://www.developper-jeux-video.com/jsperf/background.jpg’;
canvasContextBg.drawImage(imgBg,0,0);

imgFg = new Image();
imgFg.src = ‘http://www.developper-jeux-video.com/jsperf/module.png’;
canvasContextFg.drawImage(imgFg,100,100);

requestAnimId = window.requestAnimationFrame(principal);
};

4 variables ont été initialisées : canvasContextBg, imgBg, canvasContextFg, imgFg.

 

Implémentation de la boucle d’affichage (principale)

La fonction principale appelée en boucle efface le premier plan puis réaffiche l’image du sprite. On ne se préoccupe pas de l’arrière plan qui est statique : il est affiché à l’initialisation, ce qui est suffisant. Seuls sont rafraichis les canvas comportant des objets animés :

1
2
3
4
5
6
7
var posX = 10;
var posY = 10;
var principal = function() 
  canvasContextFg.clearRect(0,0,1000,600);
  canvasContextFg.drawImage(imgFg,++posX,posY);
  requestAnimId = window.requestAnimationFrame(principal);
};


Pour voir la démo, cliquez ici.

 

La preuve par les benchmarks

J’ai réalisé par le biais de jsperf un test comparatif entre l’affichage avec un seul canvas et l’affichage avec deux canvas. Les résultats sont éloquents, constatez par vous-même :

Quelque soit le navigateur, la version optimisée est toujours la plus rapide. Le différentiel relatif le plus important est sous Firefox. Cependant, chrome est le navigateur aussi plus rapide dans le mode simple layer par rapport au mode double layer des autres navigateurs.

 

Conclusion

Ce qui a été vu ici n’est pas l’unique optimisation, il en existe d’autres que j’aborderai prochainement. Un petit bémol à cette optimisation : n’imaginez pas avoir le même différentiel sur un jeu fini, l’affichage n’étant pas l’unique composante d’un jeu.

[important]Faites un retour de vos expériences ou encore des propositions d’optimisation qu’il serait intéressant de tester sur ce blog.[/important]

 

Posté dans html5Taggé layer html5, optimisation canvas, optimisation html5  |  2 commentaires

2 réponses à "Accélérer un jeu vidéo avec plusieurs canvas"

Répondre

*