I’ve been doing a little bit of work that requires the mixing of the timeline and ActionScript 3. I quickly ran into a problem where when I would tell my movie clip to gotoAndStop at a particular frame, I couldn’t then access the instance I had put on the stage in that frame. It would come back null. After some digging, I ran across people talking about how that even though you have called gotoAndStop, the actual frame hasn’t changed yet. So I then put a listener for the ENTER_FRAME event, and tried to access the instance on the stage then. That still didn’t work. It makes sense. The ENTER_FRAME event is fired when you enter the frame (durrr), so the assets on the stage haven’t rendered yet.
Unfortunately, there is no exit frame or frame complete event that I saw in the API. So the first solution I came up with to solve this problem, was simply to listen on ENTER_FRAME until the instance I had put on the stage wasn’t null. Then I would remove the listener and get my reference to the instance so I could act on it. It looks something like this:
public function set dir( value:String ):void
{
_dir = value;
if ( _dir )
{
gotoAndStop( _dir );
addEventListener( Event.ENTER_FRAME, _onEnterFrame );
}
}
private function _onEnterFrame( event:Event ):void
{
if ( characterClip )
{
removeEventListener( Event.ENTER_FRAME, _onEnterFrame );
if ( _walking )
{
characterClip.gotoAndPlay( WALK );
}
else
{
characterClip.gotoAndStop( REST );
}
}
}
public function get characterClip():MovieClip
{
return getChildAt( 0 ) as MovieClip;
}
However, that just felt a bit inelegant. I don’t like listening on ENTER_FRAME, as it has the potential to fire forever. Unfortunately, the doLater method is only in Flex. So, I tried out a different solution. I made the instances on the timeline be instances of a TimelineClip class. This class adds on the functionality of dispatching an event when they are instantiated. This way I know they are available. I made the event the class dispatched bubble, so the containing clip knows that its internal clips are ready without having to put a listener on them. The TimelineClip class looks like this:
package com.turbidwater.worldExplorer.view.sprites
{
import flash.display.MovieClip;
import flash.events.Event;
public class TimelineClip extends MovieClip
{
static public const INSTANTIATED:String = 'instantiated';
public function TimelineClip()
{
super();
dispatchEvent( new Event( INSTANTIATED, true ) );
}
}
}
And on the class that contains the timeline, I put this:
public function Character()
{
addEventListener( TimelineClip.INSTANTIATED, _onTimelineClipAvailable );
}
private function _onTimelineClipAvailable( event:Event ):void
{
if ( _walking )
{
characterClip.gotoAndPlay( WALK );
}
else
{
characterClip.gotoAndStop( REST );
}
}
There are two things I don’t like about this. One is that each of my timeline instances have to use this as their base class, which means there are more classes, including those default classes Flash makes for library symbols. The other is that when the animated state I’m going to is the same as the one I am on, the TimelineClip doesn’t fire the INSTANTIATED event. So I have to put in two places the code for what I want done when the clip is available. Is that worse than listening for ENTER_FRAME? I dunno. If there is a better way to do it, I’d be interested to know.