Mon premier jeu en Rust et Amethyst — Partie 2

Vous avez lu la première partie (https://medium.com/@nunes.nelson4/mon-premier-jeu-en-rust-et-amethyst-27707364f2d3), cool !
On démarre dans la partie deux en se focalisant sur deux aspects tres importants d’Amethyst: les états et les systèmes.
Le code complet est disponible ici: https://github.com/Abacaxi-Nelson/game_part_2
Les états
Au sein de l’application, vous trouverez deux états : Gameplay et Winner. Gameplay permet au joueur de joueur, choisir ses mains, et valider cette dernière; alors que l’état Winner indique au joueur s’il a gagner ou perdu cette manche.
Pourquoi deux états ? c’est pas flagrant dans ce jeu, mais de manière générale, il est plus pratique d’organiser son code en petit morceaux, ce qui permet de rendre le code plus testable, plus lisible.
Un Etat a un cycle de vie (on_start, on_resume etc..). Il est possible de passer d’un état a un autres via des transitions ( remplacer un état existant, ou l’ajouter a ce dernier (et pouvoir finalement revenir en arrière via pop)).
Voici un focus sur un des états. La méthode “update” est appelée en continu à chaque affichage. Cette dernière écoute une ressources “game” (qui est partagée avec les systèmes que nous verrons ensuite), et son champs “user_action”. Ensuite une transition est demandé vers un état “WinnerState” qui lui a aussi son cycle de vie (on_start, on_update etc…).
Les systèmes
Le jeu est piloté via le clavier du joueurs, des touches sont bindés avec d’en déduire des actions de jeu :
On indique ici les touches que l’on souhaite surveiller, et un libellé associé (utile dans les systèmes).
Au même titre que les états, il est possible d’avoir de multiple systèmes tournant en parallèle. Pour rappel un système est un moteur ou se concentre la logique applicative, qui est rejoué à l’infini afin de récolter en temps réels les inputs utilisateurs, les évènement, mettre à jour les tiles.
Tout commence, par la déclaration d’un SystemData; ou l’on décrit quelles sont les composants, ressources, moteurs que l’on souhaite manipuler au sein du système:
- WriteStorage<’s, Player>: Ouvre accès a tous les joueurs du conteneur “World”
- Read<’s, InputHandler<StringBindings>>: donne accès aux touches utilisé par le joueur
- ReadExpect<’s, SpriteSheetStorage>: Nous avons vu precedemment ce composant “SpriteSheetStorage”, responsable de fournir les tiles graphique
- Write<’s, Game>: donne acces à la ressource “Game”, que nous avons vu precedemment (qui contient notamment User_Action)
- Etc.
La boucle “for” sera appelé indéfiniment. Elle se base sur :
- WriteStorage<’s, Player>: qui contient tous les “players” du conteneur world
- Entities<’s>: qui contient l’ensemble des entités de world (ie tous les composants: Players, Score etc..)
Le “join” permet de ne sélectionner que les composants de type “players”, mais aussi sa référence associé (son entity finalement).
On instancie aussi tous les booleens qui passe a “true” lorsque la touche est détectée.
Enfin, on sépare le code en deux. Une partie qui concerne le vraie joueur (Toi) d’une autre qui concerne son adversaire; en effet il faut pouvoir mettre à jour les tiles en ciblant la bonne entité a mettre à jour (ie lorsque le joueur appuie sur la touche Up, il faut mettre à jour sa tile, et non celle de son adversaire, même chose pour le score etc…).
Les premières lignes concernent la mise à jour des tiles (en fonction des touches du joueur): “sprites.insert” permet de mettre à jour la tile de l’entité.
On retrouve la ressource “game” et le champs “user_action” qui permet d’indiquer a l’état courant d’un changement.
Coté adversaire, on va réagir lorsque le joueur valide sa main : UserAction::ValidateHand; on génère alors une nouvelle main, calculons le vainqueur, et indiquons au state courant un changement:
game.user_action = UserAction::ShowWinner;
Voila !