enchant.js - 4e partie
Rédigé par Fred - - Aucun commentaireEnfin, on va pouvoir faire des jeux interactifs ! On va continuer de voir un peu des choses sur les sprites mais surtout, on va voir les événements utiles à la souris et au clavier.
L’utilisation du clavier
On a déjà vu l’événement qu’il faut utiliser lorsque l’on entre dans une frame. Il faut faire un
player.addEventListener('enterframe', function(){ //code here }
Quand on appuie sur une touche, par exemple la flèche droite, il y a
en fait deux événements : quand on commence à appuyer sur la touche,
et quand on la relâche. Deux événements sont donc émis qui peuvent
nous intéresser : RIGHT_BUTTON_DOWN
et RIGHT_BUTTON_UP
.
Dans le premier exemple, on lie l’action d’appuyer/relâcher une flèche avec l’affichage dans un label de l’événement qui s’est produit.
game.rootScene.addEventListener(direction + 'buttondown', function(){ status.add(d + ' pushed'); })
Dans le second exemple, on lie l’action à un décalage d’un sprite. Si
on ajoute le changement de frame tant qu’on laisse la touche enfoncée,
on a un ours qui marche. On utilise également l’attribut .scaleX
que
l’on valorise à 1 ou -1 suivant le sens de la marche, et on revient à
la frame 0 lorsque l’on relâche la touche.
game.rootScene.addEventListener('leftbuttondown', function(){ player.x -= 5; player.frame = (player.frame+1) %3; player.scaleX = -1; }); game.rootScene.addEventListener('leftbuttonup', function(){ player.frame = 0; });
Dans ces deux exemples, on ajoute un écouteur sur la rootScene
pour les événements d’appui de touche. On peut également rester sur
l’écoute de l’entrée dans une frame et tester si une touche est
enfoncée avec game.input
. Cet objet correspond en fait à un
dictionnaire du type {left: true, up: false, right: false, down:
false}
.
L’exemple 3 reprend le code du second exemple, en utilisant
game.input
:
game.rootScene.addEventListener('enterframe', function(){ if (game.input.left) { player.x -= 5; player.frame = (player.frame+1) %3; player.scaleX = -1; } //... }
Noter la différence entre les deux exemples si on appuie sur droite et gauche en même temps. Pour contourner le problème, on peut utiliser :
if (game.input.left && !game.input.right) {
Comment faire en revanche pour les autres touches ? On peut soit
construire à la main des fonctions qui émettent le bon événement, soit
utiliser la méthode game.keybind(key, button)
.
Dans tous les cas, il faut connaître le code ASCII de la touche sur laquelle
on veut appuyer. Par exemple, si on veut pouvoir appuyer sur m pour
afficher un menu, on peut utiliser ='M'.charCodeAt(0)= qui
nous retourne 77. Ou alors, une recherche sur duckduckgo, et on trouve
que le +
du clavier numérique a comme code 107. Après avoir fait
game.keybind(77, 'm'); game.keybind(107, 'plus');
on peut écouter quatre nouveaux événements : mbuttondown/mbuttonup
et plusbuttondown/plusbuttonup
. game.input
est également enrichi
avec m
et plus
qui sont valorisés à vrai/faux.
Dans l’exemple 4, deux méthodes sont utilisées pour gérer les touches.
Dans des addEventListener('enterframe',…
on teste
game.input.m/game.input.q
pour afficher/quitter le menu. Une fois ce
menu affiché, on peut appuyer sur le +/-
du clavier numérique pour
augmenter/diminuer la vitesse de déplacement du sprite (la valeur
affichée correspond à combien de pixels le sprite se déplace). Pour
gérer le +
, on utilise
menuScene.addEventListener('plusbuttondown',…
: à chaque appui sur
la touche, le compteur de vitesse est incrémenté. Pour le -
, on
teste dans menuScene.addEventListener('enterframe',…
si
game.input.moins
est vrai. Du coup, comme il se passe plusieurs
frames pendant lesquelles la touche est enfoncée, le compteur décroît
trop rapidement.
On peut bien sûr associer plusieurs touches au même bouton :
game.keybind(68, 'right');
permet d’associer la touche d
au bouton
droit. C’est utile si on veut pouvoir utiliser q,s,d,z pour se
déplacer.
L’utilisation de la souris
Utiliser une souris ou toucher l’écran de sa tablette/son smartphone,
c’est géré de la même manière. Et ça n’est pas très différent de
l’appui sur une touche : quand on touche/clique l’écran l’événement
TOUCH_START
est émis. Quand on lâche le bouton de la souris/l’écran,
c’est TOUCH_END
qui émis. La où ça change, c’est qu’on peut déplacer
le curseur/doigt tout en appuyant (l’événement TOUCH_MOVE
est alors
émis) et surtout, il faut récupérer où le curseur est. Pour cela, il
faut passer un argument à fonction exécutée lors du touch
— l’événement — qui a comme attributs le x/y
où a eu lieu
l’événement :
game.rootScene.addEventListener('touchstart', function(event){ player.x = event.x; player.y = event.y; });
Dans l’exemple 5, on met l’écouteur sur touchstart
seulement, et
dans l’exemple 6, sur le touchmove
. Et ça ne fait pas vraiment ce à
quoi on s’attendait : pour utiliser le touchmove
, il faut en effet
que l’écouteur soit sur l’objet que l’on veut déplacer ! L’exemple 7
corrige cela.
Premier « jeu »
Comme promis, on va faire un premier vrai jeu. Pas transcendantal certes, mais un jeu quand même : cliquer sur toutes les boules rouges en évitant les bleues.
On commence par initialiser les variables dont on aura besoin :
var HEIGHT = 320; var WIDTH = 320; var ICON0 = '../images/icon0.png'; var NB_BONUS = 10; var NB_MALUS = 15; var FPS = 24;
Le fait de mettre le chemin de l’image en variable permet de faire
game.preload(ICON0);
puis plus tard this.image =
game.assets[ICON0];
. Et si on veut changer d’image, on n’a qu’un
endroit à modifier.
On ajoute ensuite les boules rouges/bleues dans le game.onload
for (var i = 0; i < NB_BONUS; i++) var player = new Item(19); for (var i = 0; i < NB_MALUS; i++) var player = new Item(20); var lbl_score = new Label(''); game.rootScene.addChild(lbl_score);
ainsi qu’un label qui nous servira à afficher le nombre de boules restantes.
On instancie quelques variables puis on regarde à chaque entrée de frame si :
- on vient d’écouler une seconde (24 frames sont passées depuis la dernière fois)
if (game.frame%FPS===0){
timer--;
}
- on a eu toutes les boules, ou si on n’a plus de temps. Dans ce cas là le jeu doit s’arrêter
if (game.score===0 || timer===0){ game.running = false; }
puis, si le jeu continue, on met à jour le label avec le score, et sinon, on regarde pourquoi le jeu est terminé :
if (game.running){ lbl_score.text = timer+"<br/>Plus que " + game.score; }else{ if (game.score===0){ lbl_score.text = "Bravo !!!"; }else{ lbl_score.text = "Il en restait encore " + game.score; } game.stop(); }
La nouvelle chose par rapport aux exemples qu’on a vu jusqu’à présent,
c’est l’utilisation d’une classe pour créer toutes les boules. Rien de
bien différent, si ce n’est que comme on utilise des objets, il y a
des this
un peu de partout.
Item = enchant.Class.create(Sprite, { initialize: function(whichFrame) { var game = enchant.Game.instance; Sprite.call(this, 16, 16); this.image = game.assets[ICON0]; this.frame = whichFrame; this.x = 16 + rand(WIDTH - 32); this.y = 16 + rand(HEIGHT - 32); this.angle = (10 + rand(70))* Math.PI/180; this.speed = 1 + rand(5); this.vx = this.speed * Math.cos(this.angle); this.vy = this.speed * Math.sin(this.angle);
Quand on touche la boule, Si c’est un bonus, on a utilisé la frame 19, et donc on décrémente le nombre restant, sinon, on arrête le jeu :
this.addEventListener('touchstart', function(){ if (this.frame===19){//bonus game.score--; game.rootScene.removeChild(this); }else{ game.running = false; } });
Et à chaque fois qu’on rentre dans une frame, on effectue le déplacement :
this.addEventListener('enterframe', function() { this.y += this.vy; this.x += this.vx; if (this.y > (HEIGHT-16) || this.y <0) { this.vy = -this.vy; } if (this.x > (WIDTH-16) || this.x <0) { this.vx = -this.vx; } });
Bien entendu, ne pas oublier de rajouter la boule à la rootScene
:
game.rootScene.addChild(this);
.
Une autre façon de faire la classe, de façon un peu plus orienté
objet, est d’utiliser les méthodes ontouchstart
et onenterframe
.
L’exemple 9 reprend le code de l’exemple précédent en changeant la
façon de définir les écouteurs :
ontouchstart: function(){ if (this.frame===19){//bonus this.game.score--; this.game.rootScene.removeChild(this); }else{ this.game.running = false; } },
La prochaine fois
Avec tout ce que l’on vient de voir, vous pouvez déjà faire pas mal de
jeux différents. Dans le tutoriel suivant, on va voir comment faire pour
détecter simplement qu’un sprite est en contact avec un autre. On
pourrait déjà le faire avec tout plein de if
, mais enchantjs permet
de simplifier tout ça.