Flash: Detecting the end of an animation
Say you have an animated character in a game, that others need to know when they are through playing their animation. Here are a few approaches:
Let's first assume we have a generic object named 'player_mc' that is our animation. We also have a centralized controller script closer to the root, that has a function like:
-
function onAnimationComplete(evt:Event = null):void{
-
//do something
-
currentPlayer = evt.target as MovieClip;
-
currentPlayer.gotoAndStop("still");
-
}
Note that we use evt = null just in case we want to use call the function without passing it an event.
1) Using addFrameScript
-
var init:Boolean;
-
if(!init){
-
/// !init check, this block is only needed when pasting on timeline
-
// to keep it from activating more than once when the movieClip recycles
-
addFrameScript(totalFrames -1, onAnimationComplete);
-
init = true;
-
}
-
-
Just remember if doing that approach, addFrameScript uses a frame number one less that what you see in Flash.
This approach is safer when indexing frame labels prior (e.g. with UIUtil) to map say a frame label of "AnimationFinishedFrame" to the frame number for addFrameScript to use.
-
addFrameScript(com.troyworks.ui.UIUtil.getNumberOfFrameLabel("AnimationFinishedFrame", onAnimationComplete);
-
2) Using Event Bubbling
Just communicate up the display list.
-
player.addEventListener("ANIMATION_COMPLETE", onAnimationComplete);
Then
-
//inside the player on last frame of the player animation
-
-
stop();
-
dispatchEvent(new Event("ANIMATION_COMPLETE", true, true));
For more info see here.
3) Use Singleton/Static or Stage as a message bus.
like 2 but using the stage or some other easy to access to communicate without bubbling.
4) Poll for Progress
This is a bit more work, but get's you the ability to find out more about the progress, eg. showing a progress bar for percentage played.
-
player.addEventListener(Event.ENTER_FRAME, onENTER_FRAME
-
-
function onENTER_FRAME(evt:Event):void{
-
var playermc:MovieClip = evt.target as MovieClip;
-
-
if(playermc.currentFrame == playermc.totalFrames){
-
onAnimationComplete();
-
}
-
}
Hope that helps.
Flash: Fast Prototyping and Sketching UI with FlowControl
While I enjoy the new power of Flash9/AS3.0, one of the things I really dislike about the AS3, is how much extra work to do basic things like setup buttons to do simple timeline control, this is especially hard on designers and animators who don't code. I agree with 5etdemi post 4 years ago, the basics have ironically gotten harder to do.
What wrong with ECMAScript/Actionscript? Well, it is not especially well suited for certain tasks, the worst of which is time-based/timeline-oriented animation and declarative drawing. I mean, it seems pretty ironic that ActionScript is derived from ECMAScript which was primarily oriented at scripting in a text-based environment, HTML and the DOM. Simply put, ActionScript has leaned more and more towards heavy lifting text operations (like RegEx and the new XML implementation) and less towards building programmatically what Flash was originally meant for, animation.
While there are useful utilities like Lee Brimelow's nice event generator utility, I wanted less, as even if being good with actionscript, when you have a few minutes to put together something for (or during!) a client/skype meeting, scripting isn't really an option. Thankfully AS3 has some tricks to help!
( press RIGHT arrow key to advance)
One tennant of Sketch is using the type and instance names to do most the event binding work, largely this is to allow Designers to stay on the timeline, but the same could apply to objects created in actionscript as well. So in the above example there are 2 scenes, and a bunch of empty frames, and a nav control. What's special is the buttons you see are actually just semi-transparent buttons (I call InvisiButtons) that have instance names like 'next', 'prev', (shown in the button names) there is no other script on the timeline for them. That via the FlowControl listening to ADDED event are autowired up to call 'nextFrame()' , 'prevFrame()' etc. These InvisiButtonscould be used as a hotspot over a comped image or basic text and wireframe graphics, allowing low to high fidelity comps to be created.
In this example a ENTER_FRAME event updates the TextField off to the right to show the current scene, frame et, to show where you are.
FlowControl has some other nifty features.
- optional features is the fadein, fadeout effect, with configureable color, so while there is a normal timeline, the fade effect is free and ads a bit of polish IMHO.
- tooltips telling you where the buttons go/do.
- keypress for left/right arrow to go to the next previous frame, escape to goto first frame-useful for slide shows
- XXX_autoBtn. If it sees this pattern that button will generate an XXX event (e.g. sayHi_autoBtn generates a new Event("sayHi"), which anybody is free to listen to.
All these are in the example (as well as all the code for FlowControl) on code.google.
Flow Control can be used 'embedded' as the document level class (or extending from it) or loaded as an external swf, and I'll be demoing that in the future. That latter feature is more targetted to creating highly skinnable games which I'll be covering in future blog posts.
AS3: recreating onLoad, MovieClip.addFrameScript part duex
Recreating onLoad with MovieClip.addFrameScript
AS2 had an onLoad event, called after the frame's children had been created, which is largely how custom components were able to manipulate the visual state for useful things like:
- register listeners.
- hiding and showing clips (especially when multiple clips are stacked on top of each other for different error messages)
- jumping to a frame,
- setting up modal dialogs/mouse opaque areas
In AS3 this doesn't work the same, and this bites you on the occassion you have an actionscript class is registered to a class via the linkageID, like that of the "Document class.
If you try it you get the warning:
Warning: 1090: Migration issue: The onLoad event handler is not triggered automatically by Flash Player at run time in ActionScript 3.0. You must first register this handler for the event using addEventListener ( 'load', callback_handler).
So you think, ok, easy enough. Just add an eventListener to load. Test movie and then nothing happens. Search the help docs, and you find zero references to 'load' event. Check DisplayObject and you find every other event (render, added, etc) than the one we are looking for. Leads to one of two possibilities. One the load event doesn't exist... or second the load event has already been called prior to the class constructor being called, so adding a reference to it won't actually do anything. I can't really tell, I've tried searching and running some tests, and can't get it to work... which leads me to the question, just how do I accomplish all those in items above?
Thankfully the fix is easy: addFrameScript in the constructor.
addFrameScript(0,onLoadHandler); //works!!!
// addEventListener("load", onLoadHandler); // doesn't work
While on the subject, in order to access clips on the timeline from the *.as they should be public variables like this:
- public var my_btn:SimpleButton
- public var my_playerControls:MovieClip
and your class should be dynamic e.g
dynamic public class MyView extends MovieClip
Keeping the scripts already on the timeline.
Since your replacing the script call when it hits frame0, whatever is on the timeline won't be called. Thankfully since MovieClip is a dynamic class, scripts on the timeline are appended to the prototype, thus available by this which refers to the prototype chain instead of the class inheritance (Flash is multiple inheritance!). This is useful when the timeline has some configuration data/setup on it, and this approach allows you to call it prior or after your onLoadHandler
public function MyMovieClipClass(){
super();
addFrameScript(0,onLoadHandler);
// addEventListener("load", onLoadHandler); //doesn't work
trace(" AAAA CONSTRUCTOR AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA");
}
function onLoadHandler():void{
trace("BBBBB onLoadHandler BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB");
trace("onLoadHandler " + currentFrame);///pre frameactions
if(!this["frame1"]){
this["frame1"]();
}//post frameactions
my_btn.addEventListener(.....
my_playerControls.gotoAndStop("buffering");
}
OUTPUTS
- AAAA CONSTRUCTOR
- BBBB onLoadHandler
- CCCC frame1TimelineScripts
