[AS3] MovieClip.play() inactif au lancement d'une application.
Par eKameleon le dimanche, juin 22 2008, 18:33 - AS3 - Flex - Air - Lien permanent
J'ai beaucoup de travail en ce moment pour finaliser les outils permettant d'utiliser le design pattern d'Inversion de Contrôle avec VEGAS (mon framework opensource) et je suis en train d'écrire un gros article (50aines de pages !) sur le sujet qui arrivera d'ici peu sur ce blog et via une publication opensource basée sur des documents au format Google Documents 
En attendant, voici un petit article très simple pour vous parler d'un mini "problème" découvert par des amis à moi il y a quelques semaines. Je devrais parler d'un "changement de comportement" plutôt que d'un problème...
Voyons donc de plus prêt le comportement de la méthode MovieClip.play() en ActionScript 3.
Nature du problème
Pour tester et isoler le problème de la méthode MovieClip.play() en AS3, il suffit de suivre la méthodologie suivante :
1 - Ouvrir Flash CS3 et créer un nouveau document.
2 - Créer un MovieClip dans la bibliothèque du document ouvert et à l'intérieur créer une petite interpolation de forme ou de mouvement pour que le clip contienne une petite animation sur plusieurs images clés.
3 - Créer un calque de code dans le scénario (timeline) du clip et mettre un stop(); sur la première image clé du clip.
4 - Poser une occurrence du symbole sur la scène et lui donner un nom d'occurrence "mc" dans le panneau de propriété en ayant bien pris le temps de sélectionner le clip avant.
5 - Créer sur la scène principale un calque de code au dessus du calque qui contient l'occurrence et taper le code suivant :
mc.play() ;
Si nous essayons de compiler le swf (CTRL + ENTER) avec une publication au format AS1 ou AS2 (voir paramètre dans panneau des paramètres de publication du document), pas de problème l'animation du clip se lance sans problème.
Si par contre nous essayons de compiler avec un format AS3 avec une publication pour le FlashPlayer 9 alors le clip ne lance pas l'animation est reste figé sur sa première image clé.
Cause du problème
Avec l'AS3, l'ActionScript a changé au niveau de la gestion des éléments visuels d'une animation. En effet maintenant les clips, les champs de textes, les éléments vidéos, etc. appartiennent tous au groupe des objets de type "flash.display.DisplayObject".
Tous ces objets peuvent être attachés sur la scène principale d'une animation ou dans d'autres éléments graphiques comme avant mais ils ne dépendent plus uniquement du clip "parent" dans lequel ils vont être attachés.
En effet, il faut regarder dans la documentation de l'ActionScript 3 au niveau de la classe flash.display.DisplayObjectContainer pour comprendre qu'il est maintenant possible de créer un clip (ou autre élément visuel) dans l'animation sans avoir obligatoirement à l'attacher dans un autre.
En AS3 les classes Loader, Sprite et MovieClip du package flash.display.* héritent de la classe DisplayObjectContainer et peuvent donc attacher toute sorte d'éléments graphique si ils héritent de la classe DisplayObject.
Voyons un petit exemple d'utilisation de cette fonctionnalité :
import flash.display.* ; var container:Sprite = new Sprite() ; var shape:Shape = new Shape() ; shape.x = 25 ; shape.y = 25 ; shape.graphics.beginFill( 0xFF0000 ) ; shape.graphics.drawRect( 0, 0, 50, 50 ) ; addChild( container ) ; container.addChild( shape ) ;
Dans l'exemple ci-dessus nous pouvons donc créer les instances de types Sprite et Shape avant de les attacher dans la scène principale plus loin dans le code.
Pour ce qui est maintenant d'un MovieClip attaché dans un DisplayObjectContainer, il suffit de faire un petit test rapide pour s'apercevoir d'un mode de fonctionnement un peu particulier par rapport à la version AS1 ou AS2 de la classe.
Je modifie le code défini plus haut pour illustrer le problème et j'ajoute un petit test avec une écoute des touches du clavier pour lancer l'animation :
import flash.events.KeyboardEvent ; mc.play() ; // ne fonctionne pas var keyDown:Function = function( e:KeyboardEvent ):void { mc.play() ; // fonctionne si nous appuyons sur une touche } stage.addEventListener( KeyboardEvent.KEY_DOWN , keyDown ) ;
En appuyant sur une touche après l'ouverture de l'animation et une fois que le clip est totalement attaché sur la scène, nous pouvons alors utiliser la méthode play() sans soucis.
Il faut donc un certain temps au player pour initialiser le MovieClip et que nous puissions utiliser la méthode, nous pouvons aussi utiliser la méthode setTimeout pour illustrer ce temps d'attente nécessaire avant d'utiliser la méthode play() :
mc.play() ; // ne fonctionne pas var run:Function = function():void { mc.play() ; // fonctionne après un délais de 150ms par exemple } setTimeout( run , 150 ) ;
Nous pouvons aussi utiliser un petit hack en utilisant la propagation évènementielle de la classe Stage et en écoutant l'évènement de type Event.ACTIVATE :
import flash.events.Event ; mc.play() ; // ne fonctionne pas var activate:Function = function( e:Event ):void { trace("activate") ; mc.play() ; // fonctionne } stage.addEventListener( Event.ACTIVATE , activate ) ;
Finalement nous pouvons dire que lorsque nous attachons un MovieClip ou tout DisplayObject dans un objet de type DisplayObjectContainer il faut absolument faire attention au temps d'initialisation de l'élément graphique dans son container avant de vouloir interagir dessus rapidement. Certaines propriétés et méthodes seront initialisées immédiatement alors que d'autres seront un peu plus capricieuses.
Solution
Finalement, il est impossible de lancer la méthode play() d'un MovieClip avant que celui-ci soit totalement attaché dans un container de l'application.
Personnellement pour remédier à ce petit problème j'utilise la fonction non documentée addFrameScript pour hacker le MovieClip et lui demander de forcer l'exécution de sa méthode play() au moment que je le désire, pour cela il suffit de taper le code suivant :
mc.addFrameScript( 0 , mc.play ) ;
De façon général il faut donc bien réfléchir avant de lancer un clip dans une animation et prendre en compte ce comportement un peu spécial de l'AS3.
Pour toutes vos questions sur cet article vous pouvez utiliser les commentaires de ce blog mais je vous propose d'aller discuter sur le Google Groups de mon blog.
Commentaires
Ce genre de détail est bon à savoir. De même pour la méthode addFrameScript() .
sinon, on pourrais obtenir un résultat similaire en lançant le play au sein d'un event ADD_TO_STAGE non ?
mc.addEventListener(Event.ADD_TO_STAGE, mc.play);
Merci pour l'article en tout cas :).
En fait, c'est pareil en AS1/2 (en suivant tes intructions). Et c'est un vieux problème. Si tu trace() les actions tu verra que stop du clip a lieu après play du parent.
Par contre, si tu décale le stop et le play d'une frame, le comportement change: en AS1/2 le clip continue à se jouer et pas en AS3. Et les trace() montrent qu'en AS1/2 les actions on changé d'ordre...
Une vraie différence par contre : si on remplace le play par un gotoAndPlay. En AS1/2 le clip fait (en gros) gotoAndStop, et en AS3 le code de la 1ère frame n'as pas lieu du tout
Hello
En fait ce qui est très marrant.. c'est qu'avec ma façon de bosser personnelle... je n'ai jamais eu à subir ce bug depuis que j'utilise Flash
Pour ma part j'utilise jamais de stop() sur la première image clé d'un clip, à part si je veux être certain de le lancer plus tard dans l'application...
Il a fallu que des collègues de travail tombe sur ce bug pour me mettre la puce à l'oreille
Dans tous les cas à mon sens la tête de lecture des clips reste capricieuse et il faut faire avec .. même si franchement pour le reste on a pas trop à se plaindre
Pour moi les MovieClip ont évolués en AS3 mais depuis le début font la force de Flash !
EKA+
Hello,
j'avais déjà remarqué ce petit bug depuis un moment lors de mes développement.lorsque mes objets avaient leur propre classe et étendais donc movieClip, je rajoutait un play() dans le constructeur ce qui arrangeait le problème. mais c'est vrai que la solution addFrameScript me semble plus élégante.
Bien vu
Salut
On peut remarquer également, si l'on créé une classe MonClip associée au symbole et en plaçant le stop() dans le constructeur, que le comportement est inversé : le clip est initialisé en premier, et donc le play() du scénario principal (ou de la classe Document) est effectif. ^^
Ce qui peut laisser un peu dubitatif sur la raison réelle du comportement sans la classe associée...
Je cite: "Si nous essayons de compiler le swf (CTRL + ENTER) avec une publication au format AS1 ou AS2 (voir paramètre dans panneau des paramètres de publication du document), pas de problème l'animation du clip se lance sans problème"
C'est curieux je n'arrive pas à reproduire ce comportement en as2 (mc.play() dans le parent, stop() dans l'enfant), qui me surprend d'ailleurs.
Moi j'obtiens un clip stoppé, ce qui est d'ailleurs logique vu l'ordre descendant d'exécution des scripts de frame 1 en as1/2 (alors que les scripts de frames ultérieurs, ainsi que les enterFrame, sont en ordre descendant). Pour ce qui est de l'ordre en as3 je n'ai pas encore bien creusé la question.
Pourrais-tu m'envoyer le fla as2 qui donne clip animé stp?
Je viens de tester l'ordre d'éxécution des scripts de frames en as3, une première différence est que les scripts de frame > 1 s'exécutent désormais dans l'ordre descendant. Ce qui est donc plus homogène qu'en as2 :-). Si tu veux je peux t'envoyer le FLA qui le montre.
ps: ces questions m'intéressent car dans le cadre de jeux flash, j'utilise beaucoup la timeline et il semblerait que les connections entre timeline et as3 connaissent quelques dysfonctionnements dans le player 9 (que j'espère réparées dans le player 10)...
ce collègue de travail ? c'est pas un mec qu'on appelle Chris AKA goabonga ? et dis moi travail tu toujours dans la même boite ?