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

Création d'un jeu de plateforme

Ce second TP portera sur la création d'un jeu de plateforme à l'aide de la bibliothèque de création de jeu Phaser.

Nous en profiterons pour approfondir notre connaissance de la bibliothèque, notamment en découvrant la gestion des images et en explorant la communication entre les scènes et différents types de collision.

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

  • Approfondissement de Phaser
  • Gestion des images dans Phaser
  • Communication entre différentes scènes
  • Gestion de différents types de collision

Mise en place du projet

Vous avez vu dans le TP précédent comment mettre en place et configurer votre projet à la main. Il s'agit d'une tache fastidieuse, mais qui vous a permis de comprendre les différents éléments qui composent un projet JavaScript moderne.

Certains outils, à l'instar de EsLint permettent, au travers d'outils en ligne de commandes, de simplifier l'installation et la configuration. Mais la grande liberté qui vous est offerte dans le choix des outils ne vous permet pas d'avoir de solution clé en main pour installer et configurer directement tous les outils souhaités.

Une solution couramment employée est l'utilisation d'un dépôt Git contenant le squelette d'un projet que vous pouvez utiliser comme base de travail. L'utilisation de degit vous permet même de ne pas récupérer le dépôt Git et de démarrer à neuf. Cependant, cette solution vous impose de maintenir votre dépôt à jour.

Pour faire simple, nous allons juste reprendre les fichiers nécessaires depuis votre précédent projet. Dans un nouveau répertoire « r301-js-platformer », vous recopierez les fichiers suivant depuis votre projet précédent :

  • les assets nécessaires du sous-répertoire « public,
  • la page HTML « src/index.html »,
  • les scripts JavaScript « src/index.js » et « src/config.js »,
  • le fichier « .gitignore »,
  • les fichiers de configuration « eslint.config.js » et « vite.config.js »
  • le fichier de définition du projet « package.json ».

Vous modifierez le fichier « package.json » pour qu'il soit cohérent avec votre projet (nom, déscription...). Vous nettoierez le fichier « src/index.js » pour qu'il se contente de créer une instance de jeu avec la configuration de « src/config.js ».

Dans notre cas, il est peu probable que les outils et bibliothèque que vous utilisez soient obsolètes, mais si nécessaire, vous pouvez le faire à l'aide de la commande suivante :

npx npm-check-updates

Il s'agit d'un outil JavaScript, qui contrôle les dépendances de votre projet et vous indique leur version actuelle. Le cas échéant, vous pouvez mettre à jour toutes ou une partie des dépendances à l'aide de npm-check-updates ou de npm directement.

Une fois que les dépendances du projet sont installées, vous devriez être capable de visualiser une zone de jeu dans votre page web de développement. Aprés avoir validé votre projet avec EsLint, vous créerez un dépôt Git local dans lequel vous ajouterez une capture de l'état de votre projet.

Pour finir, vous répliquer le dépôt local sur le serveur distant GitLab du département sous le nom « r301-js-platformer » et vous vous assurerez que votre intervenant de TP y ait accès avec un rôle de « Reporter ».

Travail à réaliser
  • Créer un répertoire local.
  • Y recopier les fichiers de base du projet précédent.
  • Y 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.

Création des scènes de base

Dans le TP précédent, tout le jeu se déroulait dans un même espace. Cependant, la plupart des jeux disposent de plusieurs écrans d'affichage, a minima un écran d'accueil et un écran de jeu.

Sur ce principe, vous allez créer une première scène Title correspondant à l'écran d'accueil. Elle affichera le titre de votre jeu et une invite à cliquer ou presser une touche.

Afin de donner un peu de vie à votre page de titre et pour vous rappeler son fonctionnement, vous ajouterez un tween permettant d'animer la taille de la police de l'invite de jeu, avec une fonction d'interpolation élastique par exemple.

Vous créerez ensuite une deuxième scène Level1 représentant le premier niveau du jeu. Elle ne comportera pour l'instant qu'un texte l'identifiant.

Phaser permet de gérer plusieurs scènes actives en même temps, mais dans un souci de simplicité, nous ne gérerons qu'une scène à la fois. Vous prendrez donc soin lors de l'ajout de ces scènes à l'instance de jeu, de n'activer que la scène d'accueil.

Enfin vous ajouterez deux gestionnaires d'événements, l'un réagissant au clic de la souris et l'autre à l'appui d'une touche. Ils déclencheront tous les deux le démarrage de la scène Level1.

Travail à réaliser
  • Création de la scène Title.
  • Animation du texte d'invite de jeu.
  • Création de la scène Level1.
  • Ajout des scènes au jeu.
  • Démarrage du premier niveau depuis l'accueil.

Gestion des images

Pour créer le premier niveau du jeu, nous allons utiliser des images. Pour commencer, vous allez ajouter une image à votre projet permettant de représenter un bloc de plateforme solide. Afin d'être utilisé dans le jeu, une image doit être accessible par celui-ci. Vous placerez donc l'image suivante : « stone.png » dans le sous-répertoire « public/img ».

Le répertoire « public » est le répertoire que vite utilise pour fournir les ressources statiques du site (les « assets »). Les ressources statiques sont les ressources que vite n'a pas besoin de transformer, les images, sons, vidéos...

Vous ajouterez trois blocs de pierre à votre jeu à l'aide de l'usine à object du jeu. Un niveau de jeu se composant de très nombreux blocs de plateforme, la création et la gestion de chaque bloc individuellement va être rapidement fastidieux. La gestion collective, pour la collision par exemple, peut être rapidement résolu à l'aide des groupes, mais pour résoudre le problème de création de bloc individuellement, vous allez créer deux nouvelles classes : TileGroup et StoneGroup.

TileGroup et StoneGroup diagrammes
Diagram class of StoneGroup

Conformément au diagramme précédent, la classe TileGroup hérite de la classe Phaser.Physics.Arcade.StaticGroup. Elle permet de créer des groupes d'images pavant la zone de jeu.

La propriété tileName contiendra l'identifiant de l'image à utiliser. Les propriétés tileWidth et tileHeight représente la largeur d'une colonne et la hauteur d'une ligne du pavage produit par le groupe.

La méthode addTiles crée et ajoute au groupe un ensemble de blocs d'image en une fois. les blocs d'images seront structurés sous forme d'un rectangle, x et y seront les coordonnées de la première image en haut à gauche avec width et height le nombre de blocs d'image en largeur et en hauteur. Pour simplifier la conception du niveau, les coordonnées seront exprimées en lignes et colonnes de blocs d'image, par exemple, dans notre cas l'image « stone.png » faisant 64×64, l'invocation de la méthode addTiles(2, 3, 3) créera une plateforme de 3 blocs dont les coordonnées du coin supérieur gauche seront (128, 192) (2×64, 3×64). Encore une fois, afin de simplifier, le positionnement des éléments, vous configurerez l'origine de chaque bloc d'image créé dans le coin supérieur gauche.

Pour faciliter la création des groupes de blocs, vous permettrez l'utilisation fluide de la méthode addTiles en retournant l'instance courante.

Vous pourrez alors créer la classe StoneGroup conformément au diagramme précédent, et dont la fonction sera de produire un groupe spécialisé pour l'affichage de blocs de pierre. La méthode de classe preload devra être invoquée depuis la méthode preload d'une instance de scène pour réaliser le chargement de l'image.

Vous devriez maintenant être capable d'afficher plus facilement des groupes de pierre dans le niveau. Mais avant de poursuivre, penchons nous sur un dernier détail. Les plus attentifs auront remarqué que le groupe dont hérite TileGroup provient de l'espace de nom du moteur physique Arcade. Les blocs d'image créés ont donc déjà un corps physique, pour les visualisés, vous pouvez activer le débogage dans la configuration du moteur physique, lors de la configuration du jeu.

Vous devez probablement constater un décalage entre les images et la boîte englobante de leur corps physiques. Ceci est dû au fait que vous avait modifié l'origine des images après leur création et que Phaser ne met pas à jour les corps physiques. Pour résoudre ce problème, vous devez rafraîchir le corps physique des blocs d'image.

Travail à réaliser
  • Charger l'image d'un bloc de pierre.
  • Afficher des blocs de pierre.
  • Création des classes TileGroup et StoneGroup.
  • Activation du débogage du moteur physique.
  • Mise à jour des corps physiques.

Création du premier niveau

Maintenant que le groupe de blocs de pierre est fonctionnel, de manière similaire, vous créerez un groupe de blocs de lave : LavaGroup, utilisant l'image « lava.png ».

Vous pourrez enfin créer le premier niveau du jeu afin qu'il corresponde au schéma suivant :

Schéma du premier niveau
Schéma du premier niveau
Travail à réaliser
  • Création de la classe LavaGroup.
  • Création du premier niveau.

Ajout du joueur

Comme précédemment, vous commencerez par ajouter l'image du joueur à votre projet « player.png »

Vous allez créer la classe Player et l'employer dans la classe Level1 conformément au diagramme suivant :

Player diagramme
Diagram class of Player

Vous commencerez par créer la classe Player avec uniquement sa propriété de classe, son constructeur et la méthode de classe preload. L'image du joueur (player.png) est en réalité un dictionnaire d'image du joueur, chaque vignette faisant la même taille, 40×40. Pour charger ce type d'image, vous utiliserez la méthode spritesheet. Pour la configuration des vignettes (frameConfig), vous ne fournirez que la largeur et la hauteur. Dans le constructeur, vous prendrez soin de redéfinir l'origine de l'image en haut à gauche et d'ajouter au joueur un corps physique.

Une fois créée, vous utiliserez la classe pour ajouter un joueur dans à la case (1, 7) du premier niveau. Vous constaterez que le joueur reste suspendu en l'air, vous ajouterez donc une gravité de 1000. Et pour empêcher le joueur de traverser les plateformes de pierre, vous ajouterez un gestionnaire de collisions entre le joueur et le groupe des blocs de pierre.

Maintenant, le joueur devrait tomber au sol, et ne plus bouger. Vous allez remedier à celà en ajoutant un peu d'interaction. Vous commencerez par ajouter les méthodes #move, moveRight, moveLeft et halt à la classe Player.

  • #move permet de déplacer le joueur latéralement. vous utiliserez un tween permettant d'animer l'abscisse de la vélocité du corps physique. L'interpolation poura être cubique, de 0 à la valeur passée en paramètre sur une durée de 300ms.
  • moveRight et moveleft utilise #move pour animer l'abscisse de la vélocité du joueur à 300 et -300 respectivement.
  • Enfin, halt permet d'arrêter le joueur en retournant l'abscisse de la vélocité du joueur à 0. Vous utiliserez un tween pour l'animation. L'interpolation pourra être cubique sur une durée de 300ms.

Pour utiliser ces méthodes, vous ajouterez les méthodes #handleInput et #handleMove à la classe Level1.

  • La méthode #handleInput permet essentiellement de structurer le code. Elle a pour fonction de regrouper tous les gestionnaires d'événements d'interaction. Dans votre cas, vous enregistrerez des gestionnaires d'événements pour l'appui et la libération des touches gauche et droite, ces gestionnaires invoqueront la méthode #handleMove lors de chaque événement.
  • la méthode #handleMove permet de gérer le mouvement latéral. Le joueur est arrêté si les deux touches sont pressées ou relachées. Le joueur se déplace à droite si la touche droite est pressée toute seule et à gauche si la touche gauche est pressée toute seule.

Les animations fonctionnent bien, car les durées de mouvement et d'arrêt sont les mêmes. Cependant si l'une de ces valeurs est modifiées, vous pourrez constater des erreurs de fonctionnement. Pour pallier à ces erreurs, vous enregistrerez les tweens que vous créez dans une propriété d'instance et vous pourrez ainsi supprimer le précédent avant d'en créer un nouveau.

Pour finir, sur les mêmes bases, vous ajouterez la gestion du saut au joueur. Afin d'empêcher le joueur de s'envoler en appuyant plusieurs fois sur la touche de saut, vous contrôlerez que le joueur touche le sol avant de réaliser le saut.

Travail à réaliser
  • Charger l'image du joueur.
  • Création de la classe Player.
  • Ajout et gestion de la gravité.
  • Ajout du mouvement du joueur.

Gestion de la caméra

Vous avez dû remarquer que vous ne pouviez pas voir l'ensemble de la scène, pour résoudre ce problème, vous allez configurer la caméra afin qu'elle se déplace en suivant les déplacements du joueur. Dans la scène, vous activerez le suivi du joueur par la caméra principale du jeu, à l'aide de la méthode startFollow.

Suite à cette modification, vous constaterez que la caméra étant centrée sur le joueur, l'affichage en bord de scène affiche une grande zone vide. Pour revenir à un comportement plus usuel, où le jeu n'affiche que la scène, vous allez limiter la zone accessible à la caméra. Vous commencerez par ajouter une méthode cachée #setBounds à la scène qui recevra en paramètres les bornes de la scène, pour ce premier niveau, il s'agit d'une zone de 1472×640 (23*64 et 10*64) pixels dont le coin supérieur gauche est en (0,0). Et dans cette méthode, vous pourrez fixer les limites de déplacement de la caméra principale à l'aide de la méthode setBounds.

Pour finir, vous noterez que le joueur peut sortir de la zone de jeu, ce qui n'est pas pratique pour le joueur. Dans son constructeur, vous allez limiter le déplacement du joueur aux dimensions du monde à l'aide de la méthode setCollideWorldBounds. Vous devriez constater en vous déplaçant vers la gauche de la scène que le joueur est limité aux dimensions initiales de la zone de jeu. Vous mettrez donc à jour les dimensions du monde pour qu'il corresponde à la scène à l'aide de la méthode setbounds dans la mèthode #setBounds.

Le joueur devrait maintenant, expérimenté une expérience de jeu plus conventionnelle.

Travail à réaliser
  • Faire suivre le joueur par la caméra principale du jeu.
  • Fixer les limites de la caméra et du monde à la taille de la scène.
  • Limiter les déplacements du joueur aux limites du monde.

Gestion des animations du joueur

Vous devez vous souvenir que l'image que vous avez chargé pour le joueur était un dictionnaire d'images et que pour l'instant le joueur est un peu rigide lors de ces déplacements.

Dans la classe Joueur, vous ajouterez une nouvelle méthode privée #createAnims. Cette méthode nour permet surtout d'organiser le code en regroupant la création des animations en un même endroit, mais son invocation sera réalisée uniquement dans le constructeur.

Dans un premier temps, la méthode #createAnims, ajoutera deux animations à notre joueur à l'aide de la méthode create du gestionnaire d'animations du Joueur :

  • L'animation « stand » ne contiendra que la vignette 0 et servira à afficher le joueur en position statique.
  • L'animation « right » contiendra les vignettes de 1 à 2, se répétera à l'infini à une fréquence de 20 images secondes, et servira à animer un déplacement vers la droite.

Pour des exemples de création d'animations, vous pouvez vous référer à la documentation.

Vous pourrez ensuite utiliser ces animations lors du déplacement du joueur dans les méthodes moveRight, moveLeft et halt. Pour le déplacement à gauche, vous utiliserez l'animation de gauche en retournant votre Sprite à l'aide de sa méthode setFlipX.

Travail à réaliser
  • Créer les animations « stand » et « right » du joueur.
  • Ajout des animations lors du déplacement du joueur.

Ajout d'un arrière-plan

Dans cette section, vous allez ajouter un arrière-plan à votre scène. Cependant, pour l'instant, vous ne pouvez ajouter qu'une image recouvrant toute la surface de jeu ou créer et positionner vous-même plusieurs images afin qu'elles recouvrent la surface de jeu. Vous allez utiliser un nouveau type d'image permettant de paver automatiquement une surface.

Vous commencerez par ajouter à votre projet l'image employée recouvrir l'arrière-plan : « bg.png ».

Vous allez maintenant pouvoir ajouter une image de type TileSprite à votre scène, afin qu'elle occupe toute la zone de jeu. Vous penserez à désactiver le défilement afin que cette image reste toujours à l'écran.

Nous avons maintenant un arrière-plan que nous avons pu remplir avec une toute petite image. Cependant, cette image ne bouge pas lorsque la caméra suit le joueur, ce qui n'est pas convenable dans de nombreux cas. Pour résoudre ce problème conformément à la documentation de la classe, vous utiliserez la méthode setTilePosition conjointement aux propriétés scrollX et scrollY de la camera principale lors de l'événement « followupdate » de la caméra principale.

Vous avez maintenant, un arrière-plan fonctionnel, mais il est courant d'avoir plusieurs images pour l'arrière-plan. Afin de se simplifier la gestion, vous allez réorganiser votre code en créant une classe Background héritant de la classe Phaser.Physics.Arcade.StaticGroup. Dans un premier temps, comme précédemment, vous y déplacerez votre code concernant l'arrière-plan en le répartissant dans le constructeur et une méthode de classe preload

Une fois votre projet de nouveau fonctionnel, vous ajouterez les images « dust1.png » et « dust2.png » à votre projet.

Vous pourrez ensuite ajouter la variable suivante à votre module JavaScript contenant la classe Background :

Vous pourrez parcourir ce tableau pour ajouter les images à votre arrière-plan. Vous utiliserez les urls comme clés des images chargées et en bouclant sur les clés du tableau, vous ajouterez les images à votre arrière-plan.

Dans notre cas, nous utilisons plusieurs images pour gommer un peu l'aspect répétitif de la texture de fond, vous allez donc décaler les images de manière similaire lors du déplacement de la caméra, mais sur le même principe, il serait facile de mettre en place un effet de défilement parallaxe en faisant varier le décalage de chaque image.

Travail à réaliser
  • Ajouter au projet les images nécessaires à l'affichage de l'arrière-plan.
  • Afficher un arrière-plan sur toute la zone de jeu, défilant avec le déplacement de la caméra.
  • Créer la classe Background.
  • Gérer un arrière-plan comportant plusieurs images.

Mort du joueur

Pour illustrer la mort du joueur, vous allez utiliser un système de particules. Ceux-ci utilisent une image pour afficher les particules, vous commencerez par ajouter l'image « particle.png » à votre projet et vous ajouterez son chargement dans la classe Player.

Vous pourrez ensuite ajouter une méthode death au joueur qui provoquera la mort du joueur lors de son invocation.

Cette méthode commencera par créer un système de particules centré sur le joueur avec la configuration suivante :

Afin d'empêcher d'autre collision avec l'environnement, vous allez

Vous pourrez faire ensuite produire une explosion d'une vingtaine de particles et cacher le joueur.

Vous provoquerez le redémarrage de la scène à l'aide de la méthode restart du gestionnaire de scène. Cependant, pour que l'animation de l'explosion soit visible, vous différerez le redémarrage de la scène ainsi que la réactualisation du clavier.

Pour finir, vous utiliserez cette méthode lors de la collision du joueur avec un bloc de lave. Vous devriez maintenant voir mourir votre joueur lorsqu'il tombe dans la lave et la scène devrait redémarrer.

Travail à réaliser
  • Ajouter au projet de l'image de particule.
  • Désactiver le joueur et provoquer une explosion lors de la mort du joueur.
  • Invoquer la mort du joueur lors de la collision du joueur et d'un bloc de lave.

Ascenseur et changement de niveau - partie 1

Vous allez ajouter un ascenseur permettant de changer de scène. Pour cela, vous allez créer une classe Elevator héritant de Phaser.Physics.Arcade.StaticGroup. La classe proposera le chargement de la bibliothèque d'image « elevator.png », contenant des vignettes de 64×64, et le constructeur ajoutera au groupe un sprite que vous stockerez dans une propriété d'instance back.

Vous devriez maintenant être capable d'afficher l'ascenseur dans la scène à la case (21, 7) de la scène.

Vous ajouterez une méthode privée #createAnims que vous invoquerez dans le constructeur et qui créera les animations. Pour le moment, cette méthode créera une animation « open », comportant les vignettes de 0 à 3 à une vitesse de défilement de 20.

Vous allez maintenant pouvoir ajouter la méthode moveIn(player) à la classe Elevator permettant de faire rentrer le joueur dans l'ascenseur.

Vous commencerez par désactiver le clavier et invoquer la méthode halt du joueur, puis vous provoquerez l'ouverture de l'ascenseur à l'aide de l'animation « open ».

Vous déplacerez ensuite le joueur au centre de l'ascenseur à l'aide d'un « tween ». Puis en différent l'invocation afin que l'animation soit finie, vous réactiverez le clavier et redémarrerez la scène.

Pour activer l'ascenseur, vous ajouterez un nouveau gestionnaire de touche de clavier pour l'interaction du joueur avec la touche bas. Le gestionnaire d'événement invoquera la méthode privée #handleInteract que vous ajouterez à la scène du premier niveau. Cette méthode utilisera la méthode overlap du moteur physique (this.physics.world) pour tester si le joueur est sur l'ascenseur en même temps qu'il est au sol et dans ce cas, invoquera la méthode moveIn.

Travail à réaliser
  • Ajouter au projet de la bibliothèque d'images de l'ascenseur.
  • Créer de la classe Elevator.
  • Créer l'animation et la méthode permettant de faire monter le joueur dans l'ascenseur.
  • Ajouter une touche permettant au joueur d'intéragir avec l'environnement et gérer l'intéraction avec l'ascenseur.

Ajout de calques

L'animation est fonctionnelle, mais pour refermer les porte de l'ascenseur, il faudrait les afficher devant le joueur. Il serait possible de jouer avec l'indice de profondeur (z-index), mais cette solution demande un peu de rigueur pour être cohérente dans le temps. Une autre solution consiste à créer des calques afin de gérer plusieurs niveaux de profondeur.

Mais avant d'ajouter des calques à votre projet, vous allez réorganiser votre code afin de simplifier la création des niveaux. Vous commencerez par créer une classe Level héritant de la classe Phaser.Scene. Cette classe va comporter ce qui concerne le joueur, la caméra et l'interaction du joueur. Elle contiendra :

  • Une méthode preload qui chargera les images du joueur.
  • Les méthodes privées de Level1 (#handleInput, #handleMove, #handleJump, #handleInteract et #setBounds(levelBound)).
  • Une méthode initScene(xPlayer, yPlayer, levelBound) qui créera le joueur à la position (xPlayer, yPlayer) et initialisera la caméra pour suivre le joueur. Elle invoquera aussi les méthodes #handleInput et #setBounds(levelBound).

De plus, la méthode #handleInteract impose que vous ayez accès à l'ascenseur de la scène. Cependant, pour plus de généricité, vous ajouterez une propriété elevators à la classe Level qui contiendra un tableau vide par défaut. C'est cette propriété qui contiendra les ascenseurs de la scène, que vous testerez dans la méthode #handleInteract.

Vous ferez ensuite hériter la classe Level1 de la classe Level. Vous penserez à invoquer la méthode preload du parent dans sa méthode preload et la méthode initScene dans sa méthode create. Vous penserez à ajouter l'ascenseur au tableau d'ascenseur de Level.

Vous devriez avoir de nouveau un projet fonctionnel, à l'exception près, qu'en fonction de la position de l'invocation de la méthode initScene, le joueur peut être créé derrière l'arrière-plan.

Pour résoudre ce problème, vous allez ajouter un ensemble de calques à votre classe Level :

Vous ajouterez le joueur au calque « player ».

Dans la classe Level1, vous ajouterez les différents éléments du jeu dans leur calque. Background ira dans le calque « bg » et tout le reste dans le calque « back ».

Pour les groupes, vous ne pouvez pas directement les ajouter au calque, vous devez ajouter le tableau de ces éléments : .children.entries.

Pour l'ascenseur, vous ajouterez dans le calque « back directement l'image de fond de l'ascenseur (back).

Travail à réaliser
  • Créer de la classe Level.
  • Réorganiser la classe Level1 pour hériter de Level.
  • Ajouter des calques à la classe Level.

Ascenseur et changement de niveau - partie 2

Maintenant que vous votre scène est organisée en calque, dans le constructeur de la classe Elevator vous allez pouvoir ajouter un sprite qui sera affiché devant le joueur. Vous le stoquerez dans une propriété d'instance front.

Dans la scène, vous pourrez accéder aux propriétés front et back de l'ascenseur pour ajouter les sprites aux calques correspondants.

Enfin, vous créerez une nouvelle animation « open » pour ce nouveau sprite, réalisant l'animation des vignettes 4 à 7 de la bibliothèque d'images.

Puis, dans la méthode moveIn, lors de la réalisation du déplacement du joueur au centre de l'ascenseur onComplete, vous pourrez jouer l'animation de fermeture des portes à l'aide de la méthode playReverse.

Maintenant que l'animation fonctionne correctement, vous allez pouvoir provoquer le changement de scène. Dans ce but, le constructeur de la classe Elevatorrecevra un nouveau paramètre contenant la clé de la scène vers laquelle emmène l'ascenseur. Ce paramètre sera sauvegarder dans une propriété d'instance to, pour pouvoir être utilisée dans la méthode moveIn à la fin des animations pour démarrer la nouvelle scène plutôt que de redémarrer la scène actuelle.

Pour que votre ascenseur soit fonctionnel, vous créerez une classe Level2 représentant votre deuxième niveau. Pour l'instant celui-ci se conformera au schèma suivant :

Schéma provisoire du second niveau
Schéma provisoire du second niveau

Aprés avoir ajouté la scène au jeu, vous ajouterez la destination de l'ascenseur du niveau 1 afin qu'il emmène au niveau 2.

Maintenant, votre joueur doit parvenir à passer du niveau 1 au niveau 2. Cependant, l'animation est un peu décevante. Afin d'y remédier, vous allez transmettre l'information de la provenance du joueur lors du démarrage de la scène. Dans la méthode moveIn de la classe Elevator, vous passerez en deuxième paramètre de la méthode start un object JavaScript comportant une propriété from avec comme valeur la clé de scène comportant l'ascenseur. Cet objet JavaScript sera transmis, lors du démarrage de scène, en paramètre de la méthode créate de la scène démarrée. Vous pourrez remplir (set) les données de la scène avec celui-ci.

Vous ajouterez ensuite une méthode moveOut(player) à la classe Elevator permettant de faire sortir le joueur de l'ascenseur.

Vous commencerez par désactiver le clavier et invoquer la méthode halt du joueur, placer le joueur au centre de l'ascenseur. Vous afficherez, à l'aide de la méthode setTexture, le sprite arrière comme ouvert (vignette 3) et le sprite avant comme fermé (vignette 4).

Vous allez provoquer l'ouverture de l'ascenseur à l'aide de l'animation « open » sur le sprite avant, mais avant, vous allez enregistrer un gestionnaire d'événement unique réagissant à la fin de l'animation du sprite avant ("animationcomplete"). Dans la fonction de rappel, vous réactiverez la gestion du clavier et vous provoquerez la fermeture du sprite arrière.

Pour finir, lors de la création des scènes, vous pourrez déclencher la sortie de l'ascenseur en fonction de la provenance de l'utilisateur que vous obtiendrez à l'aide de la méthode get du gestionnaire de données de la scène.

Travail à réaliser
  • Ajouter un sprite affichant l'ascenseur sur le calque avant.
  • Gérer la transition entre les scènes.
  • Ajouter la méthode moveOut(player) à la classe Elevator.
  • Gérer l'affichage de la sortie d'ascenseur lors des transitions entre les scènes.

Niveau 2 et écran de fin

Une fois les transitions entre les scènes opérationnelles, vous allez pouvoir créer le deuxième niveau. Dans ce niveau, vous allez mettre en place un nouveau type de blocs, les échelles.

Vous commencerez donc par ajouter une nouvelle image à votre projet : « ladder.png ». Il s'agit de l'image d'un bloc d'échelle.

Comme précédemment, vous créerez une classe LadderGroup pour ajouter des ensembles de blocs à votre scène. Cependant, les plus attentifs constateront que l'image étant de dimensions 40×64, le positionnement des images ne va pas être correct. Pour palier à ce problème, vous ajouterez deux paramètres (offsetX et offsetY) avec une valeur par défaut de 0 au constructeur de TileGroup. Vous initialiserez des propriétés d'instance pour conserver ces valeurs et vous les utiliserez dans la méthode d'instance addTiles pour pouvoir décaler la position de chaque image lors de la création par lot. Cette modification ne devrait pas avoir d'impact sur votre projet. Vous pourrez ensuite créer la classe LadderGroup pour afficher des groupes d'échelles algnés sur les cases de dimensions 64×64.

Vous pouvez maintenant mettre à jour votre classe Level2 pour que le deuxième niveau corresponde au schèma suivant :

Schéma provisoire du second niveau
Schéma du second niveau

En vous inspirant de la scène Title, vous ajouterez une nouvelle scène à votre jeu indiquant que le jeu est fini, vous penserez à l'ajouter à votre instance de jeu.

Travail à réaliser
  • Ajouter au projet de l'image des échelles.
  • Ajout de paramètres de décalage pour les groupes d'images.
  • Création de la classe LadderGroup pour gérer les groupes d'échelles.
  • Construction du deuxième niveau.
  • Création de la scène de fin.

Gestion des échelles

Pour pouvoir monter ou déscendre des échelles, vous ajouterez la gestion des touches haut et bas. Les touches haut et bas peuvent déjà être affectées, mais ici, nous parlons de l'action du joueur, de monter ou descendre. Le fait de décorréler l'action du joueur des touches dans votre code vous offre la souplesse de pouvoir changer d'avis sur les touches associées aux actions, voir de laisser votre joueur les configurer.

Ensuite, vous commencerez par ajouter trois méthodes d'instance : climbUp(ladders), climbDown(ladders) et climbStop à la classe Player. Puis, vous ajouterez la gestion des événements pour l'activation et la libération des touches associées à la montée et à la descente. Ils ne seront effectifs que lorsque le joueur est en contact avec une des échelles, vous utiliserez la méthode overlape pour tester la superposition des objets. Dans le cas où il y a superposition :

  • Si les touches monter et descendre sont activées ou relâchées, le joueur cesse de monter/descendre.
  • Sinon si la touche monter est activée, le joueur monte.
  • Sinon, le joueur descend.

Dans la classe Player, pour les méthodes climbUp(ladders) et climbDown(ladders), le paramètre ladder correspond au groupe d'échelles avec lesquelles interagit le joueur. À l'exception de la direction, dans ces méthodes, vous devez :

  • désactiver la gravité (setAllowGravity).
  • immobiliser le joueur (stop).
  • mémoriser dans une propriété d'instance le groupe d'échelles sur lesquelles est le joueur.
  • animer la vitesse en y du joueur à l'aide d'un tween de 0 à 200 ou -200 sur 300ms par exemple.

Dans la méthode climbStop, si un groupe d'échelles est mémorisé, vous animerez un retour à 0 de la vitesse en y du joueur à l'aide d'un tween, sur 50ms par exemple.

Vous devriez maintenant pouvoir monter et descendre aux échelles, mais le jeu ne réalise pas lorsque vous les quittées. Pour résoudre ce problème, vous ajouterez une méthode preUpdate à la classe Player pour vérifier si le joueur quitte l'échelle sur laquelle il se trouve. Si un groupe d'échelles est mémorisé, vous testerez si le joueur est toujours dessus à l'aide de la méthode overlape et sinon, vous réactiverez la gravité pour le joueur et vous oublierez le groupe d'échelles.

Travail à réaliser
  • Ajouter le support de l'interaction pour monter ou descendre.
  • Création des méthodes indiquant au joueur qu'il monte, descend ou fait une pause.
  • Gestion de quand le joueur quitte une échelle.
A. Jonquet DUT-INFO/REIMS