Forums Développement Multimédia

Les formations Mediabox
Les formations Mediabox

Gestion des touches en AS3

Compatible ActionScript 3. Cliquer pour en savoir plus sur les compatibilités.Par Billyben, le 22 juin 2010

Bonjour à tous, nous allons voir quelques façons de gérer les touches en AS3, qui diffèrent quelque peu de ce que vous avez pu rencontrer en AS2.

Prérequis : Niveau Débutant
Pour aller plus loin, par exemple dans la gestion du déplacement dans l'optique de la création d'un jeux, je vous propose d'aller voir :



La base : Ecouter l'enfoncement et le relâchement des touches

Dans un premier temps, nous allons voir comment savoir si l’utilisateur a enfoncé/relâché une touche. C’est la base de ce que nous verrons par la suite.

L'évènement

Tout repose sur un événement : flash.events. KeyboardEvent
Dont on distingue 2 types :

  • KEY_DOWN : String = “keyDown” - [static] Définit la valeur de la propriété type d'un objet événement keyDown. Quand on enfonce une touche.
  • KEY_UP : String = “keyUp” - [static] Définit la valeur de la propriété type d'un objet événement keyUp. Quand on relâche une touche.

Qui permettent de savoir si l’utilisateur a enfoncé ou relâché une touche.

Cet événement présente plusieurs propriétés intéressantes :

  • altKey : Boolean - Indique si la touche Alt est active (true) ou non (false). Pris en charge uniquement pour les systèmes d'exploitation Windows.
  • ctrlKey : Boolean – Indique si la touche Ctrl est activée (true) ou non (false). Sur les systèmes Macintosh, la touche de modification Commande doit être représentée par le biais de cette touche de modification.
  • shiftKey : Boolean - Indique si la touche de modification Maj est activée (true) ou non (false).
  • keyLocation : uint - Emplacement de la touche sur le clavier. Cette propriété vous permet de différencier des touches qui figurent plusieurs fois sur un clavier. Sa valeur vous permet par exemple de faire la différence entre les touches Maj de gauche et de droite : KeyLocation.LEFT représente la touche de gauche et KeyLocation.RIGHT celle de droite. Vous pouvez aussi, par exemple distinguer les touches numériques du clavier standard (KeyLocation.STANDARD) de celles du pavé numérique (KeyLocation.NUM_PAD).
  • charCode : uint - Contient la valeur du code de caractère associé à la touche enfoncée ou relâchée. Les valeurs renvoyées sont celles du clavier anglais.

Et celle qui va nous intéresser plus particulièrement pour la suite :

  • keyCode : uint - Valeur de code correspondant à la touche enfoncée ou relâchée.


La correspondance flèches – keyCode – constantes Keyboard

Il existe une classe : flash.ui.Keyboard qui contient tout un tas de constantes qui représentent les codes des touches enfoncées, et qui peut être utile (plutôt que d’aller pécher les keyCode de chaque touche…

  • Flèche Droite : keyCode = 37 - Keyboard.RIGHT
  • Flèche Gauche : keyCode =39 - Keyboard.LEFT
  • Flèche Haut : keyCode =38 - Keyboard.UP
  • Flèche Bas : keyCode =40 - Keyboard.DOWN

Si vous désirez utiliser ces constantes dans un fichier .as, n'oubliez pas d'ajouter la bonne bibliothèque à l'aide du code :

import flash.ui.Keyboard;


Détecter l’enfoncement et le relâchement d’une touche

Pour savoir si un utilisateur enfonce une touche :

stage.addEventListener(KeyboardEvent.KEY_DOWN, keyDownHandler)
function keyDownHandler(ev:KeyboardEvent):void{
     trace ("une touche a été enfoncée - keyCode="+ev.keyCode+"  - charCode="+ev.charCode) ;
}

Pour savoir si un utilisateur relâche une touche :

stage.addEventListener(KeyboardEvent.KEY_UP, keyUpHandler)
function keyUpHandler(ev:KeyboardEvent):void{
     trace ("une touche a été relâchées - keyCode="+ev.keyCode+"  - charCode="+ev.charCode) ;
}

Voilà, nous pouvons désormais suivre l'action de l'utilisateur sur son clavier.



Détecter quand l’utilisateur garde une touche enfoncée

Alors vous vous dites pourquoi en faire plus, nous avons tout ce qu’il nous faut… et bien en fait, il y a un problème. Pour le mettre en évidence, essayez ce petit script, en maintenant une touche enfoncée :

stage.addEventListener(KeyboardEvent.KEY_DOWN, keyDownHandler)
var firstHit=false;
var time:int=0
function keyDownHandler(ev:KeyboardEvent):void{
	if (firstHit){
		var delta=getTimer()-time;
		time=getTimer();
		trace ("évènement diffusé à"+delta) ;
	}else{
		time=getTimer();
		trace ("Premier évènement diffusé")
		firstHit=true;
		}
}

Le principe ici est de quantifier le temps entre chaque diffusion de l’évènement.

Vous devez voir quelque chose du genre :

Premier évènement diffusé
évènement diffusé à560
évènement diffusé à33
évènement diffusé à34
évènement diffusé à32
évènement diffusé à31
évènement diffusé à31
//etc....

Vous constatez que la seconde diffusion intervient plus d’une demi-seconde après le premier appel (cela dépend de vos réglages). En effet, nous avons le même effet que sous votre éditeur de texte par exemple, où l’appui continu sur une touche n’ajoute le second caractère (ou effacement ou autre) qu’après un certain délai. Il en résulte que si vous voulez déplacer un personnage, un vaisseau ou tout autre chose par le clavier, vous constaterez des à-coups dans son déplacement.


L’exemple : déplacer un clip

Pour les exemples suivant, nous allons mettre en place une petite application qui permettra de déplacer un clip, ce qui nous permettra de mettre en œuvre les différentes façons de suivre l’activité des touches, et les petits moteurs qui vont de pair ! Dessinez un carré sur la scène, convertissez le en symbole, puis nommez cette occurrence : 'ship' (pour vaisseau en anglais, mais c’est plus court….).
Mettons donc en œuvre ce que nous avons vu précédemment.

Nous allons maintenant déplacer notre vaisseau, avec ce que nous avons vu pour le moment. Ici, le moteur qui permet le déplacement n’est pas dissocié de la gestion des touches.

stage.addEventListener(KeyboardEvent.KEY_DOWN, keyDownHandler);
 
function keyDownHandler(ev:KeyboardEvent):void {
  switch (ev.keyCode) {
                case Keyboard.RIGHT :
                        ship.x+=5;
                        break;
                case Keyboard.LEFT :
                         ship.x-=5;
                        break;
                case Keyboard.UP :
                         ship.y-=5;
                        break;
                case Keyboard.DOWN :
                         ship.y+=5;
                        break; 
        }
 
}

Ce qui donne:

L"extension Adobe Flash Plugin est nécessaire pour afficher ce contenu.

Alors, notre vaisseau ne se déplace-t-il pas par à-coups au début ? Et impossible de suivre plusieurs touches en même temps….



Ecoute continue des touches

le principe de base

Pour palier à ce phénomène, embêtant par rapport à l’AS2, mais finalement naturel au vu de l’utilisation du clavier dans tous nos logiciels, nous allons devoir mettre en place un système qui dissocie le moteur de la gestion des touches. Il nous faut donc un/des objet(s) qui garde(nt) une trace de l’activité sur le clavier, au niveau de l’enfoncement et le relâchement des touches.

var keyDown :Boolean=false ; 
stage.addEventListener(KeyboardEvent.KEY_UP, keyUpHandler);
function keyDownHandler(ev:KeyboardEvent):void {
	keyDown=true;
}
stage.addEventListener(KeyboardEvent.KEY_UP, keyUpHandler);
function keyUpHandler(ev:KeyboardEvent):void {
	keyDown=false;
}

Comment utiliser ceci ? Nous allons prendre l’exemple d’un moteur tournant avec un Enter_Frame :

stage.addEventListener(Event.ENTER_FRAME, enterFrameHandler);
function enterFrameHandler(ev:Event) {
	if (keyDown) {
		trace("une touche est enfoncée");
	} else if (!keyDown) {
		trace("toute les touches sont relachées");
	}
}

Pour le moment ça ne sert pas à grand-chose, mais de toute façon, ici on ne veut regarder que le principe…


Première solution : N touches à suivre=N Booléens

Une première solution est d’utiliser autant de Booléens que de touches dont on veut suivre l’activité, true pour la touche enfoncée, false pour la touche relevée.

Le code

var rightKey :Boolean=false ;
var leftKey :Boolean=false ;
var upKey:Boolean =false;
var downKey:Boolean=false;
 
stage.addEventListener(KeyboardEvent.KEY_DOWN, keyDownHandler);
stage.addEventListener(KeyboardEvent.KEY_UP, keyUpHandler);
 
function keyDownHandler(ev:KeyboardEvent):void {
	  switch (ev.keyCode) {
                case Keyboard.RIGHT :
                        rightKey=true;
                        break;
                case Keyboard.LEFT :
                        leftKey=true;
                        break;
                case Keyboard.UP :
                        upKey=true;
                        break;
                case Keyboard.DOWN :
                        downKey=true;
                        break; 
        }
 
}
 
function keyUpHandler(ev:KeyboardEvent):void {
	 switch (ev.keyCode) {
                case Keyboard.RIGHT :
                        rightKey=false;
                        break;
                case Keyboard.LEFT :
                        leftKey=false;
                        break;
                case Keyboard.UP :
                        upKey=false;
                        break;
                case Keyboard.DOWN :
                        downKey=false;
                        break; 
        }
 
}

Et maintenant mettons en place le moteur capable de mouvoir notre vaisseau. Il suffit ici de tester les différents booleens correspondant aux différentes touches.

stage.addEventListener(Event.ENTER_FRAME, enterFrameHandler);
function enterFrameHandler(ev:Event) {
	if (rightKey) {
		ship.x+=5;
	}
	if (leftKey) {
		ship.x-=5;
	}
	if (upKey) {
		ship.y-=5;
	}
	if (downKey) {
		ship.y+=5;
	}
}


L'exemple

L"extension Adobe Flash Plugin est nécessaire pour afficher ce contenu.

Comme vous pouvez le voir, il n'y a pas de temps de latence, et on obtient la gestion de plusieurs touches en même temps.


Vous voulez plus court

Vous pouvez “compressez” le script pour l'écoute des touches :

var rightKey :Boolean=false ;
var leftKey :Boolean=false ;
var upKey:Boolean =false;
var downKey:Boolean=false;
 
stage.addEventListener(KeyboardEvent.KEY_UP, keyHandler);
stage.addEventListener(KeyboardEvent.KEY_UP, keyHandler);
 
function keyHandler(ev:KeyboardEvent):void 
{
    switch (ev.keyCode) 
    {
        case Keyboard.RIGHT:
            rightKey = ev.type == KeyboardEvent.KEY_DOWN;
        break;
        case Keyboard.LEFT:
            leftKey = ev.type == KeyboardEvent.KEY_DOWN;
        break;
        case Keyboard.UP:
            upKey = ev.type == KeyboardEvent.KEY_DOWN;
        break;
        case Keyboard.DOWN:
            downKey = ev.type == KeyboardEvent.KEY_DOWN;
        break;
    }
}

Plus qu'une seule fonction d'écoute (le moteur ne changeant pas).

Rendons à César ce qui appartient à César : vous pouvez remercier çayjb pour ça!



Seconde solution : utilisation d’un objet

Une autre solution consiste à utiliser un objet qui va référencer les touches enfoncées. Cet objet présentera des propriété qui vont prendre pour nom la valeur du keyCode de la touche, associé à true/false sur le même principe que précédemment.


Le code

var keys:Object=new Object();
stage.addEventListener(KeyboardEvent.KEY_DOWN, keyDownHandler);
function keyDownHandler(ev:KeyboardEvent):void{
    keys[ev.keyCode]=true;
}
stage.addEventListener(KeyboardEvent.KEY_UP, keyUpHandler)
function keyUpHandler(ev:KeyboardEvent):void{
	 keys[ev.keyCode]=false;
}

Et pour le moteur, on teste les valeurs des propriétés de l'objet.

stage.addEventListener(Event.ENTER_FRAME, enterFrameHandler);
function enterFrameHandler(ev:Event) {
	if (keys[Keyboard.RIGHT]) {
		ship.x+=5;
	}
	if (keys[Keyboard.LEFT]) {
		ship.x-=5;
	}
	if (keys[Keyboard.UP]) {
		ship.y-=5;
	}
	if (keys[Keyboard.DOWN]) {
		ship.y+=5;
	}
}


L'exemple

L"extension Adobe Flash Plugin est nécessaire pour afficher ce contenu.


Vous voulez plus court

function keyHandler(ev:KeyboardEvent):void{
    keys[ev.keyCode]=ev.type==KeyboardEvent.KEY_DOWN;
}

Vous pouvez également remercier çayjb pour ça!



Une autre solution : un mélange des deux

Vous pouvez également avoir un mélange des deux, notamment si vous vous dites que garder l’intégralité des touches enfoncées n’est pas nécessaire.

var keys:Object=new Object();
 
stage.addEventListener(KeyboardEvent.KEY_DOWN, keyDownHandler);
stage.addEventListener(KeyboardEvent.KEY_UP, keyUpHandler);
 
function keyDownHandler(ev:KeyboardEvent):void {
	if (ev.keyCode==Keyboard.RIGHT||ev.keyCode==Keyboard.LEFT||ev.keyCode==Keyboard.UP||ev.keyCode==Keyboard.DOWN) {
		keys[ev.keyCode]=true;
	}
}
 
function keyUpHandler(ev:KeyboardEvent):void {
	if (ev.keyCode==Keyboard.RIGHT||ev.keyCode==Keyboard.LEFT||ev.keyCode==Keyboard.UP||ev.keyCode==Keyboard.DOWN) {
		keys[ev.keyCode]=false;
	}
}

Est-ce bien utile ???? euuuuhhh….



Offrir le choix à l’utilisateur de définir ses touches

Il est parfois de bon ton de laisser l’utilisateur définir ses touches. D’ailleurs je regrette que ce ne soit pas fait plus souvent sur les appli/jeux que l’on rencontre, alors que c’est pourtant si facile…
Le principe ici est de conserver une référence au code de la touche que l’utilisateur veux utiliser. Pour l’exemple, j’utiliserai un objet qui nous servira à cette fin. Et nous verrons comment mettre en œuvre les 2 exemples de gestion des touches.

[Edit Nataly 27/03/13]
Au point où on en est de proposer à l’utilisateur de paramétrer ses préférences, autant les enregistrer, et pourquoi pas dans un cookie, c'est le cas de figure typique ;) Le tuto cookies s'illustre donc en pillant consciencieusement le code expliqué ici :)


Un objet ‘joueur'

Nous allons mettre en place un objet qui va nous permettre de référencer les touches que le joueur désire utiliser.

var player:Object={RIGHT:Keyboard.RIGHT,LEFT:Keyboard.LEFT, UP:Keyboard.UP,DOWN:Keyboard.DOWN};

Nous allons pouvoir interroger cet objet pour définir les actions à mener. Il suffit ensuite de modifier les valeurs de RIGHT, LEFT, UP et DOWN de cet objet pour personnaliser les touches actives.


Modification de l'objet joueur, un exemple

Pour l’exemple, nous allons mettre en place cette possibilité, très simplement…
Sur la scène, ajoutez un champ de texte dynamique, et donnez lui comme nom d’occurrence : 'txt'.
Nous allons faire en sorte qu'en cliquant sur le texte, le joueur puisse tour à tour définir les 4 touches qui vont servir à faire bouger son vaisseau.

Maintenant, tout se passe dans le code :

var array:Array=["RIGHT","LEFT","UP","DOWN"];// qui va nous permettre de cibler les propriétés
var current:int=0;// qui va nous servir à incrémenter
txt.addEventListener(FocusEvent.FOCUS_IN, onFocus);// on démarre le procédé quand le champ texte prend le focus
 
function onFocus(ev:FocusEvent) {
	txt.addEventListener(KeyboardEvent.KEY_DOWN, onKey);// on va maintenant écouter les touches appuyées
	txt.removeEventListener(FocusEvent.FOCUS_IN, onFocus);
	txt.addEventListener(FocusEvent.FOCUS_OUT, outFocus);
	txt.text="Appuyer touche "+array[current];// on affiche la direction pour la touche.
 
}
function onKey(ev:KeyboardEvent) {
	player[array[current]]=ev.keyCode;// on enregistre le code de la touche correspondante
// si jamais on a fini, ben on remet tout à 0 !!
	if (current==array.length-1) {
		txt.removeEventListener(KeyboardEvent.KEY_DOWN, onKey);
		txt.addEventListener(FocusEvent.FOCUS_IN, onFocus);
		current=0;
		txt.text="Appuyer pour définir touche";
		stage.focus=this;
		return;
	}
	current++;
	txt.text="Appuyer touche "+array[current];
}
 
function outFocus(ev:FocusEvent) {
	txt.removeEventListener(KeyboardEvent.KEY_DOWN, onKey);
	txt.addEventListener(FocusEvent.FOCUS_IN, onFocus);
	current=0;
	stage.focus=this;
}


Utilisation - Première solution – Booléen

Ici, on va tester, dans les écouteurs de KeyboardEvent, les valeurs du joueur par rapport à la touche enfoncée/relâchée.


Le code
var rightKey :Boolean=false ;
var leftKey :Boolean=false ;
var upKey:Boolean =false;
var downKey:Boolean=false;
 
var player:Object={RIGHT:Keyboard.RIGHT,LEFT:Keyboard.LEFT, UP:Keyboard.UP,DOWN:Keyboard.DOWN};
 
stage.addEventListener(KeyboardEvent.KEY_DOWN, keyDownHandler);
stage.addEventListener(KeyboardEvent.KEY_UP, keyUpHandler);
 
function keyDownHandler(ev:KeyboardEvent):void {
	  switch (ev.keyCode) {
                case player.RIGHT :
                        rightKey=true;
                        break;
                case player.LEFT :
                        leftKey=true;
                        break;
                case player.UP :
                        upKey=true;
                        break;
                case player.DOWN :
                        downKey=true;
                        break; 
        }
 
}
 
function keyUpHandler(ev:KeyboardEvent):void {
	 switch (ev.keyCode) {
                case player.RIGHT :
                        rightKey=false;
                        break;
                case player.LEFT :
                        leftKey=false;
                        break;
                case player.UP :
                        upKey=false;
                        break;
                case player.DOWN :
                        downKey=false;
                        break; 
        }
 
}

Le moteur ne variant pas.

L'exemple

L"extension Adobe Flash Plugin est nécessaire pour afficher ce contenu.



Utilisation - Seconde solution – l’objet

Ici, la gestion des touches ne change pas, on ne modifie que le moteur.


Le code
var player:Object={RIGHT:Keyboard.RIGHT,LEFT:Keyboard.LEFT, UP:Keyboard.UP,DOWN:Keyboard.DOWN};
var keys:Object=new Object();
stage.addEventListener(KeyboardEvent.KEY_DOWN, keyDownHandler);
stage.addEventListener(KeyboardEvent.KEY_UP, keyUpHandler)
function keyDownHandler(ev:KeyboardEvent):void{
    keys[ev.keyCode]=true;
}
 
function keyUpHandler(ev:KeyboardEvent):void{
 keys[ev.keyCode]=false;
}
stage.addEventListener(Event.ENTER_FRAME, enterFrameHandler);
function enterFrameHandler(ev:Event) {
	if (keys[player.RIGHT]) {
		ship.x+=5;
	}
	if (keys[player.LEFT]) {
		ship.x-=5;
	}
	if (keys[player.UP]) {
		ship.y-=5;
	}
	if (keys[player.DOWN]) {
		ship.y+=5;
	}
}


L'exemple

L"extension Adobe Flash Plugin est nécessaire pour afficher ce contenu.



Le mieux du Moins Bien, le pour et le contre

Note de Lilive:
Billyben propose dans ce tutoriel des méthodes différentes. Compte tenu des préférences de chaque programmateur, on ne peut pas toujours dire qu'une méthode est meilleure qu'une autre. C'est pourquoi, en attendant d'être persuadé d'avoir trouvé la solution “ultime”, il vous propose ceci:

Pour le moment : venez en discuter Ici