Il s'agit d'une version statique de l'intranet d'A. Jonquet, certaines fonctionnalités sont donc potentiellement non fonctionnelles.
Rejoindre la version dynamique 🔒
R301
Navigation

Mise en place d'un projet Phaser

Ce premier TP portera sur la mise en place d'un environnement de travail JavaScript dans le cadre de la présentation de la bibliothèque de création de jeu Phaser.

Ce sera l'occasion de mettre en application les bonnes pratiques et les outils associés, largement plébiscités dans le monde du développement JavaScript moderne.

Le code étant relativement simpliste, nous nous focaliserons sur les bonnes pratiques et les outils.

Remarque importante

Les notes des TP seront obtenues à partir de vos dépôts Git, vous devrez donc prendre grand soin de la qualité de ces dépôts et de leurs « commits ».

De manière similaire, les descriptions de vos « commits » devront être claires et informatives, afin de permettre l'évaluation de la progression de votre travail.

Objectifs de la séance

  • Gestion de versions à l'aide de Git
  • Gestion de paquets npm
  • Mise en place d'un environnement de travail JavaScript
  • Découverte de Phaser
  • Approfondissement du langage JavaScript

Mise en place du projet

Remarque importante

Comme pour les projets Composer, votre projet va générer de nombreux petits fichiers qui seront stockés localement dans le répertoire « node_modules » de votre projet.

Les infrastructures des systèmes d'information de l'université et du département informatique compliquent l'accès rapide à de très nombreuses ressources. Afin d'obtenir des temps de réaction convenables pour les différents outils que vous utiliserez, vous êtes encouragé à travailler dans le répertoire local « /working » de votre poste de travail.

Afin de ne pas laisser votre travail uniquement sur la machine, vous devrez impérativement réaliser un push en fin de séance afin de le sauver sur le serveur Gitlab.

Vous pourrez facilement réinitialiser votre projet sur une nouvelle machine à l'aide de la commande git clone suivie de npm install dans le répertoire du projet. Cette dernière commande permet de réinstaller tous les modules npm dont dépend votre projet.

Pour que vous n'ayez pas à installer/configurer tout les outils et de simplifier la mise en place du projet, vous allez partir d'une base de projet déjà existante, contenant déjà tous les outils configurés que vous utiliserez dans ces TP.

Vous commencerez donc par récupérer la structuration initiale du projet en clonant le dépôt r301-js-template dans un répertoire local « r301-js-introduction » :

git clone --depth=1 https://iut-info.univ-reims.fr/gitlab/jonque01/r301-js-template r301-js-introduction

Le clonage du projet fourni versionnage du projet précédent (tronqué par l'option depth). Or vous allez vouloir disposer d'un versionnage neuf. Depuis le répertoire du projet, vous supprimerez le dépôt Git local :

rm -rf .git

La gestion du projet sera réalisée au travers du gestionnaire de paquet de Node : npm. Les paquets npm centralisent les informations du projet dans un fichier « package.json » à la racine du répertoire du projet.

Vous modifierez ses valeurs en éditant directement le fichier « package.json », une liste exhaustive des différentes options est disponible dans la documentation de npm.

Pour votre projet, vous commencerez par préciser le nom, l'auteur et la description du projet.

En plus de ces options, vous trouverez dans le fichier de configuration :

  • private permettant de s'assurer que le projet ne soit pas publié sur le site de npm,
  • version spécifiant la version du projet (facultatif dans un projet privé),
  • version spécifiant la version du projet (facultatif dans un projet privé),
  • type, une option utilisée par Node pour définir comment gérer les modules JavaScript (dans notre cas, comme des modules ECMAScript),
  • scripts, un tableau associatif permettant de définir des commandes utiles à la gestion du projet,
  • keywords et license, des informations utiles lors de la publication du projet,
  • devdependencies, une liste des packets npm, avec les versions requise, nécessaire au développement du projet.

La gestion de versions et l'archivage seront réalisés à l'aide de Git. Pour un rapide tour d'horizon de Git, vous pouvez vous référer au site de Monsieur Nourrit.

Vous commencerez par créer un nouveau dépôt local à l'aide de la commande  : git init. Cette commande provoque la création du répertoire "caché" « .git » contenant les données du dépôt local.

Vous pourrez ensuite indiquer à Git de suivre tous les fichiers de votre projet : git add ..

Vous pourrez contrôler les fichiers suivis à l'aide de la commande git status. Lorsque tout est bon, vous pourrez réaliser le premier « commit » de votre dépôt : git commit -m "project initialization".

Vous associerez votre dépôt local à un dépôt distant nommé « r301-js-introduction ». Vous veillerez à ajouter votre intervenant de TP comme membre du projet avec un rôle de « Reporter ».

Vous pourrez ensuite associer votre dépôt local au dépôt distant à l'aide de la commande :git remote add origin URL_DU_DÉPÔT, puis pousser la réplication de votre dépôt local grâce à la commande : git push.

Pour finir, vous pouvez installer toutes les dépendances du projet à l'aide de npm avec la commande suivante :

npm install
Remarque importante

Dans le cadre du département Informatique, les paquets npm ajoutés à votre projet sont installés en local, car vous n'avez pas les droits d'écriture sur les machines. Ils sont donc installés dans le sous répertoire « node_modules » de votre projet. Bien que vous travailliez dans le répertoire « /working » de votre poste de travail, npm conserve un cache sur votre compte des paquets installés. Vous êtes invité à configurer npm pour qu'il utilise un répertoire temporaire afin d'économiser de la place sur votre compte et de gagner en performance d'installation.

Travail à réaliser
  • Cloner les fichiers initiaux du projet.
  • Supprimer le dépôt Git local.
  • Configurer le fichier package.json.
  • Créer un dépôt Git local.
  • Répliquer le dépôt local sur le serveur distant GitLab.
  • Vérifier les membres du projet distant GitLab et leurs rôles.
  • Installer les dépendances du projet.

Environnement de développement

Au travers des dépendances du projet que vous venez d'installer, vous disposer d'un ensemble d'outils permettant le développement d'un jeu pour navigateur à l'aide de Phaser.

Parmi ces outils, vous trouverez :

  • la bibliothèque Phaser, offrant un cadre de développement pour la création de jeu,
  • le « bundler » vite, permettant de compiler le code du projet en un script pour le déploiement,
  • et tout un ensemble de paquets npm permettant d'assurer la qualité de code du projet à l'aide d'un « linteur » et d'un formateur de code JavaScript

Dans le monde du web, le recours à un « bundler » est courant. Celui-ci améliore l'expérience de dévelopment en permettant de répartir le code sur plusieurs fichiers, ainsi que d'utiliser des fonctionnalités fournies par des composants npm. Ces fichiers et paquets npm sont ensuite agrégés en un unique script, optimisé pour le web, qui peut être distribué/déployé.

En plus du support des fichiers JavaScript, vite propose aussi de gérer les assets du site web. Dans notre cas, pour faire simple, nous nous contenterons de fournir les assets du projet dans le répertoire « public ». Celles-ci seront copiées avec la page du jeu dans le répertoire « build », lors de la génération du « bundle » de déploiement.

Et pour finir, vite propose aussi un serveur web de développement. Il s'agit d'un serveur web local, par défaut sur le port 5173, rafraîchissant automatiquement la page lors des changements du code.

Vous pouvez lancer le serveur de développement à l'aide du script npm dev, en lançant la commande :

npm run dev

Vous pourrez ensuite constater le rafraîchissement automatique de la page en modifiant le titre de celle-ci dans la page « index.html » du répertoire « src ».

Pour résumer l'organisation du projet, il comporte deux répertoires :

  • « public » qui contient les assets du projet (les fichiers non compilés), qui seront copiés dans le bundle du projet.
  • « src » qui contient les sources du projet (les fichiers compilés), qui seront compilés avant d'être intégré au bundle du projet.

Et à la racine du projet se retrouve un ensemble de fichier de configuration :

  • « eslint.config.js », le fichier de configuration d'ESLint.
  • « package.json », le fichier de description du projet, avec notamment les dépendances et les scripts npm.
  • « package-lock.json », le fichier d'installation des dépendances, qui contient les versions exactes desq dépendances installées.
  • « vite.config.js », le fichier de configuration de vite.

Si vous êtes attentif, vous avez dù constater trois autres scripts npm : build, preview et fix.

Le premier permet de créer le « bundle » du projet, prêt pour le déploiement, dans le répertoire « build ».

Le second permet de servir, avec un serveur local, le « bundle » généré dans le répertoire « build », pour verifier son bon fonctionnement.

Et le dernier permet de lancer les outils de qualité de code sur le projet. Ceux-ci reposent principalement sur ESLint et Prettier qui sont configurés pour fonctionner ensemble avec un jeu de règles pré-définies. Ce script npm essayera de régler les problèmes automatiquement, lorsque c'est possible, mais il vous revient de régler tous les problèmes restants.

Pour une meilleure expérience de développement, vous configurerez votre éditeur pour qu'il supporte ESLint et vous informe des problème lors de l'édition du code.

Pour VSCodium, il faudra peut-être activer/installer l'extension ESLint et pour WebStorm, il faudra peut-être activer ESLint en configuration automatique, dans les outils de qualité de code.

Pour tester le bon fonctionnement de votre configuration, vous ajouterez la ligne suivante au fichier « src/index.js »

Cette instruction permet l'affichage d'un message dans la console de développement du navigateur (touche F12). Votre éditeur devrait vous souligner :

  • L'utilisation de console.log, qui est une instruction généralement utile en phase de développement, et n'a pas vocation à persister dans votre code.
  • L'utilisation de simples côtes pour la chaîne de caractères. Par défaut, Prettier est configuré pour encouragé l'usage des doubles côtes lorsque c'est possible.
  • L'absence d'un point-virgule.
  • Un indentation non conforme.

En exécutant la commande suivante, vous devriez être capable de régler automatiquement tous les problèmes à l'exception du premier. Vous devrez supprimer l'affichage dans la console pour régler celui-ci.

npm run fix

Votre environnement de développement est maintenant mis en place, vous allez pouvoir vous intéresser au code.

Travail à réaliser
  • Découverte des outils de développement intégrés au projet.
  • Découverte de l'organisation du projet.
  • Utilisation de scripts npm.
  • Prise en main du serveur de développement local.
  • Prise en main des outils de qualité de code et configuration de l'éditeur.

Structure du projet Phaser

La dernière dépendance du projet, qui n'a pas été détaillée, est la bibliothèque Phaser.

Phaser n'est pas la seule solution pour réaliser des jeux en JavaScript, plusieurs alternatives sont disponibles, certaines plus petites, efficaces ou bas niveau... Mais la bibliothèque Phaser dispose de certains avantages, notamment, elle est fournie avec de nombreuses fonctionnalités déjà incluses, et surtout, elle dispose d'une grande communauté et d'une documentation exhaustive.

Vous pourrez faire un tour rapide du site web de Phaser afin de découvrir les possibilités de la bibliothèque, notamment la documentation des concepts, les exemples ou les tutoriels de la communauté.

Phaser n'est pas particulièrement contraignant sur la structuration du code, mais le projet que vous avez cloné repose sur l'utilisation de vite pour découper le code en plusieurs fichiers. Comme vous avez vu, les sources du projet sont placé dans le répertoire « src ». Pour l'instant, vous y trouvez :

  • « index.html », la page HTML qui accueillera le jeu. Cette page référence la feuille de style « css/index.css » se trouvant dans le répertoire « public » et le script JavaScript « index.js se trouvant dans le répertoire « src ». C'est ce fichier que vite utilise pour connaitre quels fichiers doivent être compilés dans le projet.
  • « index.js », le script JavaScript contenant le code du jeu. Ce script est importé depuis la page HTML et sera donc « bundlé »
  • « config.js », un script JavaScript fournissant la configuration générale de Phaser.
  • « scene/Play.js », un script JavaScript fournissant la scène du jeu, vide pour l'instant.

Le fichier « index.html » fournit la page HTML contenant le jeu. Vous pouvez l'éditer pour enrichir la page du jeu, mais dans le cadre de ce module, vous n'aurez probablement pas à y intervenir.

Le fichier « index.js », par contre, est le script principal du jeu. Pour l'instant, il se comporte d'importer la bibliothèque Phaser, la configuration fournie par le script « config.js » et la scène fournie par le script « scene/Play.js ». Le script se contente ensuite de créer une nouvelle instance de jeu avec la configuration souhaitée, puis d'y ajouter la scène Play avec l'option de démarrage automatique.

Vous noterez qu'il est possible d'importer des modules JavaScript depuis les dépendances (Phaser) avec le nom de la dépendance ou depuis le système de fichiers (« ./config » et « ./scene/Play ») en préfixant avec le répertoire courant (« . »). Dans tous les cas, il s'agit d'imports par défaut, correspondant aux exports par défaut des modules correspondants.

La configuration du jeu, définit la zone de jeu comme un espace de 800 pixels par 600 pixels, centrée, qui sera réduite pour conserver son ratio largeur/hauteur si la page est trop petite.

Enfin, la scène du jeu se compose d'une classe vide héritant d'une scene de Phaser. C'est ici, que ce trouvera l'essentiel de la logique du jeu.

Travail à réaliser
  • Découvrir la documentation de la bibliothèque Phaser.
  • Découvrir la structuration du code.
  • Découvrir comment exporter/importer des modules JavaScript.

Éléments de jeu et interaction

Maintenant que votre projet est fonctionnel, vous allez finaliser la découverte de Phaser au travers d'un jeu trés simple, un compteur de clics. Le jeu proposera une surface, que l'utilisateur devra cliquer le plus de fois en 1 seconde.

Pour commencer, vous allez deux éléments, un disque et un texte, en ajoutant la méthode suivante à la classe Play :

La méthode create de la classe Scene est une méthode spéciale, qui est appelée automatiquement au démarrage de la scène. Son objectif est de créer la scène.

Dans ces quelques lignes de code, this est l'instance de notre scène, nous pouvons donc accéder à la propriété add contenant une instance d'usine à élément de jeu, dont les méthodes circle et text permettent d'ajouter à la scène, réspectivement, un disque et un texte.

Outre la configuration spécifique des éléments de jeu avec setStrokeStyle et setFontSize, vous noterez l'utilisation du gestionnaire d'échelle, référencé par la propriété d'instance scale de la scène, pour obtenir les dimensions de la scène. Ainsi que la méthode d'instance setOrigin pour positionner l'origine des éléments de jeu.

Après avoir rafraîchi votre navigateur, vous devriez voir apparaitre les éléments de jeu dans votre scène.

Afin d'ajouter la gestion du clic, vous pourrez utiliser le code suivant :

Le support de l'interaction de l'utilisateur est ajouté au disque à l'aide de la méthode d'instance setInteractive de la classe GameObject dont hérite tous les éléments de jeu. Il est ensuite possible d'ajouter une fonction de rappel à l'événement « pointerdown » du disque. Cette fonction de rappel sera alors invoquée lors de chaque événement de type « pointerdown » déclenché sur le disque.

Dans notre cas, le texte de l'élément de jeu est modifié pour afficher « click ».

Détail JavaScript

JavaScript propose plusieurs solutions pour transmettre une fonction. On retrouve généralement la construction d'une fonction « classique » (à l'aide du mot clé function) ou par l'utilisation de fonction fléchée.

La principale différence réside dans la création d'un nouveau context dans le cas des fonctions « classique » (le this). Dans ce cas, Phaser propose généralement lors de l'enregistrement de la fonction de rappel de spécifier le context à transmettre à la fonction lors de l'invocation, comme le paramètre context de la méthode on, par exemple.

Travail à réaliser
  • Ajout d'un disque et d'un texte à la zone de jeu.
  • Gestion du clic sur le disque.

Ajout d'un événement différé

Avant de compter le nombre de clics de l'utilisateur, vous allez mettre en place un chronomètre permettant de compter 1 seconde.

Parmi les nombreux outils proposés par Phaser, la classe scène propose, au travers de sa propriété d'instance time, une horloge permettant de programmer temporellement des événements à l'aide de la méthode d'instance addEvent.

Lors du clic sur le disque, vous remplacerez la modification du texte par la création d'un nouvel événement différé de 1000ms, en utilisant addEvent. Vous stockerez cet événement différé dans une propriété d'instance timer, qui sera initialisée à null

Maintenant, votre application devrait créer un nouvel événement différé de 1s à chaque clic de souris. Pour afficher le temps restant d'un événement, vous utiliserez le code suivant que vous ajouterez à la classe Play  :

Ce code permet de définir la méthode update de la classe Play, il s'agit d'une méthode spéciale de la classe Scene, qui est appelée automatiquement pour mettre à jour la scène.

Vous remarquerez que pour partager des variables entre les méthodes, il faut recourir aux propriétés d'instance, comme pour l'événement différé timerText. Sur le même principe, le texte créé dans la méthode d'instance sera stocké dans une propriété d'instance timerText, qui sera modifié dans la méthode d'instance update.

Le texte est mis à jour régulièrement, par Phaser, à l'aide de la méthode getRemainingSeconds de l'événement différé.

Vous devriez maintenant voir la seconde défilée après un clic de la souris sur le disque. Cependant, si vous êtes attentif, vous constaterez que le texte est constamment initializé avec « 0.00 » dans la méthode d'instance update. Pour prévenir ce comportement, vous allez ajouter une fonction de rappel à l'événement différé, qui initialisera l'événement différé à null. Vous en profiterez pour mettre à jour le texte, en invitant l'utilisateur à cliquer sur le disque.

Pour finir, vous avez dû constater qu'il est possible, en cliquant plusieurs fois, de créer plusieurs événements différés en même temps. Vous réglerez ce problème en restreignant la création d'un événement différé, que lorsqu'il n'y en a pas déjà un de créer.

Travail à réaliser
  • Création d'un événement différé lors du clic de souris sur le disque.
  • Utilisation de la méthode d'instance update pour mettre à jour la scène.
  • Ré-initialisation du texte à la fin de l'événement différé.

Compteur de clics

Vous allez maintenant ajouter la gestion des clics en comptant le nombre de clics réalisés pendant la durée de l'événement différé.

Vous commencerez par ajouter un nouveau texte à la scène, au centre en haut de la zone de jeu, permettant d'afficher le nombre de clics de l'utilisateur.

Vous pourrez ensuite ajouter une propriété d'instance pour stocker le nombre de clics. Cette propriété sera initialisé à 1 lors de la création de l'événement différé. Puis lors des clics de souris, où un événement différé est déjà créé, cette propriété sera incrémenté. L'affichage du nombre de clics sera mis à jour lors de chaque clic de souris.

Travail à réaliser
  • Création du texte affichant le nombre de clics.
  • Gestion d'un compteur de clics lors des clics de souris.
  • Mis à jour du texte affichant le nombre de clics lors des clics.

Améliorations

Pour l'utilisateur, la fin du jeu est un peu technique, car s'il clique une fois aprés la fin de l'événement différé, une nouvelle partie démarre, effaçant le score précédent. Pour prévenir ce comportement, vous allez désactiver le clic sur le disque pendant 2 secondes, en fin de partie. Pour cela vous pourrez utiliser la méthode d'instance delayedCall de l'horloge de la scène et la propriété d'instance enabled de la propriété d'instance input du disque.

Pour finir, vous allez ajouter une petite animation sur le texte affichant le nombre de clics, lorsqu'il est modifié. Pour animer des propriétés d'objet, Phaser propose d'utiliser des « Tweens ». Il s'agit de fonction permettant d'interpoler les valeurs des propriétés dans le temps. Vous pouvez par exemple ajouter le code suivant qui permet d'interpoler les valeurs scaleX et scaleY de l'objet clicksText (qui affiche le nombre de clics) selon une fonction Bounce.Out en 500ms.

Ce code est à ajouter lors de la modification du texte de clicksText lors du clic de la souris.

Travail à réaliser
  • Ajout d'un délai en fin de partie.
  • Ajout d'une animation sur le nombre de clics lors d'un nouveau clic.
A. Jonquet DUT-INFO/REIMS