banner

Précédent   InfographiK - Communauté Graphique > FLASH & ACTION SCRIPT > Débuter avec Flash > Les cours de base

Réponse
 
LinkBack Outils de la discussion
  #1 (permalink)  
Vieux 05/04/2007, 12h33
Avatar de Smike
Administrateur
 
Date d'inscription: May 2002
Localisation: 26
Messages: 3 008
Zoom [ActionScript] La création d'événements

Pour illustrer l'utilisation, nous allons prendre l'exemple d'une classe SpaceShip, qui à terme sera utilisée pour controler un vaisseau, dans une application type jeu.
Bien entendu, cette classe ne pourra être utlisée dans un environnement de production, sachant qu'elle sera très simplifiée. Le but n'est pas de faire un jeu, mais de comprendre le fonctionnement des évènements en ActionScript.

Durant cet article, nous allon utiliser la classe EventDispatcher de Macromedia.
Cette classe nous donnera la possibilité de créer, écouter, dispatcher des évènements au cours de notre application.

Classe SpaceShip
Créons une classe rapide pour notre vaisseau spatial. Cette classe, pour le moment, va :
  • Créer un nouveau vaisseau (Nouvelle instance = Contructeur)
  • Faire un checkPoint de quelques poins avant de pouvoir décoller

Voilà le code ActionScript de cette classe (commenté) :

ActionScript Code:
  1. class SpaceShip
  2. {
  3. // Le nom du Vaisseau
  4. private var _sSpaceName:String;
  5.  
  6. // Le moteur est-il prêt ?
  7. private var _bEngineReady:Boolean
  8.  
  9. // Le réservoir est-il plein ?
  10. private var _bFioulComplete:Boolean;
  11.  
  12. // Constructeur
  13. function SpaceShip(){
  14. trace("[SpaceShip] Constructor");
  15. _sSpaceName = "SpaceEvent666"
  16. }
  17.  
  18. // Getter pour le nom du Vaisseau
  19. public function getName():String {
  20. return _sSpaceName;
  21. }
  22.  
  23. // Controle du Vaisseau
  24. public function doControl():Void {
  25. _bEngineReady = true;
  26. _bFioulComplete = true;
  27. }
  28.  
  29. }

Jusque là tout va bien normalement. Dans un fichier flash, nous allons créer notre vaisseau :

ActionScript Code:
  1. var vaisseau:SpaceShip = new SpaceShip();

Pour demander de faire un controle du vaisseau avant toute chose, on doit appeler la méthode doControl() de notre classe :

ActionScript Code:
  1. vaisseau.doControl();

A ce moment là, nous devinons que le vaisseau est prêt. Effectivement, la méthode doControl() initialise toutes les propriétés du vaisseau. Mais dans le cas de traitements plus complexes (liste de checkpoint à la NASA Style), comment être sur que le vaisseau est prêt. Imaginez si on le fait decoller avant que tous les point des controles soient checkés, le drame assuré! Dans une logique non évènementielle, on pourrait très bien faire ce genre de traitements :

ActionScript Code:
  1. var checked:Boolean = vaisseau.doControl();
  2. if(checked) {
  3. //faire decoller le vaisseau
  4. }else {
  5. //il y a une erreur lors du checkpoint
  6. }

Ce qui, d'ailleurs, reste complètement juste au niveau programmation. Mais au lieu de réaliser les différents test à l'extérieur de la classe, nous allons les effectuer à l'interieur. Et c'est la classe qui va nous prévenir du résultat (prêt ou non). Dans le code principal (le fla), nous allons juste écouter ce que nous dit la classe.

On va donc attendre un message (event) que va dispatcher la classe à son écouteur.
Voyons donc maintenant comment créer et envoyer des messages.



La classe EventDispatcher (ou la gestion d'évènements)

1./ Initilisation

Pour gérer les évènements dans Flash Mx 2004, on peut utiliser plusieurs classes (standart ou non). Les 2 classes intégrées par Macromedia sont BroadCasterMx du package mx.transitions et la classe EventDispatcher du package mx.events. C'est cette dernière qui sera utilisée dans la suite de l'article.

Si par curiosité vous avez ouvert la classe dans votre éditeur préféré, vous avez peut-être remarqué une chose assez étrange. Il n'y pas de contructeur. Cette classe est appelée grace à ces méthodes statiques (cf. article) Première chose à effectuer, comme dans tout travail avec des classes issu de divers package, il faut importer la classe :

ActionScript Code:
  1. import mx.events.EventDispatcher;

Ensuite il faut initialiser le EventDispatcher dans notre classe. Pour réaliser cette opération, il suffit d'appeler la méthode initialize de la classe. Le paramètre à passer à cette méthode estt tout simplement l'objet qui va envoyer des messages, donc dans notre cas, notre vaisseau, c'est à dire la classe SpaceShip elle-même. Le constructeur devient donc :

ActionScript Code:
  1. function SpaceShip(){
  2. trace("[SpaceShip] Constructor");
  3. EventDispatcher.initialize(this);
  4. _sSpaceName = "SpaceEvent666"
  5. }

Tout est prêt pour envoyer des messages. Maintenant, il va falloir spécifier quels messages vont être envoyés et surtout qui va les écouter.

2./ Création d'un évènement

Pour déclarer un évènement (un message), nous allons utliser la méthode addEventListener de la classe EventDisptacher. Dans un cadre général, voilà la syntaxe de cette méthode :

ActionScript Code:
  1. addEventListener(type d'évènement:String, objet écouteur:object);

Nous choisissons d'envoyer un message "onReady" quand le controle du vaisseau est Ok. Pour le moment, on va envoyer ce message à notre classe elle-même. On peut donc tout à fait gérer des évènements à l'interieur même d'une classe. Nous allons donc ajouter un évènement de type "onReady", et c'est la classe qui va ecouter l'arrivée de ce message.

ActionScript Code:
  1. addEventListener("onReady", this);

Notre constucteur devient donc :

ActionScript Code:
  1. function SpaceShip(){
  2. trace("[SpaceShip] Constructor");
  3. EventDispatcher.initialize(this);
  4. _sSpaceName = "SpaceEvent666";
  5.  
  6. addEventListener("onReady", this);
  7. }

Si vous compilez le code dans cet état, une belle erreur de compilation va arriver dans votre fenêtre de sortie, en vous disant : Flash IDE a écrit :

ActionScript Code:
  1. **Error** E:\COM
  2. WEB\Dev\FORUMFLASH\ADDDVANCE\AS\Evenements\SpaceShip.as:
  3. Line 17: There is no method with the name 'addEventListener'.
  4. addEventListener("onReady", this);

Et bien oui...il faut déclarer les méthodes comme propriétés de notre classes afin d'avoir accès à celle-ci. Notre déclaration de propriétés devient donc :

ActionScript Code:
  1. private var _bEngineReady:Boolean
  2. private var _bFioulComplete:Boolean;
  3. private var _sSpaceName:String;
  4. public var addEventListener:Function;

Nous verrons dans quelques instants, qu'il faudra ajouter encore 2 fonctions à cet "entête". Nous venons donc de créer un évènement de type "onReady". On veut que cet évènement soit envoyé quand le contrôle du vaisseau est Ok. Alors penchons nous sur la méthode doControl().

3./ Envoi d'un évènement

Cette méthode réalise donc un checkpoint de différentes propriétés. Dans cet exemple, il est vraiment simpliste, mais pensons à la complexité que ce genre de controle pourrait atteindre. Une fois que les différentes étapes de controles sont terminés, et que le controle est Ok, il va falloir envoyer notre petit message "onReady", pour dire : "c'est tout Ok, on peut y aller". Pour envoyer un message, on utilise la méthode dispatchEvent() du EventDispatcher. Comme vous l'aurez compris, il donc rajouter cette fonction dans la déclaration de notre classe :

ActionScript Code:
  1. public var dispatchEvent:Function;
  2.  
  3. Par définition, la méthode dispatchEvent() s'ecrit :
  4. dispatchEvent(eventObject);

Le paramètre eventObject est une variable de type object. Cet objet doit obligatoirement comporter: une propriété type (qui correspond à l'évènement envoyé). On pourra bien entendu rajouter autant de propriétés que nécessaire. Dans notre exemple, on envoi le message "onReady". On va rajouter un "lien" vers votre classe, on va donc aussi envoyer une référence à notre classe. L'envoi du message s'écrira donc sous cette forme :

ActionScript Code:
  1. dispatchEvent({type: "onReady", target: this});

On a bien notre type qui correspond au message envoyé et une propriété supplémentaire target qui, ici, fait référence à notre Vaisseau. Notre fonction doControl() devient donc :

ActionScript Code:
  1. public function doControl():Void {
  2. _bEngineReady = true;
  3. _bFioulComplete = true;
  4. dispatchEvent({type: "onReady", target: this});
  5. }

Notre message est initialisé, la méthode doControl() est chargé de dispatcher cet évènement si tout est Ok. Mais comment recevoir ce message et agir en conséquence. ?

4./ Reception d'un évènement

Pour recevoir un évènement, il faut être abonné à cet évènement. C'est à dire qu'il faut spécifier quel objet peut recevoir ce message ou en d'autres termes, qui ou quels sont les écouteurs de ce message. Et c'est au moment de la création de l'évènement que l'on spécifie ceci, rappelez vous :

ActionScript Code:
  1. addEventListener("onReady", this);

Cette méthode créer un évènement de type "onReady" et défini son écouteur. On peut donc dire que notre vaissaeu (=this), est abonné à l'évènement "onReady". Maintenant, pour que l'abonné puisse recevoir le message, il doit tout simplement implémenter une méthode (callback), du nom de notre évènement, dans notre cas. Nous verrons plus tard, que l'on faire ça de plusieurs manières. Donc si nous voulons recevoir notre évènement "onReady", nous allons implémenter une méthode onReady dans notre classe SpaceShip:

ActionScript Code:
  1. public function onReady() : Void {
  2. trace("Ready !");
  3. }

Pour le moment cette méthode n'est pas très utile, car aucun paramètre n'est reçu, donc les traitements sont réduits. Mais souvenez, encore une fois, notre envoi de message:

ActionScript Code:
  1. dispatchEvent({type: "onReady", target: this});

Nous envoyons un objet qui contient des propriétés (comme "target"), on peut donc, tout logiquement, recevoir cet objet. Notre méthode onReady() devient donc :

ActionScript Code:
  1. public function onReady(ev:Object) : Void {
  2. trace("Reception de l'évènement " + ev.type)
  3. trace(ev.target.getName() + " Ready !");
  4. }

On peut constater que la méthode getName() de notre classe est accessible, sachant que l'on a passé une référence à notre classe à notre évènement. Voici donc le code complet de notre classe SpaceShip:

ActionScript Code:
  1. import mx.events.EventDispatcher;
  2. class SpaceShip
  3. {
  4.  
  5. private var _bEngineReady:Boolean
  6. private var _bFioulComplete:Boolean;
  7. private var _sSpaceName:String;
  8.  
  9. public var addEventListener:Function;
  10. public var dispatchEvent:Function;
  11.  
  12. function SpaceShip(){
  13. trace("[SpaceShip] Conctructor");
  14. EventDispatcher.initialize(this);
  15. _sSpaceName = "SpaceEvent666"
  16.  
  17. addEventListener("onReady", this);
  18. }
  19.  
  20. public function getName():String {
  21. return _sSpaceName;
  22. }
  23.  
  24. public function doControl():Void {
  25. _bEngineReady = true;
  26. _bFioulComplete = true;
  27. dispatchEvent({type: "onReady", target: this});
  28. }
  29.  
  30. public function onReady(ev:Object) : Void {
  31. trace("Reception de l'évènement " + ev.type)
  32. trace(ev.target.getName() + " Ready !");
  33. }
  34.  
  35. }

Au niveau de notre code principal, on a ceci :

[highlight=ActionScript]
var vaisseau:SpaceShip = new SpaceShip();
vaisseau.doControl();

On peut compiler dès maintenant notre petit exemple et regarder la fenêtre de sortie. Flash IDE a écrit:

ActionScript Code:
  1. [SpaceShip] Conctructor
  2. Reception de l'évènement onReady
  3. SpaceEvent666 Ready !


Callbacks privées et publique
Comme les propriétés et méthode, on va pouvoir définir des callbacks privées et/ou publique. Nous allons créer un callback "onReady" accessible depuis l'exterieur (évènement publique). Et un évènement "_onReady" qui lui sera accessible uniquement à l'interieur de la classe (privé). En effet, il peut s'avérer très pratique d'avoir un callback public et private du même nom. Ainsi, on peut très bien réaliser des traitements obligatoire dans le callback privé et des traitements optionnel dans le callback publique. Au niveau Design, c'est très propre et très clair. Voyons la définition de nos évènements :

ActionScript Code:
  1. addEventListener("_onReady", this);
  2. addEventListener("onReady", this);

On voit bien ici que 2 évènements sont gérés. Lors de notre controle, on va donc envoyer ces messages.

ActionScript Code:
  1. dispatchEvent({type: "_onReady", target: this});
  2. dispatchEvent({type: "onReady", target: this});

Bien entendu, comme nous l'avons vu, nous devons créer nos méthodes _onReady() et onReady() si l'on veut que les messages soient reçus :

ActionScript Code:
  1. private function _onReady(ev:Object):Void {
  2. trace("Private callback")
  3. trace(ev.target.getName() + " Ready !");
  4. }
  5. public function onReady(ev:Object) : Void {
  6. trace("Public callback")
  7. trace(ev.target.getName() + " Ready !");
  8. }

Sans changer le fla, on peut compiler. Flash IDE a écrit :

ActionScript Code:
  1. [SpaceShip] Conctructor
  2. Private callback
  3. SpaceEvent666 Ready !
  4. Public callback
  5. SpaceEvent666 Ready !

On observe donc bien que les 2 callbacks sont appelés. Rien ne nous empecherait de définir plus de paramètre lors de l'envoi du message publique ou privée... Par exemple :

ActionScript Code:
  1. dispatchEvent({type: "_onReady", target: this});
  2. dispatchEvent({type: "onReady", target: this, msg:"Pret à decoller"});

Avec la méthode onReady() :

ActionScript Code:
  1. public function onReady(ev:Object) : Void {
  2. trace("Public callback")
  3. trace(ev.target.getName() + " Ready !");
  4. trace(ev.msg);
  5. }



Modification des callbacks publique
L'avantage de définir des callbacks publique est de pouvoir les modifier à l'extérieur de la classe. Toujours en se basant sur notre classe SpaceShip, il est possible de modifier le callback onReady() depuis l'extérieur. Donc dans notre fla, nous pouvons re-définir cette méthode :

ActionScript Code:
  1. vaisseau.onReady = function(ev:Object):Void {
  2. trace("Ready too go");
  3. }

Attention, cette méthode doit être défini avant l'appel à la méthode doControl(). En effet, les évènements et callback doivent être initialisés avant les traitements. Le fait de re-définir la méthode onReady() annule la méthode qui existe à l'interieur de la classe.

Donc très rapidemment, on s'aperçois que l'implementation des callbacks publique n'est pas nécessaire à l'interieur de la classe. On peut donc se passer de l'implémentation de la méthode. Ceci étant, il faut définir cette méthode comme propriété de la classe. (obligatoire si on veut la reféfinir). Voici donc le code complet de la classe SpaceShip avec les différentes corrections apportées :

ActionScript Code:
  1. import mx.events.EventDispatcher;
  2. class SpaceShip
  3. {
  4.  
  5.  
  6. private var _bEngineReady:Boolean
  7. private var _bFioulComplete:Boolean;
  8. private var _sSpaceName:String;
  9.  
  10. public var dispatchEvent:Function;
  11. public var addEventListener:Function;
  12.  
  13. public var onReady:Function;
  14.  
  15. function SpaceShip(){
  16. trace("[SpaceShip] Conctructor");
  17. EventDispatcher.initialize(this);
  18. _sSpaceName = "SpaceEvent666"
  19.  
  20. addEventListener("_onReady", this);
  21. addEventListener("onReady", this);
  22. }
  23.  
  24. public function getName():String {
  25. return _sSpaceName;
  26. }
  27.  
  28. public function doControl():Void {
  29. _bEngineReady = true;
  30. _bFioulComplete = true;
  31. dispatchEvent({type: "_onReady", target: this});
  32. dispatchEvent({type: "onReady", target: this, msg:"Pret à decoller"});
  33. }
  34.  
  35. private function _onReady(ev:Object):Void {
  36. trace("Private callback")
  37. }
  38.  
  39. }

Et le code principal :

ActionScript Code:
  1. var vaisseau:SpaceShip = new SpaceShip();
  2. vaisseau.onReady = function(ev:Object):Void {
  3. trace("Ready too go");
  4. }
  5. vaisseau.doControl();



Définir des écouteurs spécifiques.
Le fait de re-définir les callbacks publique de la classe est une technique très utilisées quand onveut gérer quelques évènements. Pour être encore plus clair et plsu efficace, il est tout à fait possible de créer des "vrais" écouteurs d'évènements, qui eux seront indépendant de notre classe. Nous savons qu'un écouteur est un objet. Pour ecouter tous types d'évènements, nous allons donc créer un simple objet :

ActionScript Code:
  1. var oListener:Object = new Object();

Je donne le nom de "oListener" pour, dès le premier coup d'oeil, savoir que :

- 1: C'est un objet (o)
- 2: C'est un éctoueur (Listener)

De même, pour que l'objet puisse recevoir le message, il faut implémenter une fonction onReady. Mais cette fois ci, cette fonction est définie au niveau de l'objet :

ActionScript Code:
  1. oListener.onReady = function(ev:Object):Void {
  2. trace("Callback " + ev.msg);
  3. }

Maintenant notre écouteur créé, il faut l'abonner à notre évènement. Pour cela, utilisons la méthode addEventListener de notre classe :

vaisseau.addEventListener("onReady", oListener);

Rien ne change en réalité, nous avons toujours notre évènement et notre abonné.Wink
Afin d'être complet, nous pouvons maintenant supprimer la propriété onReady que nous avons définis un peu plus haut. On peut la supprimer var nous n'effectuons plus de redéfinition de méthode. Voilà donc le nouveau code de la classe Spaceship :

ActionScript Code:
  1. import mx.events.EventDispatcher;
  2. class SpaceShip
  3. {
  4.  
  5. private var _bEngineReady:Boolean
  6. private var _bFioulComplete:Boolean;
  7. private var _sSpaceName:String;
  8.  
  9. public var dispatchEvent:Function;
  10. public var addEventListener:Function;
  11.  
  12.  
  13. function SpaceShip(){
  14. trace("[SpaceShip] Conctructor");
  15. EventDispatcher.initialize(this);
  16. _sSpaceName = "SpaceEvent666"
  17.  
  18. addEventListener("_onReady", this);
  19. }
  20.  
  21. public function getName():String {
  22. return _sSpaceName;
  23. }
  24.  
  25. public function doControl():Void {
  26. _bEngineReady = true;
  27. _bFioulComplete = true;
  28. dispatchEvent({type: "_onReady", target: this});
  29. dispatchEvent({type: "onReady", target: this, msg:"Pret à decoller"});
  30. }
  31.  
  32. private function _onReady(ev:Object):Void {
  33. trace("Private callback")
  34. }

}Et maintenant le code principal dans notre fla :

ActionScript Code:
  1. var vaisseau:SpaceShip = new SpaceShip();
  2. var oListener:Object = new Object();
  3. oListener.onReady = function(ev:Object):Void {
  4. trace("Callback " + ev.msg);
  5. }
  6. vaisseau.addEventListener("onReady", oListener);
  7. vaisseau.doControl();

Là nous avons une bonne gestion de évènements qui est mis en place. Nous pouvons abonner pleins d'objet à notre évènement et réaliser les traitements adéquates.Pour aller encore plus loin, et finir ce 2eme volet sur la programmation évènementielle, si on peut abonner un objet à un évènement, il est alors possible de créer nous même, de toute pièce, cet objet. Et cet objet peut avoir des propriétés, méthodes propres à lui même.

Et bien oui, on peut très bien abonner une instance d'une classe à un évènement ! Et c'est là que tout devient magique. Imaginons que notre vaisseau spatial passe son control, au lieu de le dire à nous, ce qui n'a rien de bien utile, il préfère le dire à la base de lancement.

Et la base de lancement, elle, va dire, si elle est Ok, "c'est bon, vous pouvez décoller". Un exemple des plus concrets non ? Implémentons ces différentes notions... Voici la classe SpaceShip :

ActionScript Code:
  1. import mx.events.EventDispatcher;
  2. class SpaceShip
  3. {
  4.  
  5. private var _bEngineReady:Boolean
  6. private var _bFioulComplete:Boolean;
  7. private var _sSpaceName:String;
  8.  
  9. public var dispatchEvent:Function;
  10. public var addEventListener:Function;
  11.  
  12.  
  13. function SpaceShip(){
  14. trace("[SpaceShip] Conctructor");
  15. EventDispatcher.initialize(this);