Phaser 2.0 Tutoriel : Flappy Bird (Partie 2)

Publié le .

Phaser 2 Tutoriel
Phaser 2.0 Tutoriel : Flappy Bird (Partie 2)

Une introduction à la Physique et aux Prefabs

Dans la première partie de ce tutoriel, nous avons vu comment jeter les bases d’un jeu et fait un menu en utilisant le préchargements d’assets, des sprites et un groupe. Dans cette partie du tutoriel, nous allons voir comment implémenter un système physique. Nous verrons également une introduction au Prefabs.

Pour information, cette partie du tutoriel est l’adaptation de celui de Jeremy Dowell que vous pouvez trouver à cette page.

Qu’est-ce qu’un Prefab ?

Prefab signifie littérallement « Préfabriqué ».

Dans notre cas, un prefab est un objet du jeu qui a étét préparé et qui peut être instancier plusieurs fois avec les même propriétés de base à chaque fois. Techniquement parlant, c’est simplement un terme dans le développement de jeu pour les classes/prototypes qui font référence à un objet du jeu avec des visuels et des comportement prédéfinis.

Ouvrez dans votre éditeur préféré le fichier game/states/play.js et retirer tout le code des fontions create() et update() pour avoir :

  1.   'use strict';
  2.   function Play() {}
  3.   Play.prototype = {
  4.     create: function() {
  5.     },
  6.     update: function() {
  7.        
  8.     }
  9.   };
  10.  
  11.   module.exports = Play;

Télécharger

Editons également game/states/preload.js de tel sorte que nous serons directement mener à l’état play. Du coup nous n’aurons pas à passer par le menu à chaque fois que nous voudrons voir notre progression. Actuellement, la fonction update() de game/states/preload.js ressemble à cela :

  1. update: function() {
  2.         if (!!this.ready) {
  3.             this.game.state.start('menu');
  4.         }
  5.     },

Télécharger

Changeons-la pour qu’elle ressemble à :

  1.     update: function() {
  2.         if (!!this.ready) {
  3.             this.game.state.start('play');
  4.         }
  5.     },

Télécharger

Bien ! Faisons maintenant la chose la plus importante maintenant nécessaire à notre jeu !

Activer la physique

Nous allons donc utiliser le moteur de physique de Phaser. Bonne nouvelle, il est simple.

Retournons à game/states/play.js et au début de la fonction create(), ajoutons la ligne suivante :

  1. create: function() {  
  2.     this.game.physics.startSystem(Phaser.Physics.ARCADE);
  3. }

Télécharger

Cette ligne dit à Phaser que nous allons utiliser le système de physique arcade. Quand Phaser atteint cette ligne, il fait plein de choses magiques ! La dernière n’étant pas les calculs pour simuler les effets de la physique à chaque frame.

Régler la gravité

Régler la gravité pour tout le jeu est ridiculement facile ! L’exemple suivant montre comment faire cela :

  1. create: function() {  
  2.     this.game.physics.startSystem(Phaser.Physics.ARCADE);
  3.     this.game.physics.arcade.gravity.y = 500;
  4. }

Télécharger

Maintenant, chaque sprite du jeu avec un corps physique va accélérer vers le sol avec une vitesse de maximum de 500 pixels par seconde.

Nous reviendrons à ce code plus tard. En effet, nous devrons prendre en considération des circonstances spéciales. Nous en attendant, allons de l’avant !

Ajoutons le fond

Nous le faisons exactement comme dans la première partie de ce tutoriel.

  1. create: function() {  
  2.     /* physics and gravity code gere */
  3.  
  4.     // add the background sprite
  5.     this.background = this.game.add.sprite(0,0,'background');
  6. }

Télécharger

Dans notre navigateur, nous revoyons avec joie notre fond ! Remarquez qu’il ne tombe pas... Et ce même si nous avons activé la physique dans le jeu. En effet, nous n’avons pas activé la physique sur celui-ci...

Générer un Prefab

Allons-y ! Amusons-nous !!!

Nous allons créer un prefab bird qui contiendra tout le code pour le comportement de notre oiseau et sa manière de voler. Dans un nouveau terminal, pas celui dans lequel tourne grunt si il est déjà en route, lançons la commande :

$ yo phaser-official:prefab "bird"

Il vous faudra répondre à deux questions comme ceci :

  1. Creating the new prefab: bird  
  2. [?] What is the name of your prefab? bird
  3. [?] What sprite key would you like to use? bird
  4.    create game/prefabs/bird.js

Télécharger

« bird » sera le nom de notre prefab, et va utiliser le sprite ayant comme identifiant ou clé ( key ) « bird » que nous avons déjà chargé.

Si grunt n’est pas déjà en route, lancez-le.

Noous devrion maintenant avoir un nouveau fichier : game/prefabs/bird.js. Ouvrons-le. Nous devons y trouver ce code :

  1. 'use strict';
  2.  
  3. var Bird = function(game, x, y, frame) {
  4.   Phaser.Sprite.call(this, game, x, y, 'bird', frame);
  5.  
  6.   // initialize your prefab here
  7.  
  8. };
  9.  
  10. Bird.prototype = Object.create(Phaser.Sprite.prototype);
  11. Bird.prototype.constructor = Bird;
  12.  
  13. Bird.prototype.update = function() {
  14.  
  15.   // write your prefab's specific update code here
  16.  
  17. };
  18.  
  19. module.exports = Bird;

Télécharger

Le générateur a automatiquement créé le fichier de cette class. Elle hérite de Phaser.Sprite et sera incluse dans notre fichier dist/js/game.js lors de la construction effectuée automatiquement à la sauvegarde.

Personnaliser un Prefab

Nous allons écrire un bout de code qui devrait vous rappeler quelque chose de la partie 1

  1. var Bird = function(game, x, y, frame) {  
  2.   // The super call to Phaser.Sprite
  3.   Phaser.Sprite.call(this, game, x, y, 'bird', frame);
  4.  
  5.   // set the sprite's anchor to the center
  6.   this.anchor.setTo(0.5, 0.5);
  7.  
  8.   // add and play animations
  9.   this.animations.add('flap');
  10.   this.animations.play('flap', 12, true);
  11. }

Télécharger

Dans la partie 1, nous avions mis en place l’animation de notre oiseau ainsi :

  1. this.bird = this.add.sprite(200,5,'bird');  
  2. this.bird.animations.add('flap');
  3. this.bird.animations.play('flap', 12, true);

Télécharger

Cependant, notre prefab est une class, et le mot this référence l’instance actuelle. Donc, toute opération ou propriété que nous référencions avec this.bird dans le menu est maintenant réalisé avec this dans notre nouveau Sprite Prefab.

Ajouter un corps physique à un sprite

Maintenant vient le temps de mettre en place la seconde chose plus importante pour pouvoir bénéficier de la physique dans un jeu basé sur la physique... Nous allons ajouter un corps physique ( physics body ) à notre prefab « bird » :

  1. var Bird = function(game, x, y, frame) {  
  2.     /* super and animations setup here */
  3.     this.game.physics.arcade.enableBody(this);
  4. }

Télécharger

Ca y est, c’est tout ce qu’il faut pour que notre jeu reconnaisse ce sprite comme étant sujet à la physique.

Maintenant, mettons cela à l’écran dans game/states/play.js

Appeler des Prefabs et autres modules

Parce que nous utilisons browersify pour rendre possible l’utilisation de pattern dans le module node, à partir du moment où nous créons un prefab ou un nouveau fichier de class que nous voulons utiliser dans une autre partie du jeu, nous avons besoin de faire une requête globalement accessible, c’est à dire que l’on peut y accéder depuis n’importe où dans notre jeu. Pour cela, nous utilisons la méthode de browersify require().

Un appelle requérant ressemble à cela :

  1. var ClassName = require('relative/path/to/file');

Cela trouvera un fhier selon le chemin idiqué et importera la valeur de son module.exports et assignera cette valeur à ClassName.

Dans notre cas, il faut, dans game/states/play.js, juste sous 'use strict'; ajouter ce qui suit :

  1. var Bird = require('../prefabs/bird');

Ce que nous faisons ici est de remonter d’un cran ../ et d’avancer ensuite dans le répertoire prefabs/ et le module qui est défini dans bird.js.

Maintenant, la class bird est disponible partout dans game/states/play.js

Instancier un Prefab dans un état

Maintenant que nous avons requis notre class prefab, nous pouvons normalement l’instancier dans notre fonction create() de notre fichier game/states/play.js comme cela :

  1. create: function() {  
  2.     /* physics and background instantiation here */
  3.  
  4.     // Create a new bird object
  5.     this.bird = new Bird(this.game, 100, this.game.height/2);
  6.     // and add it to the game
  7.     this.game.add.existing(this.bird);
  8. }

Télécharger

Maintenant la partie la plus important du jeu marche : La Physique !

Mais, nous avons besoin de quelque chose pour que notre oiseau ne tombe pas hors de son monde !!!!

La véritable puissance des Prefab

Dans notre jeu, nous aurons seulment un oiseau à la fois. Alors, utiliser les un prefab pour le créer peux sembler exagéré. Mais juste pour voir expérimentons pour découvrir en quoi les prefabs peuvent nous aider...

Disons que nous voulos créer 10 oiseaux aléatoire sur notre écran de jeu.

Sans les prefabs, notre code ressemblerais à cela :

  1. create: function() {  
  2.     this.game.physics.startSystem(Phaser.Physics.Arcade);
  3.  
  4.     var birdGroup = this.game.add.group();
  5.     for(var i = 0; i < 10; i++) {
  6.         var bird = new Bird(this.game, this.game.world.randomX,this.game.world.randomY);
  7.         bird.anchor.setTo(0.5, 0.5);
  8.         bird.animations.add('flap');
  9.         bird.animations.play('flap', 12, true);
  10.         this.game.physics.arcade.enableBody(bird);
  11.         this.game.add.existing(bird);
  12.         birdGroup.add(bird);
  13.     }

Télécharger

Par contre avec un prefab :

  1. create: function() {  
  2.     this.game.physics.startSystem(Phaser.Physics.Arcade);
  3.  
  4.     var birdGroup = this.game.add.group();
  5.       for (var i = 0; i < 10; i++) {
  6.         var bird = new Bird(this.game, this.game.world.randomX, this.game.world.randomY);
  7.         birdGroup.add(bird);
  8.       }
  9. }

Télécharger

Que ce soit avec l’un ou l’autre, vous obtiendrez :

Avec les deux méthodes, chacun des oiseaux du groupe a les mêmes propriétés et physique. Cependant, le second exemple est plus propre, et ajouter une méhode à notre class bird est véritablement la seule façon de pouvoir contrôler tous nos oiseaux...

Bon, ben voilà... Retournons à notre jeu !

Créer un TileSprite Prefab

Nous allons ajouter un sol avec des TileSprite ( comme dans la partie 1 ), mais cette fois, nous le ferons avec un prefab...

Retournons à notre terminal et lançons la commande suivante :

$ yo phaser-official:prefab "ground"

et répondez aux questions comme ceci :

  1. $ yo phaser-official:prefab bird
  2. Creating the new prefab: bird  
  3. [?] What is the name of your prefab? bird
  4. [?] What sprite key would you like to use? bird
  5.    create game/prefabs/bird.js

Télécharger

Ouvrons le fichier ground.js du répertoire game/prefabs/ . Il devrait ressembler exactement à bird.js du même répertoire. Toutefois, la class s’appelle maintenant Ground.

En regardant le code de game/prefabs/ground.js, vous vous direz : « Je pensais que nous allions utiliser des TileSprite au lieu des Sprite dont la class hérite. »

Et vous aurez raison ! Nous allons voir comment remédier à cela.

Heureusement pour nous, javascript est si malléabble qu’il est vraiment facile de faire en sorte que notre prefab hérite de TileSprite au lieu de Sprite. Il suffit de modifier game/prefabs/ground.js de la façon suivante :

  1. 'use strict';
  2.  
  3. var Ground = function(game, x, y, width, height) {  
  4.   Phaser.TileSprite.call(this, game, x, y, width, height, 'ground');
  5. };
  6.  
  7. Ground.prototype = Object.create(Phaser.TileSprite.prototype);  
  8. Ground.prototype.constructor = Ground;
  9.  
  10. Ground.prototype.update = function() {  
  11.   // write your prefab's specific update code here  
  12. };

Télécharger

Il suffit donc de remplacer « Sprite » par « TileSprite », de supprimer frame et d’ajouter width et height aux lignes 3 et 4. Maintenant, notre prefab hérite de Phaser.TileSprite au lieu de Phaser.Sprite .

Au moment de la rédaction de la partie de ce tutoriel, il semble que la sauvegarde ne soit pas détecté par le générateur. Il faut sauvegarder un état... game/states/play.js par exemple. Jeremy devrait résoudre cela pour la prochaine version du générateur.

Appliquons la physique à notre sol

Pourquoi appliquer la physique à notre Prefab Ground ?

Tout simplement parce que tout ce qui doit entrer en collision avec d’autres choses doit avoir un corps physique.

Cela ce fait de la même façon que sur notre oiseau.

  1. var Ground = function(this.game, x, y, width, height) {  
  2.   Phaser.TileSprite.call(this, this.game, x, y, width, height, 'ground');
  3.  
  4.   // enable physics on the ground sprite
  5.   // this is needed for collision detection
  6.   this.game.physics.arcade.enableBody(this);
  7. }

Télécharger

Maintenant, chaque fois que nous créerons un nouvel objet Ground, il subira automatique la physique. Juste comme avec notre prefab bird.

Ajoutons un sol à notre état play dans le fichier game/states/play.js :

  1. create: function() {  
  2.     /** All previous code **//
  3.  
  4.     // create and add a new Ground object
  5.     this.ground = new Ground(this.game, 0, 400, 335, 112);
  6.     this.game.add.existing(this.ground);
  7. }

Télécharger

Souvenez-vous, parce que Ground hérite de TileSprite, nous avons besoin de lui passer les paramètres requis pour TileSprite. Lesquels sont ( game,x,y,width,height,key ). Comme nous avons codé en dur la clé ou l’identifiant key dans la class pour que ce soit 'ground', nous n’avons pas besoin de passer ce paramètre.

Nous devons aussi requérir notre prefab dans ce fichier. Sous celui de bird, nous ajoutons :

  1. var Ground = require('../prefabs/ground');

Sauvegardons et voyons ce qui se passe à l’écran !

Oups...

Tiens notre sol, tout comme notre oiseau est soumis à la gravité... Il ne défile pas non plus comme dans notre menu... Réglons cela :

  1. var Ground = function(this.game, x, y, width, height) {  
  2.   Phaser.TileSprite.call(this, this.game, x, y, width, height, 'ground');
  3.   // start scrolling our ground
  4.   this.autoScroll(-200,0);
  5.  
  6.   // enable physics on the ground sprite
  7.   // this is needed for collision detection
  8.   this.game.physics.arcade.enableBody(this);
  9.  
  10.   // we don't want the ground's body
  11.   // to be affected by gravity
  12.   this.body.allowGravity = false;
  13. };

Télécharger

Nous retrouvons la méthode autoScroll de nous avion vue dans la partie 1. La deuxième partie permet de soustraire notre sol à la gravité.

Qu’est ce que this.body ?

this.body est une référence au corps physique d’un objet du jeu ( GameObjet ) C’est un objet complexe qui contient toutes les données physique et détermine comment l’objet qui lui est rattaché bouge, réagit aux collisions et autres stimulation physique. Chacun des systèmes objets corps physique ( body object ) a différente méthode pour manipulation et collision.

this.body.allowGravity est un simple interrupteur pour dire au système Arcade physics si tel ou tel corps est affecté par la gravité.

Regardons le résultat !

Pas mal ! Le sol reste en place ! Mais... Notre oiseau passe au travers en tombant...

Faire entrer en collision les objets !

Nous avons notre sol et notre oiseau, faisons les entrer en collision !

Ouvrons game/states/play.js, rendons nous à la fonction update() et ajoutons le code suivant :

  1. update: function() {  
  2.     this.game.physics.arcade.collide(this.bird, this.ground);
  3. }

Télécharger

Cette simple ligne de code dit au système Arcade Physics de vérifier si this.bird et this.ground entre en collision. Si une collision est détectée, le système physique réagit en conséquence et c’est pourquoi nous voyons ceci :

Mais que diable ce passe-t-il ?

Question pertinente ! Repassons enrevue tout ce que nous avons fait d’un point de vue physique d’une manière manière intelligible pour un être humain.

Dans la fonction Create() de notre état « play », nous disons à Phaser d’utiliser le système Arcade physics. Nous avons appliqué une gravité global pour que tout ce qui a un corps physique tombe entre en collision. Dans notre Bird prefab , nous avons dit au system physics de créer un corps sur notre sprite. Ce qui reviens à dire à notre système physics de lui appliquer la gravité et les collisions globales.. Nous avons fait de même sur notre prefab Ground. Cependant, nous avons ensuite dite à notre sol de ne pas se soumettre à la gravité... Puis dans la fonction update() de notre état « play » nous disons à notre système physics de tester les collission entre notre sol et notre oiseau et d’agir en conséquence...

Ce que nous n’avons pas fait est de dire au système physique que notre sol ne devait pas être soumis au conséquences d’une collision. Du coup, lorsque notre oiseau entre en collision avec notre sol, celui-ci entraîne ce dernier dans sa chute...

Empêchons cela !

Notre notre prefab game/prefabs/ground.js, juste sous la ligne qui empêche la gravité d’agir sur notre sol, nous ajoutons le ligne suivante :

  1. this.body.immovable = true;

Cette simple ligne de code dit au système physique que tout objet Ground créé doit réagir uniquement à la physique créée et mis en place uniquement par lui-même... Il ne sera donc pas soumis aux forces externes...

Ce que nous devrion voir à l’écran maintenant :

Prochaine étape !

Dans la prochaine partie de ce tutoriel Phaser, nous verrons les contrôles du joueur et son animation...

Code source

Tout le code pertinent de cette partie du tutoriel est disponible sous forme gist ici.

Vous avez aimé ? Partager !

Twitter Facebook Google Plus Tumblr Linkedin
Cet article à 5 articles connexes :