🚧 Attention, peinture fraîche !
Cette page a été traduite par une seule personne et n'a pas été relue et vérifiée par quelqu'un d'autre ! Les informations peuvent par exemple être erronées, être formulées maladroitement, ou contenir d'autres types de fautes.
Ajouter de l'interactivité
Nous allons continuer à explorer l'interfaçage entre le JavaScript et le WebAssembly en ajouter des fonctionnalités d'interaction avec notre implémentation du jeu de la vie. Nous allons permettre aux utilisateurs de changer le statut vivant ou mort d'une cellule en cliquant dessus, et aussi leur permettre de mettre en pause le jeu, ce qui va faciliter le dessin de certains schémas.
Mettre en pause et reprendre le jeu
Ajoutons un bouton pour changer si le jeu est en cours de lecture ou en pause.
Dans wasm-jeu-de-la-vie/www/index.html
, ajoutez le bouton juste au-dessus le
<canvas>
:
<button id="lecture-pause"></button>
Dans le JavaScript wasm-game-of-life/www/index.js
, nous allons faire les
changements suivants :
- Conserver l'identifiant retourné par le dernier appel à
requestAnimationFrame
, pour que nous puissions annuler l'animation en faisant appel àcancelAnimationFrame
avec cet identifiant.
- Lorsque le bouton lecture/pause sera actionné, on va regarder si nous avons un
identifiant pour un calcul d'une génération de l'animation. Si c'est le cas,
alors le jeu est actuellement en cours de lecture, et cela veut donc dire que
nous souhaitons annuler le calcul pour éviter que
boucleDeRendu
soit appelé à nouveau, ce qui aura pour effet de mettre en pause le jeu. Si nous n'avons pas d'identifiant pour un calcul d'une génération de l'animation, alors cela veut dire que nous sommes actuellement en pause, et nous devons faire appel àrequestAnimationFrame
pour reprendre le jeu.
Comme le JavaScript pilote le Rust via le WebAssembly, c'est tout ce que nous avons besoin de faire, et nous n'avons pas besoin de changer le code source Rust.
Nous ajoutons la variable animationId
pour conserver l'identifiant retourné
par requestAnimationFrame
. Lorsqu'il n'y a pas de calcul d'une génération de
l'animation, nous donnons assignons la valeur null
à cette variable.
let animationId = null;
// Cette fonction est la même qu'avant, sauf que le résultat de
// `requestAnimationFrame` est assigné à `animationId`.
const boucleDeRendu = () => {
dessinerGrille();
dessinerCellules();
univers.tick();
animationId = requestAnimationFrame(boucleDeRendu);
};
A n'importe quel moment, nous pouvons savoir si le jeu est en pause ou non en
vérifiant la valeur de animationId
:
const estEnPause = () => {
return animationId === null;
};
Maintenant lorsque le bouton lecture/pause est cliqué, nous vérifions si le jeu
est en pause ou en lecture, et respectivement nous reprenons l'animation
boucleDeRendu
ou nous arrêtons le calcul de la prochaine génération de
l'animation. De plus, nous allons changer le texte du bouton pour refléter
l'action que le bouton va faire lors du prochain clic.
const boutonLecturePause = document.getElementById("lecture-pause");
const lecture = () => {
boutonLecturePause.textContent = "⏸";
boucleDeRendu();
};
const pause = () => {
boutonLecturePause.textContent = "▶";
cancelAnimationFrame(animationId);
animationId = null;
};
boutonLecturePause.addEventListener("click", event => {
if (estEnPause()) {
lecture();
} else {
pause();
}
});
Enfin, jusqu'ici nous avons démarré le jeu et son animation en faisant
directement appel à requestAnimationFrame(boucleDeRendu)
, mais nous voulons
remplacer cela par l'appel à lecture
pour que le bouton ait la bonne icone
initiale.
// On utilise cela à la place de `requestAnimationFrame(boucleDeRendu)`.
lecture();
Rafraîchissez http://localhost:8080/ et vous devriez maintenant mettre en pause et reprendre le jeu en cliquant sur le bouton !
Changer l'état d'une cellule avec un évènement "click"
Maintenant que nous pouvons mettre le jeu en pause, nous pouvons ajouter la possibilité de muter les cellules en cliquant sur elles.
Pour basculer le statut d'une cellule de vivante à morte, ou de morte à vivante.
Ajoutez une méthode basculer
à Cellule
dans
wasm-jeu-de-la-vie/src/lib.rs
:
#![allow(unused)] fn main() { impl Cellule { fn basculer(&mut self) { *self = match *self { Cellule::Morte => Cellule::Vivante, Cellule::Vivante => Cellule::Morte, }; } } }
Pour basculer l'état d'une cellule à une ligne et colonne donnée, nous
traduisons le couple ligne et colonne en indice du vecteur de toutes les
cellules et nous faisons appel à la méthode basculer
sur la cellule à cet
indice :
#![allow(unused)] fn main() { /// Méthodes publiques, exportées en JavaScript. #[wasm_bindgen] impl Univers { // ... pub fn basculer_cellule(&mut self, ligne: u32, colonne: u32) { let indice = self.calculer_indice(ligne, colonne); self.cellules[indice].basculer(); } } }
Cette méthode est définie dans le bloc impl
qui est annoté avec
#[wasm_bingen]
afin qu'il puisse être appelé en JavaScript.
Dans wasm-jeu-de-la-vie/www/index.js
, nous écoutons les évènements de clics
sur le noeud <canvas>
, puis nous traduisons les coordonnées du clic dans le
contexte de la page en coordonnées dans le canvas, et ensuite ces coordonnées
en ligne et colonne, puis nous évoquons la méthode basculer_cellule
, et enfin
nous redessinons la scène.
canvas.addEventListener("click", evenement => {
const zoneRectangulaire = canvas.getBoundingClientRect();
const echelleX = canvas.width / zoneRectangulaire.width;
const echelleY = canvas.height / zoneRectangulaire.height;
const distanceGaucheDuCanvas = (evenement.clientX - zoneRectangulaire.left) * echelleX;
const distanceHautDuCanvas = (evenement.clientY - zoneRectangulaire.top) * echelleY;
const ligne = Math.min(Math.floor(distanceHautDuCanvas / (CELL_SIZE + 1)), hauteur - 1);
const colonne = Math.min(Math.floor(distanceGaucheDuCanvas / (CELL_SIZE + 1)), largeur - 1);
univers.basculer_cellule(ligne, colonne);
dessinerGrille();
dessinerCellules();
});
Recompilez avec wasm-pack build
dans le dossier wasm-jeu-de-la-vie
, ensuite
rafraîchissez à nouveau la page http://localhost:8080/
et vous devriez pouvoir dessiner vos propres schémas en cliquant sur les
cellules pour pouvoir changer leur état.
Exercices
- Ajouter un composant
<input type="range">
pour pouvoir régler combien de ticks se produisent à chaque séquence de l'animation.
- Ajouter un bouton qui réinitialise l'univers dans un état initial aléatoire lorsqu'on clique dessus. Et un autre bouton qui réinitialise l'univers avec uniquement des cellules mortes.
- Lors d'un
Ctrl + Clic
, insérez un planeur centré sur la cellule cible. Lors d'unShift + Clic
, insérez un pulsar.