Déplacer un objet avec les touches du clavier - Vaisseau spatial
Introduction
A la lecture de Starfield multidirectionnel je me suis dit que cela pourrait être bien de décliner le tutoriel dans une autre version, qui proposerait de déplacer un objet sur la scène avec les touches du clavier. Je reprends donc le code de l'autre tutoriel, les images de l'autre tutoriel, et je copie/colle de larges portions de l'excellent texte d'explications de Monsieur Spi, en l'adaptant. Je fais cela pour que cette page puisse être lue sans qu'il soit besoin de lire l'autre, pour les lecteurs qui auraient seulement besoin de déplacer 1 objet sur scène, au lieu de plusieurs. Grand merci à Monsieur Spi pour le tuto d'origine, et à toute l'équipe qui a développée le jeu.
Voici ce que nous allons réaliser:
Utilisez les flèches du clavier pour déplacer le vaisseau.
Commençons par poser les bases
Ce tutoriel s'adresse aux débutants et est écrit sur Flash CS3 en AS2. Le programme est également disponible à la fin de cette page en version AS3.
Les captures d'écran sont prises sur flash CS4, mais vous devriez vous y retrouver. Les sources disponibles sont bien pour la version CS3.
Ce tutoriel s'adresse aux débutants. Je n’expliquerai pas en détail toutes les commandes utilisées, mais je donnerais quand même beaucoup de précisions, sur lesquelles vous pourrez passer si vous êtes plus habitué.
Avant de commencer tout projet il est nécessaire de le définir précisément.
Ici, le but est de mettre un objet en mouvement, avec accélérations et décélérations commandées par les touches fléchées du clavier.
Voyons un peu comment nous pouvons construire cette idée.
Paramètres du projet
Allons-y, ouvrez un nouveau projet et donnez lui les paramètres suivants :
- Largeur = 600 px
- Hauteur = 400 px
- Couleur de fond = noir
- FPS = 30
Création du vaisseau
Avec les outils de dessin de Flash, dessinez un vaisseau spatial. Pour reprendre celui réalisé dans le projet commun, ce sera celui-ci:
Puis sélectionnez l'ensemble du dessin et convertissez-le en symbole de clip (touche F8) que vous nommerez vaisseau:
Veillez à bien choisir comme point d'alignement le point central.
Vous avez maintenant dans la bibliothèque un nouveau symbole, et sur la scène une occurrence de ce symbole. Dans le panneau des propriétés donnez-lui le nom vaisseau, de nouveau. C'est par ce nom d'occurrence que nous pourrons par la suite accéder en Actionscript à cet objet de la scène.
Pour finir déplacez ce vaisseau avec l'outil de déplacement (touche V) pour le placer à peu près au centre de la scène.
Télécharger les sources (CS3).
Faire avancer le vaisseau
Pour commencer nous allons avoir besoin d'exécuter à intervalles réguliers (à la cadence de l'animation, soit 30 fois par secondes) une même suite d'instructions Actionscript qui déplace le vaisseau de quelques pixels. Comme ceci va se faire plusieurs fois par secondes, à la cadence de l'animation (on dit aussi “à chaque image”) cela va donner l'impression que le vaisseau est en mouvement.
Déplacer le vaisseau une fois
Pour déplacer le vaisseau de quelques pixels, nous allons modifier ses coordonnées. Pour rappel sur les coordonnées voyez le paragraphe "Repère" de cet article.
Allez sur votre timeline, normalement vous n’avez qu’un calque avec une image. Créez un nouveau calque et nommez-le Actions.
C’est là que nous allons placer tout notre code. Cliquez dessus, affichez le panneau d’actions (F9), et entrez le code suivant:
vaisseau._x += 100; vaisseau._y += 50;
Ceci veut dire “ajouter 100 à la propriété _x du vaisseau” et “ajouter 50 à la propriété _y du vaisseau”.
Testez l'animation (ctrl + Entrée sous windows). Si vous regardez bien, vous verrez que le vaisseau est maintenant affiché non plus au milieu de la scène, mais 100 pixels vers la droite, et 50 pixels plus bas, car les instructions que nous avons écrites ont été exécutées dès le démarrage. Et elles ne le sont qu'une fois.
Si besoin, téléchargez le .fla correspondant (CS3)
Première fonction
Pour rendre notre code plus organisé, nous allons créer une fonction, c'est-à-dire regrouper la suite d'instructions que nous avons écrites sous un seul nom: deplacerVaisseau
Remplacez le code par:
// Fonction qui déplace le vaisseau function deplacerVaisseau() { vaisseau._x += 100; vaisseau._y += 50; }
Si vous testez le résultat maintenant, vous verrez qu'il ne se passe plus rien, et que le vaisseau n'a pas bougé. C'est normal, car ce code sert uniquement à définir une fonction. Maintenant Flash sait qu'il existe une fonction nommée deplacerVaisseau, et qu'elle contient les deux instructions que nous avons écrites. Mais pour exécuter ces instructions, il faut appeler la fonction:
// Définition de la fonction qui déplace le vaisseau function deplacerVaisseau() { vaisseau._x += 100; vaisseau._y += 50; } // Appel de la fonction deplacerVaisseau();
Cette fois, si vous testez, vous retombez sur le même résultat que précédemment. Quand Flash “lit” la ligne…
deplacerVaisseau();
…il exécute les instructions contenues dans la fonction nommée deplacerVaisseau. Comme la fonction n'est appelée qu'une fois, il ne l'exécute qu'une fois.
Mmmoui, tout cela est bien compliqué pour le même résultat . Ce que nous y avons gagné, c'est de pouvoir séparer notre futur code en petits blocs plus petits, les fonctions. Pour l'instant l'intérêt n'est pas prodigieux, mais vous allez voir qu'ensuite cela organise et clarifie beaucoup notre code.
La boucle principale
Pour exécuter le déplacement plusieurs fois nous allons écrire la boucle principale de notre programme. La boucle principale est la suite d'instructions qui sont exécutées à chaque image. Voici comment la définir:
Remplacez tout notre précédent code par:
// Boucle principale function onEnterFrame() { vaisseau._x += 2; vaisseau._y += 1; }
Comme cette fonction s'appelle onEnterFrame, Flash va exécuter cette fonction à répétition, à la cadence de l'animation. C'est automatique. Si vous testez, vous verrez le vaisseau avancer doucement de vers 2 pixels vers la droite et 1 pixel vers le bas, ceci 30 fois par secondes. L'illusion d'un mouvement est ainsi créée.
Hé, oh, qu'est devenu notre fonction deplacerVaisseau? Ah oui, autant pour moi. Retrouvons-la alors en faisant:
// Boucle principale function onEnterFrame() { deplacerVaisseau(); } // Définition de la fonction qui déplace le vaisseau function deplacerVaisseau() { vaisseau._x += 2; vaisseau._y += 1; }
Cette fois le résultat et encore le même. A chaque image la fonction onEnterFrame est exécutée, et elle appelle elle-même la fonction deplacerVaisseau. “Quel bazar” chuchotent certains. Que nenni, au contraire. Avec cette façon de faire, quand notre programme grossira, nous garderons une boucle principale très lisible, du genre:
// Boucle principale function onEnterFrame() { deplacerVaisseau(); deplacerEnnemis(); deplacerTirLaser(); detecterLesCollisions(); mettreAJourLeScore(); }
Ce qui sera nettement plus lisible que si nous écrivions d'un seul bloc toutes les instructions qui font cela. Mais nous n'y sommes pas encore (et d'ailleurs nous n'y arriverons pas dans ce tutoriel ).
La vitesse
La vitesse est donc fonction des deux valeurs qui s'ajoutent aux _x et _y du vaisseau. Nous allons créer deux variables pour stocker ces valeurs:
// Création des deux variables pour la vitesse vitX = 2; vitY = 1; // Boucle principale function onEnterFrame() { deplacerVaisseau(); } // Définition de la fonction qui déplace le vaisseau function deplacerVaisseau() { vaisseau._x += vitX; vaisseau._y += vitY; }
Nous utiliserons par la suite plusieurs autres variables. Pour garder un code propre je propose de placer toutes les variables au même endroit, c'est-à-dire en haut du code.
Pour en savoir d'avantage sur les variables, voyez la documentation d'Adobe: Formation à ActionScript 2.0 dans Adobe Flash > Données et types de données > Présentation des variables.
Le résultat est encore le même. Cela devient lassant . Si vous voulez modifier la vitesse, il vous suffit de modifier les valeurs de vitX et vitY. Essayez donc avec des valeurs négatives, cela nous servira plus tard.
Le grand avantage de cette façon de faire, c'est que nous allons pouvoir faire varier ces valeurs en Actionscript, par exemple quand les touches fléchées du clavier sont pressées.
Utiliser les touches du clavier
Nous avons donc une boucle qui appelle une première fonction qui fait avancer le vaisseau. Nous allons y ajouter une fonction qui fait varier la vitesse selon les touches pressées. Modifiez la boucle principale par:
// Boucle principale function onEnterFrame() { modifierVitesse(); deplacerVaisseau(); }
La fonction modifierVitesse() sera donc elle aussi appelée 30 fois par seconde. Il nous reste à l'écrire. Ajoutez ce code à la fin du programme:
// Détecter les touches et modifier la vitesse function modifierVitesse(){ // Si la touche fléchée droite et enfoncée... if (Key.isDown(Key.RIGHT)) { // Augmenter de 1 la vitesse horizontale vitX ++ ; // ...sinon... } else { // Si la touche fléchée gauche est enfoncée... if (Key.isDown(Key.LEFT)) { // Diminuer la vitesse horizontale de 1 vitX -- ; // ...sinon... } else { // faire décroitre de 10% la vitesse horizontale vers 0 vitX = vitX * 0.9; } } // Même principe pour les touches haut et bas et la vitesse verticale: if (Key.isDown(Key.UP)) { vitY --; } else { if (Key.isDown(Key.DOWN)) { vitY ++ ; } else { vitY = vitY * 0.9; } } }
Et au début du code modifions la vitesse de départ pour qu'elle soit nulle, que le vaisseau n'avance pas sans que nous ayons encore appuyé une touche:
// Création des deux variables pour la vitesse
vitX = 0;
vitY = 0;
Et voilà vous pouvez tester, les explications ensuite:
Bon c'est bien gentil, ça marche, mais comment je m'y retrouve, moi?
- Les commentaires // dans la fonction devraient vous y aider.
- Pour mieux comprendre comment utiliser if…else… vous pouvez vous référer à la documentation du langage, et aussi à cette page.
- Key.isDown(leCodeDUneTouche) vaut vrai quand la touche est enfoncée, et faux sinon. Voir la documentation de la classe Key pour plus d'informations.
- La ligne vitX = vitX * 0.9 signifie: Prend la valeur de vitX, multiplie-la par 0.9, et stocke le résultat dans la variable vitX. C'est comme si on disait: Réduit vitX à 90% de sa valeur. Si ceci se reproduit plusieurs fois, la valeur de vitX va devenir tellement petite que le vaisseau ne bougera plus.
Eviter la sortie de la scène
Maintenant nous allons nous occuper de faire en sorte que le vaisseau ne sorte pas de la scène.
Nous allons avoir besoin de 4 nouvelles variables qui seront les bornes à ne pas dépasser.
Ajouter au début du code les lignes suivantes:
// Bornes à ne pas dépasser xmax = 600; xmin = 0; ymax = 400; ymin = 0;
Nous allons utiliser ces valeurs de 3 façons différentes:
Arrêter le vaisseau
Nous pouvons à chaque fois que nous déplaçons le vaisseau vérifier s'il a oui ou non dépassé les bornes et agir en conséquence. Nous allons faire ceci dans une nouvelle fonction:
// Réagir quand le vaisseau "passe les bornes" function gererLimites() { // Si le vaisseau a dépassé la limite gauche if (vaisseau._x < xmin) { // Le replacer au plus à gauche, mais sans franchir la limite vaisseau._x = xmin; // Stopper son déplacement horizontal vitX = 0; } // Même chose pour la limite droite if (vaisseau._x > xmax) { vaisseau._x = xmax; vitX = 0; } // Et même choses pour les limites haut et bas if (vaisseau._y < ymin) { vaisseau._y = ymin; vitY = 0; } if (vaisseau._y > ymax) { vaisseau._y = ymax; vitY = 0; } }
Et bien sûr il va falloir appeler cette fonction quelque part. Nous pourrions tout à fait le faire dans le onEnterFrame, mais je trouve plus logique de le faire dans deplacerVaisseau:
function deplacerVaisseau() { vaisseau._x += vitX; vaisseau._y += vitY; gererLimites(); }
Et voici le résultat:
C'est ce qui s'appelle foncer dans le mur!
Faire rebondir le vaisseau
Nous pouvons aussi choisir de le faire rebondir plutôt que de s'arrêter. Il suffit de donner le signe opposé à la vitesse. Modifions la fonction:
// Réagir quand le vaisseau "passe les bornes" function gererLimites() { if (vaisseau._x < xmin) { vaisseau._x = xmin; vitX = - vitX ; } if (vaisseau._x > xmax) { vaisseau._x = xmax; vitX = - vitX; } if (vaisseau._y < ymin) { vaisseau._y = ymin; vitY = - vitY; } if (vaisseau._y > ymax) { vaisseau._y = ymax; vitY = - vitY; } }
Passer de l'autre côté
Dernière proposition, faire revenir le vaisseau d'un côté quand il dépasse de l'autre:
// Réagir quand le vaisseau "passe les bornes" function gererLimites() { if (vaisseau._x < xmin) { vaisseau._x = xmax; } if (vaisseau._x > xmax) { vaisseau._x = xmin; } if (vaisseau._y < ymin) { vaisseau._y = ymax; } if (vaisseau._y > ymax) { vaisseau._y = ymin; } }
Ce qui peut s'écrire encore plus légèrement. Puisqu'il n'y a qu'une instruction à chaque if, nous n'avons pas besoin des accolades:
// Réagir quand le vaisseau "passe les bornes" function gererLimites() { if (vaisseau._x < xmin) vaisseau._x = xmax; if (vaisseau._x > xmax) vaisseau._x = xmin; if (vaisseau._y < ymin) vaisseau._y = ymax; if (vaisseau._y > ymax) vaisseau._y = ymin; }
Limiter la vitesse
Peut-être avez-vous regardé ce qui se passe quand on laisse les touches enfoncées longtemps, dans le dernier exemple. La vitesse augmente indéfiniment, puisque rien n'arrête le vaisseau. Réglons cela.
La fonction limiterVitesse() permet de limiter la valeur de vitX et de vitY et ainsi d’éviter que le vaisseau accélère exponentiellement:
Rajoutez ce code à la fin du programme:
// Limiter la vitesse function limiterVitesse() { if (vitX > vitMax) vitX = vitMax; if (vitX < - vitMax) vitX = - vitMax; if (vitY > vitMax) vitY = vitMax; if (vitY < - vitMax) vitY = - vitMax; }
Cette fonction utilise une nouvelle variable nommée vitMax qui représente la vitesse max que le vaisseau peut atteindre. On pourrait mettre à chaque fois une valeur numérique fixe mais c’est plus pratique, lorsque la même valeur se répète, de créer une variable et de modifier juste cette variable quand on veut faire une modification. Allons donc créer cette variable au tout début du code:
// Vitesse maximale autorisée vitMax = 15;
Il faut bien évidemment appeler cette fonction quelque part. Un bon endroit pour la faire est dans la fonction qui elle-même modifie la vitesse, la fonction modifierVitesse():
function modifierVitesse(){ // Le code que nous avions déjà if (Key.isDown(Key.RIGHT)) { vitX ++ ; } else { if (Key.isDown(Key.LEFT)) { vitX -- ; } else { vitX = vitX * 0.9; } } if (Key.isDown(Key.UP)) { vitY --; } else { if (Key.isDown(Key.DOWN)) { vitY ++ ; } else { vitY = vitY * 0.9; } } // Appel de notre nouvelle fonction limiterVitesse(); }
Et voilà. Nous avons beau laisser un touche enfoncée, la vitesse plafonne à 15 pixels par image.
Faire tourner le vaisseau
Il est enfin temps de s'occuper de ce qui nous chagrinait tant: le vaisseau ne tourne pas!
Ceci va être très simple à faire. Il est possible de faire tourner un clip sur lui-même en modifiant sa propriété _rotation. On lui donne comme valeur un angle en degrés, et le clip pivote de cet angle autour de son point d'alignement (c'est pour cela qu'il était important de le choisir au milieu lors de la création du symbole).
La difficulté qui se pose là est uniquement d'ordre mathématique. Nous connaissons les valeurs de vitX et vitY, mais comment en déduire l'angle que fait la vitesse avec l'horizontale. Heureusement pour nous Actionscript propose une méthode toute faite. La fonction…
Math.atan2(vitY, vitX);
…calcule justement cet angle. Pour s'en servir, il suffit d'affecter la valeur retournée par la fonction à la propriété _rotation du clip. Nous allons faire ceci dans la fonction deplacerVaisseau():
function deplacerVaisseau() { vaisseau._x += vitX; vaisseau._y += vitY; vaisseau._rotation = Math.atan2(vitY, vitX); gererLimites(); }
Nous rencontrons là une nouveauté: une fonction peut retourner un résultat. Pour utiliser ce résultat, il suffit d'écrire la fonction à l'endroit où on a besoin de ce résultat, tout simplement, comme on vient de le faire.
Test….zut ça ne marche pas. Enfin, on voit bien un petit changement, mais bon.
L'erreur est que la fonction atan2() retourne un angle en radians, et que la propriété _rotation demande un angle en degrés. On sait tout cela en regardant la documentation du langage pour la classe Math ( Math.atan2() ) et la classe MovieClip ( MovieClip._rotation ).
Il faut donc convertir des radians en degrés. Un petit tour dans la catégorie Mathématiques & physique appliquées des Ressources nous amènera à la solution:
function deplacerVaisseau() { vaisseau._x += vitX; vaisseau._y += vitY; vaisseau._rotation = Math.atan2(vitY, vitX) / Math.PI * 180; gererLimites(); }
Cette fois c'est impeccable.
Je crois que pour être tout à fait heureux, il ne nous manque plus qu'un détail…
Le réacteur
Une petite flambée derrière notre vaisseau serait plus que bienvenue. Ne nous laissons donc pas décourager, et remédions à cette cruelle lacune.
Dessiner la flamme
Editez le symbole du vaisseau, d'un double clic sur son icône dans le panneau bibliothèque (Ctrl + L). Puis créez un nouveau calque dans ce symbole, et dessinez une flamme sur ce nouveau calque:
Sélectionnez le dessin, convertissez-le en symbole de clip (F8) que vous nommerez flamme, et choisissez comme point d'alignement le point indiqué sur l'image suivante:
Puis glissez le calque contenant ce nouveau symbole sous le calque contenant le vaisseau, et nommez l'occurrence de ce symbole flamme:
A ce stade vous pouvez déjà tester pour vérifier qu'il y a bien désormais une flamme de réacteur qui suit votre vaisseau.
Allumer les gaz
Il nous reste à faire en sorte que la taille de cette flamme soit proportionelle à la vitesse de déplacement.
Modifier la longueur de la flamme
Pour modifier la taille de la flamme nous allons agir sur son facteur d'étirement/contraction dans le sens de la largeur. Il s'agit de la propriété _xscale du clip flamme, lui-même contenu dans le clip vaisseau. Par exemple, pour réduire la flamme à 50% de sa largeur nous pouvons écrire en début de code:
vaisseau.flamme._xscale = 50;
Pour tout savoir sur _xscale et les autres propriétés des clips, voyez la documentation de la classe MovieClip.
Calculer la longueur proportionnellement à la vitesse
D'abord il faut évaluer l'importance de la vitesse. Sur quelle valeur se baser? vitX ou vitY? En fait nous allons calculer la “longueur” de la vitesse, qui est la longueur du segment de droite en rouge sur ce dessin:
Pour connaître cette longueur il faut utiliser le théorème de Pythagore qui permet de calculer la longueur de la diagonale d'un rectangle quand on connaît les longueurs des côtés. Ici les côtés ont pour longueur vitX et vitY, et la longueur de la vitesse est donc:
racine carrée de ( vitX2 + vitY2 )
Ce qui en Actionscript se code:
var longueur = Math.sqrt( vitX * vitX + vitY * vitY );
On peut utiliser aussi la fonction Math.pow() qui permet de mettre une valeur au carré mais je trouve ça lourd à écrire .
J'expliquerais le mot var dans quelques lignes. Mais d'abord voyons ce qu'il faut faire de cette longueur.
Comme c'est la longueur de la diagonale, cette longueur peut être plus grande que vitMax. Donc ajoutons une ligne pour que ce ne soit jamais le cas:
var longueur = Math.sqrt( vitX * vitX + vitY * vitY ); // Longueur sera comprise entre 0 et vitMax if (longueur > vitMax) longueur = vitMax;
On a ainsi une longueur comprise entre 0 et vitMax. Si on divise cette longueur par vitMax cela nous donne donc un nombre entre 0 et 1.
var longueur = Math.sqrt( vitX * vitX + vitY * vitY ); if (longueur > vitMax) longueur = vitMax; // facteur sera compris entre 0 et 1 var facteur = longueur / vitMax;
Pour le _xscale de la flamme il faut donner un chiffre entre 0 et 100 (voir la doc). Avec 0 la longueur de la flamme sera nulle, avec 100 elle sera telle que nous l'avons dessinée. Avec un chiffre entre les deux, elle sera d'une taille intermédiaire. Pour transformer notre facteur entre 0 et 1 en un _xscale entre 0 et 100 il nous suffit de le multiplier par 100:
var longueur = Math.sqrt( vitX * vitX + vitY * vitY ); if (longueur > vitMax) longueur = vitMax; // facteur sera compris entre 0 et 1 var facteur = longueur / vitMax; vaisseau.flamme._xscale = facteur * 100;
var
Le mot var sert à créer une variable locale à la fonction. C'est une variable qui ne va servir qu'à l'intérieur de la fonction, et qui sera détruite quand l'exécution de la fonction se termine.
Pour en savoir plus, voyez de nouveau la documentation d'Adobe: Formation à ActionScript 2.0 dans Adobe Flash > Données et types de données > Présentation des variables.
Appliquer le résultat
nous n'avons plus qu'à rentrer tout cela dans une fonction:
// Régler la taille de la flamme function dimensionnerReacteur() { var longueur = Math.sqrt( vitX * vitX + vitY * vitY ); if (longueur > vitMax) longueur = vitMax; var facteur = longueur / vitMax; vaisseau.flamme._xscale = facteur * 100; }
Et à appeler cette fonction dans la boucle principale:
// Boucle principale function onEnterFrame() { modifierVitesse(); deplacerVaisseau(); dimensionnerReacteur(); }
Et voilà, notre petit vaisseau est parfaitement opérationnel!
Version AS3
Voici un portage de la même chose en Actionscript 3:
Vous pourrez trouver dans cette discussion un autre portage réalisé par Thade.
Aller plus loin
Ceci était donc une variante du tutoriel Starfield multidirectionnel, qui n'est lui-même qu'un début au développement d'un petit jeu de tir spatial avec de nombreuses options et ajouts, comme le tir, les ennemis (astéroïdes), les explosions, l'effet “aimant” avec son effet spécial électrique, un HUD, la possibilité de redéfinir les touches, … Vous trouverez dans cet autre tutoriel la façon d'animer un ciel étoilé qui défilerait derrière un vaisseau qui cette fois resterait toujours au centre de la scène, le reste de l'espace bougeant autour de lui.
Les étapes que nous avons réalisées sur cette page ne sont donc pas “compatibles” avec le jeu tel qu'il est développé là-bas, et dont vous trouverez les sources complètes à la fin de la page. Leur étude pourra vous apprendre beaucoup.
En espérant que ce tutoriel vous a plu, je vous laisse avec le zip contenant toutes les sources.
Comme toujours, n'hésitez pas à faire vos remarques ou poser vos question dans la discussion du forum indiquée ci-dessous.
