Introduction au développement de jeux HTML5 avec Phaser et l'API du gyroscope

Cet article nécessite une relecture rédactionnelle. Voici comment vous pouvez aider.

Cette traduction est incomplète. Aidez à traduire cet article depuis l'anglais.

Brouillon
Cette page n'est pas terminée.

Exemple de jeu

A la fin de ce tutoriel, vous aurez une démo entièrement fonctionnelle du jeu : Cyber Orb. Il ressemblera à quelque chose comme celà :

A 2D game board featuring a small yellow ball. There is a large black hole for the ball to escape down, and a number of barriers blocking the ball from escaping.

Introduction

Dans ce tutoriel nous verrons pas à pas comment construire un jeu mobile avec les technologies HTML5 qui utilise l'API d'orientation du téléphone en tant que gameplay principal avec le framework  Phaser. Des bases de JavaScript sont requises pour comprendre cet article. Le code source du projet en cours sera présent à la fin de chaque étape. Un démo complète du jeu sera présente à la fin de ce tutoriel.

Introduction à Phaser

Phaser est un framework pour créer des jeux mobiles et PC en utilisant les technologies HTML5. Malgré son manque de maturité, la communauté est assez active, et il évolue rapidement.  Les sources sont sur Github, lisez y la documentation de base, jetez un œil aux exemples. Le framework Phaser offre un ensemble d'outils qui permettent d'accélérer le développement et aider à mettre en oeuvre les tâches communes nécéssaire au développement d'un  jeu.

Mettre en place le projet

Vous pouvez voir le code d'exemple du projet sur GitHub. La structure n'est pas compliquée : le point de départ est le fichier index.html où on initialise le framework, met en place le canvas et joue au jeu.

Screenshot of the GitHub repository with the Cyber Orb game code, listing the folders and the files in the main structure.

Vous pouvez l'ouvir avec votre navigateur préféré pour essayer le jeu. Il y a aussi trois dossiers:

  • img : toutes les images utilisées dans le jeu
  • src : les fichiers JavaScript où le code source est définit
  • audio : tous les fichiers son du jeu

Nous commencerons par le code de base nécéssaire au framework pour nous concentrer sur l'élaboration du jeu en lui même, mais je passerais rapidement l'expliquation des points clefs du code source à disposition.

Mettre en place le canvas

L'affichage du jeu se fait sur un élément <canvas> : le point de départ est le fichier index.html avec le contenu suivant :

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <title>Cyber Orb</title>
    <style> body { margin: 0; background: #333; } </style>
    <script src="src/phaser.min.js"></script>
    <script src="src/Boot.js"></script>
    <script src="src/Preloader.js"></script>
    <script src="src/MainMenu.js"></script>
    <script src="src/Game.js"></script>
</head>
<body>
<script>
(function() {
    var game = new Phaser.Game(320, 480, Phaser.CANVAS, 'game');
    game.state.add('Boot', Ball.Boot);
    game.state.add('Preloader', Ball.Preloader);
    game.state.add('MainMenu', Ball.MainMenu);
    game.state.add('Game', Ball.Game);
    game.state.start('Boot');
})();
</script>
</body>
</html>

Ça ressemble à une simple page de site HTML avec des éléments basiques dans la balise <head> : encodage, titre, css et l'inclusion des fichiers Javascript. <body> contient l'initialisation du framework et la définition des game states ( phases du jeu ). Pas besoin de mettre une balise <canvas> nous même, elle est générée automatiquement par le framework.

Utiliser les game states

Les games states dans phaser sont différentes phases du jeu. Dans notre cas on les charge depuis des fichiers Javascript séparés pour mieux les maintenir par la suite. Dans ce jeu nous avons les game states : Boot, Preloader, MainMenu et Game. Boot s'occupe d'initialiser quelques paramètres, Preloader charge tous les graphismes, MainMenu est l'écran visible après le chargement sur lequel apparait le bouton start. il fera la transition vers le game state Game, où l'on commence à jouer.

Vous pouvez déjà jeter un coup d'oeil au code de base, regarder comment ça marche. Dans ce tutoriel on modifiera le game state  Game dans le fichier Game.js. Toute l'initialisation ira dans la fonction create() ( démarrée une seule fois, au début du jeu ), certaines chosent doivent aller dans la fonction update() ( exécutée à chaque frame ), ou même dans vos propres fonctions pour des tâches plus complexes.

Ajouter la balle et sa physique

D'abord, dans la fonction create, initialisons l'objet 'ball' et assignons lui quelques propriétés :

ball = this.add.sprite((320-22)/2, 450, 'ball');
ball.anchor.setTo(0.5, 0.5);
ball.body.bounce.setTo(0.3, 0.3);
ball.body.setCircle(10, 11, 11);
ball.body.linearDamping = 1;

On ajoute un sprite à une place donnée sur l'écran en utilisant l'image 'ball'. On ajoute aussi le point de repère de tous les calculs physiques ( 'anchor' ) au milieu de la balle, le rebond et le cercle de détection de collisions. linearDamping est utilisé pour appliquer la friction, et ainsi l'empecher de rebondir pour à jamais.

Controler la balle

C'est déjà sympa d'avoir une balle prête à être lancée dans la zone de jeu, mais c'est aussi important de pouvoir le faire. Maintenant on va ajouter la possibilité de controler la balle avec le clavier sur les ordinateurs, et ensuite on ajoutera l'implémentation de l'API du gyroscope. Maintenant, concentrons nous sur le clavier :

keys = this.game.input.keyboard.createCursorKeys();

Comme vous pouvez le voir, Phaser a une fonction toute prête qui nous permet de gérer les flèches directionnelles du clavier. L'object keys sera vérifié aux pressions de touche du joueur, la balle réagira donc en conséquence :

var force = 10;
if(keys.left.isDown) {
    ball.body.velocity.x -= force;
}
else if(keys.right.isDown) {
    ball.body.velocity.x += force;
}
if(keys.up.isDown) {
    ball.body.velocity.y -= force;
}
else if(keys.down.isDown) {
    ball.body.velocity.y += force;
}

Le code suivant est à placer dans la fonction update, il sera exécuté à chaque frame. De cette manière on peut vérifier quelle touche est préssée à un temps donné et appliquer une force définie à la balle, ce qui a pour effet d'augmenter sa vélocité dans la bonne direction.

Implémentation de l'API gyroscopique

La particularité du jeu est qu'il utilise l'API gyroscopique sur les mobiles. Grâce à elle, vous pouvez jouer au jeu en inclinant l'appareil dans la direction où vous voulez que la balle aille. Voilà le code qui l'implémente :

window.addEventListener("deviceorientation", this.handleOrientation, true);

On ajoute un event listener à l'évenement "deviceorientation" et on modifie la fonction handleOrientation :

handleOrientation: function(e) {
    var x = e.gamma; // range [-90,90]
    var y = e.beta;  // range [-180,180]
    ball.body.velocity.x -= x*2;
    ball.body.velocity.y -= y*4;
}

Plus l'appareil est incliné, plus la force appliquée à la balle et sa vélocité sont importantes.

Ajouter un trou

L'objectif principal du jeu est de déplacer la balle du point de départ vers le point d'arrivé, qui est dans notre cas, un trou dans le sol. L'implémentation ressemble beaucoup à celle de la création de la balle :

hole = this.add.sprite((320-22)/2, 90, 'hole');
hole.body.immovable = true;
hole.anchor.setTo(0.5, 0.5);
hole.body.setCircle(5,15,15);

La seule différence est que 'hole.body' est mis à immovable, il ne bougera donc pas quand la balle le touchera et la collision sera alors calculée ( ce point sera aprofondit plus loin dans cet article ).

Créer le mur du labyrinthe

For the labyrinth we want to build to make the life (and play) of the player harder we could use a level editor, but for the purpose of this article let’s create one level from scratch manually.

walls = this.game.add.group();
walls.add(panel);

First let’s start with creating the group to which we’ll add the top panel, so the ball can bounce off of it.

walls.create(220-32, 480-128, 'element-h').body.immovable = true;
walls.create(92, 480-128-32, 'element-w').body.immovable = true;
walls.create(0, 240, 'element-w').body.immovable = true;
walls.create(128, 240, 'element-w').body.immovable = true;
walls.create(256, 240, 'element-h').body.immovable = true;
walls.create(180, 58, 'element-h').body.immovable = true;
walls.create(52, 154, 'element-w').body.immovable = true;

Then the walls are created by setting the top and left positions, passing the image name and setting the immovable attribute to true, so the wall can’t be moved when hit by a ball. You can of course do it your way or read about implementing the data from an external level editor, which is supported in Phaser.

Collision detection

We have the ball that can be controlled by the player, the hole that it have to reach and the obstacles that block the way and that the ball have to be guided through them. There’s a problem though – our game don’t have the collision detection, so nothing happens when the ball hits the blocks, it just goes through. That’s not the effect we wanted to achieve, so let’s work on it. The good news is that the framework will mostly take care of the collision detection, we only have to specify what will collide with what:

this.game.physics.collide(ball, walls, this.wallCollision, null, this);

This will tell the framework to execute the wallCollision function when the ball will hit any of the walls. The wallCollision function is an empty one, but we can use it to add any functionality we want, like for example adding the bounce sound.

We should also get back to the ball object and restrict it to move only in the visible area, so it won’t roll out of the screen. There’s very helpful function in Phaser that is called collideWorldBounds:

ball.body.collideWorldBounds = true;

It does exactly what we need – now the ball will bounce off of the edges of the screen like from the walls.

Adding the elapsed time

To improve the replayability and give the players option to compete with each other we can introduce the elapsed time. Thanks to this the player can play the given levels again and again trying to improve his score. To implement that in the game we have to create a variable to store actual number of second elapsed from the start of the game and to show it for the player in the game. Let’s define the variable first:

timer = 0;

Then we can initialize the text object:

timerText = this.game.add.text(15, 20, "Time: "+timer, { font: "24px Arial", fill: "#333333" });

We’re defining the top and left position of the text, the content that will be showed and we can also apply basic CSS-like styling to the text, so it looks better. We have this printed out on the screen, but it would be good to update the value every second:

loop = this.game.time.events.loop(Phaser.Timer.SECOND, this.updateCounter, this);

This loop will execute the updateCounter function in every single second from the beginning of the game, so we can apply the changes accordingly. This is the core of the updateCounter function:

updateCounter: function() {
    timer++;
    timerText.content = "Time: "+timer;
},

As you can see we’re incrementing the timer variable and updating the content of the text object with the current value, so the player sees the elapsed time.

Finishing the level

The ball is rolling on the screen, the time is running out, we have the hole that we have to reach, so let’s set up the possibility to actually finish the game. We will cover few aspects of the situation when the ball gets to the hole.

this.game.physics.collide(ball, hole, this.finishLevel, null, this);

That will activate the finishLevel function as soon as the ball reaches the hole:

alert('HOLE!');
timer = 0;
level++;
this.game.state.start('Game');

For now the alert pops out with the short information that the hole was reached. We reset the timer, increment the level counter and restarts the Game state with the current settings saved in the variables.

Ideas for new features

This is merely a working demo of a game that could have lots of other different features. We can for example add power-ups to collect along the way that will make our ball roll faster, other can stop the timer for a few seconds or give the ball special powers. There’s also room for the obstacles and traps which will slow the ball down, speed up the time or trap the ball itself. You can create more levels with the different difficulty for each one. You can even have achievements, leaderboards and medals for different actions in the game. There are endless possibilities – they only depend on your imagination.

Summary

I hope that the tutorial above was easy to learn from and you can dive into Phaser, read the documentation, hack the examples and create awesome games on your own already. You can play the demo of the Cyber Orb game or check out the source code on GitHub. The technology gives us the tools, the frameworks are getting faster and better, so you just have to start your own journey and see if HTML5 game development is something that you want to spend your time on.

Étiquettes et contributeurs liés au document

 Contributeurs à cette page : PhilippeS, Antoine
 Dernière mise à jour par : PhilippeS,