Trust Me, AS3, It’s a MovieClip

Flash ActionScript 3.0

This topic came up when someone asked me how (if it was possible) to instruct one movie clip to start playing after another has stopped.  For example, the main timeline does its thing, humming along, when it suddenly comes to a keyframe that contains a nested movie clip.  A simple stop() action in that keyframe tells the main timeline to rest where it is, and the nested movie clip starts playing on its own.  When the nested clip hits the last frame of its own timeline … that’s when the main timeline needs to start moving again.  How to do that?

In my reply, I said there were a number of possible ways.  You could set up a loop, for example — MovieClip.onEnterFrame (AS2) or Event.ENTER_FRAME (AS3), maybe setInterval() (AS2) or the Timer class (AS3) — and in that loop, check the current frame of the nested movie clip against the number of its total frames.  In AS2, that would be a comparison of _currentframe to _totalframes; in AS3, currentFrame to totalFrames.  When the former equals the latter, invoke MovieClip.play() on the main timeline and quit the loop.

But much easier than that, and less processor intensive, is simply to put a keyframe script in the last frame of the nested movie clip, telling its parent (the main timeline) to play.  In AS2, that would be this._parent.play();.  In AS3, this.parent.play(); (no underscore on parent).  Ah, but there lies a problem.  The AS3 version, which is technically correct, causes a compiler warning:  1061: Call to a possibly undefined method play through a reference with static type flash.display:DisplayObjectContainer.  What on earth? 

What’s going on?

Thankfully, that error message gives a very useful clue.  Forget the cryptic 1061, forget “static type” blah blah.  The useful parts are “undefined method play” and “DisplayObjectContainer.”  Essentially, this is saying, “Hey, you’re pointing me to an instance of the DisplayObjectContainer class and asking me to invoke a play() method on it.  There is no play() method in this class, bub, so what am I supposed to do?”

If you look up the DisplayObjectContainer class in the ActionScript 3.0 Language and Components Reference, you’ll find that indeed, the class doesn’t support the method play().  That’s a MovieClip method.  But wait … shouldn’t the expression this.parent — as stated by a movie clip in the main timeline — point to a valid MovieClip reference?  To be fair, DisplayObjectContainer is indeed an ancestor in MovieClip’s family tree.  MovieClip extends Sprite, which extends DisplayObjectContainer, but the main timeline certainly is a movie clip.  In fact, you can prove it.

Create a new ActionScript 3.0 FLA, draw a quick shape and convert it to a movie clip symbol.  Enter its timeline and, in frame 1, type trace(this.parent is MovieClip); (is replaces instanceof in AS3).  Test your movie and keep an eye on the Output panel.  Sure enough, Output panel says true.

So what gives?  If the expression this.parent points to a valid MovieClip instance, why isn’t Flash smart enough to allow for the invocation of a MovieClip method, such as play(), on that instance?  Honestly, that’s a good question.  It’s so good, I can’t answer it.  But I do know how to get around it.  ;)

The “unsafe” workaround

That overly cautious error message is the result of Flash CS3’s default strict mode for the compiler.  I don’t recommend the following change, but if you simply want to turn off the warning (along with a bunch of other useful warnings, all in one shot), head over to File > Publish Settings > Flash tab, then click the Settings button.  Remove the checkmark from Strict Mode and you’re done.  Test your code and it’ll work just fine — because (in this context) this.parent really is a MovieClip reference.

Again, experiment with that if you like, but I really don’t recommend it.  By turning off strict mode, you may be tempting yourself to write lazy code, and sooner or later, especially in AS3, that will bite you with a set of sharp teeth.  And it’ll happen on a day when your boss is breathing down your neck.

The better workaround

I call this one “better” because, by using it, you end up demonstrating that you know how to be sensitive to the compiler, paranoid as it may occasionally be.  The better you know what the compiler needs, the likelier you’ll master the language.

We already know, even if the compiler doesn’t seem to, that the expression this.parent — in this context! — does refer to a MovieClip instance.  We’re simply going to tell the compiler that.  By casting that expression as a MovieClip, you’re effectively saying, “Trust me on this one; this is a MovieClip.”  Casting is done like this:

MovieClip(this.parent).play();

Note that only the object reference, this.parent, goes inside the parentheses.  The new combined expression, MovieClip(this.parent) gets .play() (with the leading dot) tacked on to the end, and even with strict mode on, that’ll do it.

That goes for any MovieClip class member, by the way, not just play().  All the cast does is let the compiler know it’s truly dealing with a MovieClip, so any MovieClip feature is covered.

23 Responses to “Trust Me, AS3, It’s a MovieClip”

  1. tag hag Says:

    thank you, this article saved my bacon!

  2. David Stiller Says:

    tag hag,

    Mmmm, bacon!

    Glad to help! :)

  3. eRez Says:

    you can also try this -
    (this.parent as MovieClip).play();

    i also find this useful, in cases where a MC contains a text-field. in such cases if i want to change the text-field’s text i’d do something like -
    (mc.getChildByName(”my_txt”) as TextField).text = “abc”;

  4. David Stiller Says:

    eRez,

    Yes! I keep forgetting about as in AS3. Thanks for the reminder!

  5. casey govero Says:

    Thanks again for the help with this question david. Always enjoy reading your blogs.

    -casey

    P.S. My band just finished recording one of our original tracks, it can be found on our band’s site, www.kharmaband.com! take a listen and let me know what you think man!

  6. David Stiller Says:

    Casey,

    You’re welcome — and good luck with Kharma! I like your sound.

  7. tunghoy Says:

    Dave,

    Your code indeed removes that silly 1061 error, but now when I use MovieClip(this.parent).gotoAndPlay("myLabel");, the compiler now throws this error: Error #1009: Cannot access a property or method of a null object reference.. WTF is that?

  8. David Stiller Says:

    tunghoy,

    Somehow, it sounds like you’re getting a null object reference. If your code truly appears in the keyframe of a nested movie clip, the expression this.parent should indeed point to a MovieClip instance. What does your Output panel say when you add the following trace() statements?

    trace(this.parent);
    trace(typeof this.parent);
  9. tunghoy Says:

    Hi Dave, thanks for your fast response. When I run the trace statements, it returns responses twice, as though the MC is running twice. It returns:


    [object MainTimeline]
    object
    null
    object

    And it throws the error again:
    TypeError: Error #1009: Cannot access a property or method of a null object reference. at mc_Intro/::frame140()

    The whole movie is pretty short (so far). All I have is this:

    Main timeline, frame 3:

    stop();
    var intro:mc_Intro = new(mc_Intro);
    this.addChild(intro);

    This plays a MC symbol, with this code at the end, on 140:

    MovieClip(this.parent).gotoAndPlay("clipTwo");

    Back on the main timeline, at the clipTwo label, is this:

    stop();
    this.removeChild(intro);
    var secondMovie:mc_1999 = new(mc_1999);
    addChild(secondMovie);

    Finally, at the end of the 2nd mc, is just a stop() action.

    I made sure the 2 MCs were exported for AS, and made absolutely certain that I’m using the correct identifiers. The base class for both MCs is flash.display.MovieClip.

    So do you have any idea what kind of stupidity I’m engaging in?? There are no objects at all on the main stage. It’s an empty stage with code in the timeline.

  10. tunghoy Says:

    Oh yeah, and there’s another stop() action just before the clipTwo frame label.

  11. tunghoy Says:

    Shoot — I just figured out the problem: I forgot to put in a fracking stop() action before the gotoAndPlay statement! That’s why the MC was running twice w/o displaying twice, and returning two sets of trace statements.

    Thanks for the idea of tracing out those objects! You saved me a lot more wall-head-banging time.

  12. David Stiller Says:

    tunghoy,

    Ha! Well, that’s cool … I’m glad you solved it! :-D trace() statements can be a real life saver. There are a number of additional troubleshooting techniques that may help you even more. Check out these:

    Introducing the ActionScript 3.0 debugger
    Debugging ActionScript 2.0 Code: Lifting the Blindfold

  13. tunghoy Says:

    Great, I’ll look at that over the weekend. Adobe’s tutorials are a lot better than their LiveDocs. I’ve also been going through the advanced AS tutorials on lynda.com (a dangerous place because I want to learn everything).

    BTW — I looked through your tutorial on pausing the timeline, but I’m having some trouble with that, also. My main timeline contains only child MCs, external sound and pause & play buttons. How can I get the whole thing (main timeline & currently playing MC) to pause? None of my stop actions are working….

  14. David Stiller Says:

    tunghoy,

    Each of those child MCs has a timeline of its own. For every timeline you want to stop, you have to invoke a MovieClip.stop() on it. The simple pause mechanism on this blog (AS2 and AS3 versions) only pauses the timeline it’s in, so to pause the main timeline, you use the code as is; to pause the main timeline and a bunch of movie clips … well, you’d have to loop through the properties of the main timeline in AS2 (use a for..in loop), then, using instanceof to weed out the movie clips, invoke stop() on the others. In AS3, you could use a similar approach by checking the children of the display list.

    It gets even more complicated for your sounds, if they’re ActionScript based. You’d have to invoke Sound.stop() (AS2) or SoundChannel.stop() (AS3) on each of those, or use a global volume toggle.

  15. tunghoy Says:

    OK, what I did was create Pause and Play buttons on a layer in the first MC, then copied and pasted the layer to the other MCs. Using your code for the video and code that I learned for the external sound, I got it working (copying and pasting the code to each MC, also) — the buttons pause & play the timeline and the sound.

    I suppose one day I’ll try looping through an array of child MCs so I can have just 1 set of buttons on the main timeline. But that isn’t today!

    My next part of the project is a mute button. That shouldn’t be too hard. I’ll get the volume, set it to zero on one click, then set it back to where it was on the next click.

  16. David Stiller Says:

    tunghoy,

    Sounds good! Flash usually provides numerous ways to reach a given goal.

  17. alan Says:

    Regarding the pausing action at the top, what if you want more interactivity and continue to play the main timeline whilst paused sub movies appear in the window? I need the user to be able to click on the sub clip whenever they like, (only then would i like the main timeline movie to stop) then once the submovie has played, the main shall resume exactly where it left off.
    any ideas please???

    Thanks

  18. David Stiller Says:

    alan,

    For that, you could program a button in the subclip to instruct the main timeline to stop, while instructing the subclip’s timeline to play. Assuming a button instance name of myButton:

    myButton.addEventListener(
      MouseEvent.MOUSE_UP,
      function(evt:MouseEvent):void {
        MovieClip(this.parent).stop();
        play()
      }
    );

    Is that what you mean?

  19. Alex Wilson Says:

    Hi David…I really enjoy your blog..you have saved me on more then one occasion. I’m just getting into AS3 and just bought your Foundation book.

    I have a question and it might be more then you are willing to get into. I know this may not be the right topic to post this in but I thought of it because you mentioned things like the Timer class and and totalFrames early in the posting.

    I do a lot of audio slide show-type projects where I put down an edited piece of audio down in a layer then a series of photos down under that and create fades (usually all in the main timeline but sometimes I used nested movie clips) and that sort of thing. I have looked all over the internet, in books and have asked everyone I know about how to make a scrubber bar that could show the progress of the slide show. The viewer would be able to grab a button on the bar and slide it to go to different points in the slide show. There are a lot of tutorials on the web about how to build a seek slider bar for .flv files but I can’t find any on how to build one for regular flash content.

    Like I said, this may be to complex an issue to give a quick answer so I understand if you don’t want to deal with it but if you do have a AS3 solution or if you have posted anything similar an another entry or something let me know. Thanks!

  20. David Stiller Says:

    Alex,

    Thanks for picking up Foundation! I hope you enjoy it! :) Tom and I tried to make it fun as well as informative.

    As for your non-FLV scrubber, it’s definitely a good idea and would make a useful tutorial. Hearing it as you’ve described it, two things come to my mind: a) you’d like to scrub a loaded SWF/movie clip and/or b) you’d like to scrub a series of stills, which are being displayed on a Timer-run basis. To accomplish that first one, you could look at the MovieClip.currentFrame and MovieClip.totalFrames properties — those would be comparable to the time and duration properties used in cahoots with FLVs in the draggable progress bar (AS2) article elsewhere on this blog. If you construct knob and track movie clips as described in that other article, the following — just a rough sketch — will allow the knob/track combo to scrub the frames of a movie clip with the instance name mc:

    mc.stop();
    
    var ratio:Number = track.width / mc.totalFrames;
    var vertical:Number;
    
    knob.addEventListener(
      MouseEvent.MOUSE_DOWN,
      mouseDownHandler
    );
    knob.addEventListener(
      MouseEvent.MOUSE_UP,
      mouseUpHandler
    );
    
    function mouseDownHandler(evt:MouseEvent):void {
      vertical = track.y + (track.height / 2);
      knob.startDrag(
        true,
        new Rectangle(
          track.x,
          vertical,
          track.width,
          0
        )
      );
      knob.addEventListener(
        MouseEvent.MOUSE_MOVE,
        mouseMoveHandler
      );
    }
    
    function mouseUpHandler(evt:MouseEvent):void {
      knob.stopDrag();
      knob.removeEventListener(
        MouseEvent.MOUSE_MOVE,
        mouseMoveHandler
      );
    }
    
    function mouseMoveHandler(evt:MouseEvent):void {
      mc.gotoAndStop(
        Math.floor(
          (knob.x - track.x) / ratio
        )
      );
    }

    Compare that against the AS2 code shown for the video scrubber, and you’ll see considerable (almost complete) overlap — though this version is only a scrubber, not a progress indicator. A progress indicator would require an update of the knob clip on, say, an ENTER_FRAME handler.

    The trouble with scrubbing SWFs like this is that all it does (at least, as I’ve shown it) is scrub the SWF’s (or movie clip’s) main timeline. Nested timelines — or, more confoundingly, interior animation generated by ActionScript — simply isn’t something a scrubbing mechanism can accommodate.

    If you’re talking about the other approach (choice B above), then you could theoretically store your image/still references in an array and “scrub” through the array. In which case your currentFrame/totalFrames equivalents would be a variable for counting (often i, like you would use in a for loop) and the Array.length property. To make the “scrubbing” actually do something … well, that would depend entirely on how the slideshow was programmed. It could definitely get complicated pretty quickly, especially if the scrubber needed to halt and resume a timer.

    I do keep a lengthy to-do list for this blog, and your idea(s) are definitely worth writing about, so I’ve added them to the list. The only thing iffy about the whole kaboodle is that lately I’m so incredibly behind on this blog! I’m working on two books at the moment, and that has taken its toll on my Adobe forum presence and this blog.

  21. Alex Wilson Says:

    Hi David…I’ve been pulling out what little hair I have left trying to solve a little problem I’m having communicating between parents and children with AS3. Thought you may be able to help. Lets say that you have a master swf. that you load another .swf into via addChild and the URLRequest ect.. If I have a button in the loaded .swf and wanted to communicate back to the parent .swf I would do (and have done with great success) what you described above (MovieClip(this.parent).stop():). My question is….what if I want to do the opposite. What if you have a button on the master .swf and want to communicate up to the child swf? I basically just want to stop the loaded .swf from playing with a button on the main timeline. (MovieClip(??????).stop) I’ve tried everything I can think of and nothing seem to work? HELP

  22. Alex Wilson Says:

    Hi David, so sorry to bug you. I know you are probably bombarded by tons of requests for help but I thought I would just give it a shot on this post. I have been playing with some code that you gave a long time ago (two or three posts up) to scroll through content in a timeline based slide show. It consists of a track and slider that the user can scrub to move the timeline back and forth. I’ve used that code several times and it works great, but I’m still trying to figure out how to make the knob update with the progress as the content plays on the timeline. I combined the code that you gave me on this post with some that I got from your book “Foundation Flash CS3 for Designers” on making a .mp3 player (the code to make the scrubber knob move as the audio progresses with on ENTER_FRAME handler). I think I may be close to getting this worked out but after a could weeks of working with it and posting on forums I can’t think of anything else. If you could take a look this code and maybe point me in the right direction I would appreciate it! This is for my graduate project so I can finally finish up and get that piece of paper that says I’m done! If you can help me out that would be great, if you are to swamped then thats ok, keep on writing those book, can’t wait to see on on CS4

    code:
    //////seek slider code\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
    var pausetl:Boolean = false;
    //when it is playing “pausetl” is false (the timelines initial state is playing)
    var ratio:Number = track.width / this.totalFrames;
    var vertical:Number;

    knob.addEventListener(MouseEvent.MOUSE_DOWN, seekStartDrag);
    knob.buttonMode = true;

    playTl();

    //custom functions
    function playTl():void {
    this.play();//sets the main timeline to play
    pausetl = false;//sets the variable pausetl to false
    knob.addEventListener(Event.ENTER_FRAME, seekKnobUpdate);
    }

    function pauseTl():void {
    this.stop();//sets the main timeline to stop
    pausetl = true;//sets the variable pausetl to true
    knob.removeEventListener(Event.ENTER_FRAME, seekKnobUpdate);
    }

    ////seek slider functions
    function seekStartDrag(evt:MouseEvent):void {
    pauseTl();
    vertical = track.y + (track.height / 2);
    knob.startDrag(
    true, //sets the “lockcenter” parameter to true, center point of mc locks to mouse
    new Rectangle(track.x, vertical, track.width, 0));
    //specifies the position of where the drargging should be constrained to “track’s” width and to a height of 0
    stage.addEventListener(MouseEvent.MOUSE_UP, seekStopDrag);
    stage.addEventListener(MouseEvent.MOUSE_MOVE,seekKnobUpdate);//by changing “knob” to stage I was able to leave the know with my pointer.
    }

    function seekStopDrag(evt:MouseEvent):void {
    knob.stopDrag();
    playTl();
    stage.removeEventListener(MouseEvent.MOUSE_UP, seekStopDrag);
    stage.removeEventListener(MouseEvent.MOUSE_MOVE, seekKnobUpdate);
    }

    function seekKnobUpdate(evt:MouseEvent):void {

    this.gotoAndPlay(Math.floor((knob.x - track.x) / ratio));
    //go to and play the show from the number generated by this code
    }

    //////play/pause toggle button
    pptoggle_btn.addEventListener(MouseEvent.CLICK, onClick);

    function onClick(event:MouseEvent):void {

    if (pausetl) {
    //if “pausetl” is true then run this
    playTl();
    this.pptoggle_btn.gotoAndStop(”pause”);//sets the mc pptoggle to its “pause” frame.

    } else {
    //if “pausetl” is false then run this
    pauseTl();
    this.pptoggle_btn.gotoAndStop(”play”);//sets the mc pptoggle to its “play” frame
    }
    }

  23. Tiemen Says:

    Hey David,

    Though I believe you’re super-busy again, I thought it’d never hurt to queue a blog request :)

    I vote for your thoughts on event handler organisation policy. The concept of event handlers is well documented, but I personally have trouble keeping my code organized. I’m at present working with the Zend framework, and have all kinds of event handlers and responders lying all over the place… it’s an horrible sight. I saw someone use a switch statement inside a “click handler” function to let it encompass different behavior for different targets, which has some merit, but still feels a bit blunt…?

    Please help. With my code in this state I can’t possibly get a girl to come over :’(

Leave a Reply