Appuyez sur Entrée pour voir vos résultats ou Echap pour annuler.

Tutorial : Gestion des scenes d’un jeu, des transitions et de la mise à l’echelle avec Pixi.js

La plupart des tutorials sur le développement de jeux en HTML5 expliquent surtout comment développer la mécanique du jeu, dessiner des choses à l’écran …. or une grande partie du travail quand on développe un jeu réside dans la gestion des scènes, des menus, des transitions …etc

dans ce tutorial, je vais vous expliquer comment gérer les scènes d’un jeu, mettre une scène en pause et la reprendre, ainsi que la mise à l’échelle de l’écran pour que votre jeu soit compatible avec différentes résolution ; très important si vous ciblez des plateformes mobiles

//Le but de ce tutorial est d’obtenir ce résultat //

Introduction

Je vais utiliser l’excellent Pixi.js : un moteur de rendu HTML5 très puissant , il permet entre autres de détécter automatiquement le support WebGL et l’utilise pour augmenter les performances du rendu, à défaut il utilisera un rendu Canvas 2D classique.

Comme langage de programmation je vais utiliser TypeScript , et Visual Studio 2012 for Web comme IDE.

Pour exploiter pleinement la puissance de VisualStudio et TypeScript avec Pixi je vous recommande d’utiliser le fichier de définitions suivant : pixi.d.ts

Voici comment j’ai organisé mon code

code-tree

à noter que ceci représente la structure finale

Le dossier engine contient les classes communes

Le dossier game contient les classes spécifiques

le dossier Lib contient les librairies tierses : ici on a placé Pixi.js avec son fichier de définition pixi.d.js

 

 

 

 

 

 

 

Les scenes

La classe Scene

nous allons commencer par créer une classe Scene.

un objet Scene est en fait un objet PIXI.Stage qu’on va étendre pour rajouter la possibilité de mettre en pause, de le reprendre et de le mettre à jour

Notez comment nous avons étendu l’objet PIXI.Stage en utilisant une syntax TypeScript même si la librairie Pixi n’a pas été développée à la base en TypeScript

la méthode onUpdate() va permettre d’enregistrer un callback qui nous permettera de mettre à jour l’état des objets du jeu. (nous allons voir plus tard que nous pouvons utiliser une autre méthode plus propre qui consiste à étendre la méthode Update dans une classe fille).

 

La classe ScenesManager

Pour gérer les scènes nous allons créer la classe ScenesManager

Un jeu n’est sensé disposer d’un seul ScenesManager, c’est lui qui va gérer l’ensemble des scènes et des transition, c’est pour celà que nous déclarons toutes ses méthodes comme statiques.
La méthode create() va initialiser un objet Renderer de Pixi et démarrer une boucle infinie immédiatement (gameloop).

La méthode loop() va vérifier qu’une scène courante existe et qu’elle n’est pas en pause,si c’est le cas, elle va mettre à jour son état via update() puis la dessiner via render().

 

Ensuite nous ajoutons deux méthodes au ScenesManager qui nous permettent de créer une scène et de passer d’une scène à une autre.

ce code remplace la ligne  « //# next code block » dans le code précédent

Les noms sont explicites je pense et ne nécessite pas plus de précisions que ça 😉

notez que la méthode goToScene() va mettre en pause  la scène courant avant de basculer vers une autre scène, ainsi nous préservons la cohérence de l’état de la précédente scène.

 

Testons notre code 🙂

pour cela on va créer un fichier HTML avec des références vers Pixi.js, ScenesManager.class.js and Scene.class.js

puis nous ajoutons ce script </body>

et voici le résultat:

 

Basculer d’une Scène à l’autre

 

vous avez noté que nous avons ajouté la logique de notre jeu (le petit lapin en rotation) dans le callback d’une instance Scène, dans la vrai vie, quand on a des centaines (milliers?) de lignes de code cette approche risque de rendre le code illisible  (surtout qu’il faudra par la suite ajouter aussi du code pour gérer les menus et les interactions).

pour faire les choses plus proprement nous allons créer des classes filles de la classe Scene qui contiendront le code spécifique à chaque scène. nous allons aussi modifier notre ScenesManager pour lui permettre d’instancier des Scènes de différents types.

Dans ScenesManager la méthode createScene devient :

ici nous avons remplacer la création d’un objet Scene « new Scene() » par une une instanciation générique  « new TScene() »

TScene est déclaré ainsi  : « TScene: new () => Scene = Scene », cette syntaxe un peu bizarre indique simplement à TypeScript d’accépter comme argument des Types héritant la classe Scene.

on peut obtenir le même résultat avec cette syntaxe plus simple :
public static createScene(id: string, TScene:any): Scene {…}
mais avec cette syntaxe nous perdons la vérification des types d’objets qu’effectue TypeScript à la compilation..

Maintenant nous allons créer une classe GameScene qui hérite de Scene et implémente le code du jeu.

et notre script dans le fichier HTML de test devient

comme vous voyez, nous obtenons le même résultat visuel que précédemment sauf que cette fois ci, toute la logique du jeu est cachée dans une classe spécifique

 

 

Intro, Menu et transitions

Nous allons maintenant créer deux nouvelles Scenes pour afficher un logo avec un effet de fadeIn (vous pouvez utiliser cette scène pour précharger les ressources par exemple) puis un bouton cliquable permettant de lancer le jeu.

Le jeu va aussi contenir un bouton cliquable permettant de revenir au menu.

Scene d’intro

 

Le menu

La plupart du code que j’utilise ci-dessous n’est qu’une reprise de cet exemple de code pour pixi , j’ai juste ajouté un contrôle permettant de ne pas prendre en compte les événements (clique …etc) quand le menu n’est pas la scène courante (donc invisible)

en effet, au moment ou j’écrivais ce tutorial, il semble que Pixi traite lees événements rattachés à des objets invisibles, j’ai ajouté ce petit controle if (_this.isPaused()) return; au début de chaque fonction traitant un événement.
il y a un correctif en cours sur le repository github de pixi une fois implémenté il ne sera plus nécessaire d’ajouter ce test .

rien d’extraordinaire 🙂 assurez vous juste que vous aves bien fait appel à setInteractive(true) pour le bouton et la scène qui le contient sinon les événements ne seront pas traités.

 

Accès au menu depuis le jeu

nous allons modifier GameScene pour ajouter un bouton qui permet d’aller au menu.

on ajoute ces ligne à la fin du constructeur de GameScene

 

et pour finir nous créons les nouvelles scènes dans notre script de test

 

Cliquez ici pour voir le résultat

 

 

Mise à l’échelle de l’affichage

maintenant que nous avons un système de transition opérationnel ainsi que des éléments interactifs pour aller d’une scène à l’autre ; nous allons voir comment redimensionner l’affichage de notre jeu afin qu’il exploite toujours au maximum la taille de l’écran disponible tout en préservant le bon rapport hauteur/largeur.

 

nous allons besoins de stocker la largeur et la hauteur par défaut ainsi que la largeur et hauteur actuelle de l’affichage.

nous calculons ensuite le rapport de mise à l’échelle afin de l’appliquer à tous les objets du jeu.

nous aurons besoin de ces variables

… ainsi qu’une méthode qui calcul le ratio et redimensionne  l’affichage de Pixi en fonction

nous modifions également la méthode ScenesManager.create() pour pouvoir demander ou non la mise à l’échelle automatique (via le paramètre scale).

Quand j’écrivais ce tutorial, je pensais que la mise à l’échelle d’un objet Pixi Stage (parent de nos classes Scene) était suffisante pour mettre à l’échelle tous ses objets enfants : il aurait donc simplement fallu de multiplier scenes.scale.x/y par le  ratio afin que tout soit à la bonne taille …. J’avais tort 🙂

en fait, cette solution ne semble pas fonctionner, et je ne saurais dire si c’est un comportement normal de Pixi ou un bug … peu importe, j’ai mis en place un contournement.

Voyons comment nous allons gérer la mise à l’échelle.

Nous allons tout d’abord ajouter une methode qui applique un ratio à un objet Pixi DisplayObject ainsi qu’a tous ses enfants (DisplayObject est le parent de tous les objets PIXI)

ensuite dans la méthode ScenesManager.loop() nous appliquons le ratio à tous les objets de la scène courante avant de la dessiner,  nous dessinons la scène puis juste après nous récupérons le ratio par défaut. Ainsi tout reste transparent pour le développeur quand il s’agira de manipuler des positions ou des dimensions des objets du jeu.

 

 

tout est prêt maintenant, nous avons juste initialiser scenesManager avec le paramètre « scale »  à true, pour lui demander d’appliquer la mise à l’échelle automatiquement.

 

Cliquez ici pour voir le résultat ,

redimensionnez la fenêtre et voyez comment l’affichage s’adapte 🙂

 

 

Télécharger le code source complet

 

J’espère que ce tutorial vous sera utile dans le développement de vos jeux mobiles en HTML5 🙂 …

Vos remarques est suggestions sont les bienvenues.