Game Loop et requestAnimationFrame

Une fonctionnalité qui unit tous les moteurs de jeu, est la Game Loop : une fonction qui gère le cycle du jeu ; la physique (la mise à jour de la position de nos éléments de jeu, le contrôle des collisions), et le Draw, des objets qui font partie du jeu (les GameObject).


requestAnimationFrame, gérer l’actualisation de la scène

En HTML5, la fonction requestAnimationFrame a été ajoutée pour remplacer l’ancienne méthode d’animation, qui utilisait un timer (setTimeout) pour afficher une image toutes les « quelques » millisecondes.

Avec requestAnimationFrame, nous pouvons indiquer au moteur JavaScript la fonction à lancer la prochaine fois qu’un frame est déclenchée. Voici la syntaxe :

 
 
  1. <em>(request ID)</em> = requestAnimationFrame(<em>callback</em>)

 

Grace à cette fonction native, le navigateur peut optimiser le rendering nativement en gardant le framerate plus stable et fluide (avec un maximum de 60fps), ce qui réduit considérablement les « coups » ennuyeux qui ont été notées en utilisant le Timer (setTimeout, setInterval).

Dans le cas où un autre onglet du navigateur est ouvert, le rendering serait temporairement désactivé, contrairement à setTimeout, qui continuerait à fonctionner en utilisant la CPU, GPU et de la mémoir e: l’utilisation accrue de la performance du hardware, qui dans les appareils portables et mobiles, se résume à un gaspillage de batterie.


Un polyfill pour requestAnimationFrame

Vu que l’HTML5 n’était pas encore la norme en 2014, requestAnimationFrame a été mis en place temporairement par un préfixe dans l’identifiant (webkit, moz, ou, ms), qui fait référence aux différents noms de navigateur : nous définissons une fonction générique, un polyfill, qui renvoie l’identifiant de la fonction correcte.

Pour éviter d’avoir à appeler chaque fois toutes les possibles fonctions créons ce que l’on nomme un Polyfill, ou une fonction qui rappelle la fonction correcte pour chaque client.

Créons un nouveau fichier JavaScript (utils.js), dans lequel on va insérer toutes les fonctions pratiques que nous utiliserons dans notre framework. Commençons par ce polyfill :

 
 
  1. window.requestAnimFrame = (function(callback) {
  2. return window.requestAnimationFrame ||
  3. window.webkitRequestAnimationFrame ||
  4. window.mozRequestAnimationFrame ||
  5. window.oRequestAnimationFrame ||
  6. window.msRequestAnimationFrame ||
  7. function(callback) {
  8. window.setTimeout(callback, 1000 / 60);
  9. };
  10. })();

 

Bien que la probabilité soit éloignée, nous pouvons avoir un navigateur qui ne supporte pas requestAnimationFrame, auquel cas notre fonction utilisera l’ancienne méthode setTimeout.

Pendant que nous y sommes, déplaçons l’event listener load de main.js vers utils.js

 
 
  1. window.addEventListener('load', function() {
  2. StartGame();
  3. },

 

Compatibilités

Feature Chrome Firefox (Gecko) Internet Explorer Opera Safari (WebKit)
Basic support 10.0 webkit
24.0
4.0 moz [1]
23 [2]
10.0 (Oui) -o
15.0
6.0 webkit
6.1
requestID return value 23.0 webkit
24.0
11.0 (11.0) moz 10.0 (Oui) -o
15.0
(Oui)

GameLoop

Nous pouvons enfin définir le GameLoop, c’est-à-dire la fonction qui nous permet de créer une boucle infinie dans laquelle on pourra gérer les animations et les événements frame après frame. Définissons la fonction GameLoop, à l’intérieur de l’objet Game :

 
 
  1. function Game() {
  2. // ... choses définies dans la leçon précédente
  3. this.GameLoop = function() {
  4. if(!this.paused) {
  5. // mettre à jour tous les objets
  6. this.Update();
  7. }
  8. // dessine toute la scène sur l'écran
  9. this.Draw();
  10. window.requestAnimFrame(function() {
  11. // relance la fonction GameLoop sur chaque image
  12. game.GameLoop();
  13. });
  14. }
  15. }

 

Le cœur de la boucle est requestAnimFrame, dans lequel nous appelons à nouveau la fonction GameLoop de l’instance Game, de sorte qu’un mécanisme récursif est déclenché, dans lequel Update () et Draw () sont exécutés à chaque frame.

La plupart des frameworks de jeux vidéo prévoient que les objets du jeu (GameObject, que nous allons voir) ont des fonctions Update et Draw, ce qui en fait deux éléments fondamentaux du moteur et même s’ils ne sont pas encore définis, nous nous engageons à les définir rapidement.

On peut également définir la variable paused, qui bloquera la fonction Update lorsqu’elle est définie sur true.

Remarque : Draw () reste toujours actif, de manière à afficher quand même les images. Dans les chapitres suivants, nous verrons comment optimiser les performances pendant la pause en utilisant le canevas OffScreen.

Comments
Chargement ...
"