On va avoir pas mal de lignes de code, il est donc judicieux de bien s’organiser et de bien charger et référencer les librairies dont nous aurons besoin. Nous verrons aussi la partie amorçage du jeu.
La page HTML
Voyons quelles sont les librairies JavaScript dont nous allons avoir besoin :
-
pixi.js : c’est notre librairie de référence,
-
sat.js : pour les collisions,
-
pixi-particles.js : pour les particules,
-
howler.min.js : pour les sons.
Il va falloir également mettre en place quelques règles de style pour éviter les marges, les ascenseurs et mettre le fond noir. Au final le squelette de la page HTML est celui-ci :
<!DOCTYPE HTML> <html> <title>Crazy Bird</title> <meta charset="UTF-8"> <style> body { margin: 0; overflow: hidden; background-color: black; } </style> <!-- Load Pixi.js Engine and libraries --> <script src="js/pixi.js"></script> <script src="js/sat.js"></script> <script src="js/pixi-particles.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/howler/1.1.28/howler.min.js"></script> <body> <!-- Load Crazy Bird --> <script src="js/game.js"></script> <script src="js/boot.js"></script> <script> document.addEventListener('DOMContentLoaded', function () { C.boot.init(); }, false); </script> </body> </html>
On n’a un CDN que pour la librairie des sons, pour les autres il faut donc les placer dans le dossier js :
Remarquez que j’ai aussi prévu les versions minifiées histoire d’être complet mais pendant toute la phase développement on utilisera les versions explicites.
J’ai aussi prévu deux chargements pour des fichiers du jeu :
-
game.js
-
boot.js
On va voir ça dans la suite de ce chapitre.
J’ai aussi ajouté un événement (DOMContentLoaded) pour démarrer l’amorçage lorsque le DOM est totalement chargé. On fait alors appel à une méthode init
que nous allons voir plus loin dans ce chapitre.
Pour le moment on obtient juste un écran tout noir mais la base est en place…
Organiser le code
JavaScript est un langage à part, pas vraiment simple, pas vraiment compliqué non plus, mais ils dépaysent par rapport aux autres langages plus classiques. On ne dispose pas de classes, d’espaces de noms ou d’héritage mais on peut tout de même créer des objets, d’ailleurs dans ce langage tout est objet…
Il y a de multiples façon d’organiser son code avec JavaScript, je vais vous en proposer une qui me convient. J’ai dit qu’on n’avait pas d’espaces de noms avec JavaScript ce qui est assez gênant, tout mettre dans l’espace global peut devenir catastrophique avec des conflits de nommage. On peut par contre simuler les espaces de noms :
var CRAZYBIRD = CRAZYBIRD || { }; var C = CRAZYBIRD;
Ici je crée un objet qui permet d’isoler le code de l’espace global. Ça va me servir de pseudo espace de nom pour placer tout le jeu dedans.
Remarquez aussi que je crée un alias pour éviter d’avoir à utiliser le nom complet CRAZYBIRD
dans l’ensemble du code, je vais pouvoir me contenter de C
.
Pour les autres fichiers on aura alors ce genre de définition :
C.boot = { ... };
Le fichier « game »
Il nous faut un fichier central pour mémoriser les variables globales de l’application et pour loger la boucle du jeu :
var CRAZYBIRD = CRAZYBIRD || { // Properties state: undefined, stage: undefined, renderer: undefined, sprites: { loading: undefined }, // Game loop gameLoop: function () { requestAnimationFrame(C.gameLoop); if (typeof C.state !== 'undefined') { C.state(); } C.renderer.render(C.stage); } }; var C = CRAZYBIRD;
Voyons les propriétés :
-
state : notre jeu va passer par des états différents : chargement, menu, jeu, fin de jeu… Au niveau de la boucle de jeu le traitement sera évidemment dans chaque cas différent. Il est judicieux de créer une variable pour pointer la fonction qui doit à chaque étape effectuer le traitement, c’est le rôle de
state
, -
stage : c’est le conteneur que nous connaissons déjà,
-
renderer : le gestionnaire du canvas que nous avons déjà utilisé,
-
sprites : un objet qui va contenir les pointeurs de tous les sprites, pour le moment on aura juste celui nécessaire pour afficher « Loading ».
On va évidemment enrichir cette collection de propriétés en avançant dans le codage.
On a ensuite la boucle de jeu classique, on teste la valeur de state
parce que pour certaines étapes il n’y a pas besoin de modifier l’affichage et donc il est inutile d’appeler un traitement.
Le fichier « boot »
Dans ce fichier on va avoir l’amorçage du jeu :
/* global PIXI, C */ C.boot = { // Properties dimensions: { width: 1366, height: 768 }, // Initialisations init: function () { // Resize event window.onresize = function () { C.boot.setDimensions(); }; // Pixi init C.renderer = PIXI.autoDetectRenderer(this.dimensions.width, this.dimensions.height); this.setDimensions(); document.body.appendChild(C.renderer.view); C.stage = new PIXI.Container(); // Load google font window.WebFontConfig = { google: { families: ['Snippet'] }, active: function () { C.boot.setup(); } }; // Include web-font loader script (function () { var wf = document.createElement("script"); wf.src = ('https:' === document.location.protocol ? 'https' : 'http') + '://ajax.googleapis.com/ajax/libs/webfont/1/webfont.js'; wf.async = 'true'; document.head.appendChild(wf); })(); C.gameLoop(); }, setup: function () { var style = { font: 'bold italic 72px Snippet', fill: '#dddd42', stroke: '#aaaa20', strokeThickness: 4, dropShadow: true, dropShadowColor: '#888800', dropShadowAngle: Math.PI / 6, dropShadowDistance: 4 }; // Set loading text C.sprites.loading = new PIXI.Text('Loading...', style); C.sprites.loading.anchor.set(0.5); C.sprites.loading.position.set(C.renderer.width / 2, C.renderer.height / 2); C.stage.addChild(C.sprites.loading); C.load.init(); }, setDimensions: function () { C.renderer.view.style.width = window.innerWidth + "px"; C.renderer.view.style.height = window.innerHeight + "px"; } };
Voyons ses méthodes.
Init
C’est la toute première méthode appelée. On commence par mettre en place un gestionnaire pour l’événement de redimensionnement de l’écran :
// Resize event window.onresize = function () { C.boot.setDimensions(); };
On a déjà vu ça dans le chapitre 5 de la première partie ainsi que la fonction associée setDimensions
.
Ensuite on charge une police de Google :
// Load google font window.WebFontConfig = { google: { families: ['Snippet'] }, active: function () { C.boot.setup(); } }; // Include web-font loader script (function () { var wf = document.createElement("script"); wf.src = ('https:' === document.location.protocol ? 'https' : 'http') + '://ajax.googleapis.com/ajax/libs/webfont/1/webfont.js'; wf.async = 'true'; document.head.appendChild(wf); })();
C’est aussi du code connu qu’on a déjà rencontré dans le chapitre 4 de la première partie.
Remarquez qu’on attend le chargement de la police avant de d’appeler la fonction setup
.
Pour finir on met en route la boucle de jeu :
C.gameLoop();
setup
Dans cette fonction on se contente de créer un texte avec la police chargée qui affiche « Loading… » au centre de l’écran. Si vous cherchez les possibilités de stylisation du texte c’est ici dans la documentation.
La fonction se termine avec un appel d’une fonction que nous n’avons pas encore implémentée :
C.load.init();
C’est tout simplement la suite des opérations que nous verrons dans un prochain chapitre.
Pour le moment on affiche juste ceci :
Le dossier js s’est un peu étoffé. On peut d’ailleurs faire le point de ce qu’on a comme dossiers et fichiers pour le moment :
En résumé
Voyons où nous en sommes:
-
on a toutes les ressources mais on ne peut pas encore s’en servir,
-
on a chargé toutes les librairies nécessaires,
-
on a créé le canvas et il s’adapte automatiquement à l’affichage,
-
on a chargé une police Google qui nous servira plusieurs fois,
-
on affiche un message d’attente « Loading… ».