Handling Basic Animation with State MovieClips

by Michael James Williams on June 18, 2009 · 18 comments

in Avoider Game Extras,Tutorial

In Handling Basic Animation with a Divided Timeline we added animation states to a game’s characters by splitting each of their timelines up.

Now let’s take a look at the second method for handling animation in Flash games: using one MovieClip for each animation state.

If you didn’t follow the divided timeline tutorial, I recommend reading through it to get some idea of what that method involves. That goes double if you’re a little unclear on how to deal with animation in the first place; I’ll just skim over those basics in this post.

Just as in the last tutorial, I’ll start from a game that has no animation — so if you’ve already added animation using a divided timeline, you might want to revert to an earlier version of your FLA for this. The game I’m going to use is (as usual) the one from my AS3 avoider game tutorial, and you can download all the files I’ll be using from this link.

Here’s what it’ll look like when we’re done:

Snapshot_O06.png
Click to play

What’s the Basic Idea?

In my game, the avatar is a MovieClip, and so are each of the enemies. These MovieClips are added directly to the play screen, so we can move them about just by changing their x and y values.

Due to inheritance, every MovieClip is also a DisplayObjectContainer, which means we could add another MovieClip to, say, our enemy MovieClip, just like how that MovieClip is added to the play screen. And because this new MovieClip is inside the enemy MovieClip, if we change the enemy’s x and y positions, the new MovieClip will move with the enemy!

OK, that’s a little confusing when written down, and doesn’t really explain why we would want to do this. It’s best if I show you.

First, edit the Enemy movie clip in the Library:

screenshot

Select it all (if all the layers are unlocked, you can click Edit > Select All)…

screenshot

…and select Modify > Convert to Symbol. This familiar-looking dialog box will appear:

screenshot

Call it Enemy_DefaultState, and export it for ActionScript with the same class name. (You’ll probably get that message about no such class being in the classpath; don’t worry about it.) Hit OK.

Now our Enemy movie clip doesn’t contain the lines and fills that make up the enemy image, but another MovieClip that in turn contains those lines and fills. You can tell this just by clicking on it:

screenshot

This means we could have just created a new movie clip as normal, called it Enemy_DefaultState, dragged it into the Enemy symbol, and deleted the old graphic — that’d give the same result.

If you run the game, you’ll find it works as normal:

screenshot

That’s reassuring, but not very useful so far. Let’s get this animating so that there was a point to all that. Go into editing mode for the Enemy_DefaultState movie clip by double-clicking it in the library.

Now, add some new frames to the timeline, just as we did in the earlier tutorial. (In fact, I’m just going to copy and paste the frames from before!)

screenshot

Don’t bother adding a label to the first frame or adding a blank keyframe. Just get the animation in there. Run the game again, and you’ll see that it’s all animating nicely:

Snapshot_O03.png
Click to play

Great! Now let’s have a go at different animation states with the avatar.

One MovieClip Per State

You can probably guess what we need to do here. We’ve got to end up with five different movie clips, one for each state of the avatar’s animation. That is:

  1. Avatar_StandingStillState
  2. Avatar_MovingLeftState
  3. Avatar_MovingRightState
  4. Avatar_MovingUpState
  5. Avatar_MovingDownState

Go ahead and make each of these. It’s probably easiest just to make five brand new movie clips in the library. Again, don’t bother with labelling frames or adding blank keyframes with a stop() action.

When you’re done, you need to end up with the Avatar_StandingStillState movie clip in the place of the original lines and fills inside the Avatar movie clip.

Run the game, just to make sure it’s working:

Snapshot_O04.png
Click to play

OK, good. How do we switch between these states, though?

A Top Down Approach

First, let’s think about how we’d like to be able to switch states. In the divided timeline tutorial, we did it with this code in AvoiderGame.as:

?View Code ACTIONSCRIPT3
if ( downKeyIsBeingPressed )
{
	if ( avatar.currentAnimationState != "MovingDown" )
	{
		avatar.gotoAndPlay("MovingDown");
		avatar.currentAnimationState = "MovingDown";
	}
	avatar.moveABit( 0, 1 );
}
else if ( upKeyIsBeingPressed )
{
	if ( avatar.currentAnimationState != "MovingUp" )
	{
		avatar.gotoAndPlay("MovingUp");
		avatar.currentAnimationState = "MovingUp";
	}
	avatar.moveABit( 0, -1 );
}
else if ( leftKeyIsBeingPressed )
{
	if ( avatar.currentAnimationState != "MovingLeft" )
	{
		avatar.gotoAndPlay("MovingLeft");
		avatar.currentAnimationState = "MovingLeft";
	}
	avatar.moveABit( -1, 0 );
}
else if ( rightKeyIsBeingPressed )
{
	if ( avatar.currentAnimationState != "MovingRight" )
	{
		avatar.gotoAndPlay("MovingRight");
		avatar.currentAnimationState = "MovingRight";
	}
	avatar.moveABit( 1, 0 );
}
else
{
	//player is not pressing any keys at all
	if ( avatar.currentAnimationState != "StandingStill" )
	{
		avatar.gotoAndPlay("StandingStill");
		avatar.currentAnimationState = "StandingStill";
	}
}

I think this is a pretty good layout, but we’ll have to change those gotoAndPlay() calls to something else. How about avatar.switchState( "StandingStill" ) instead?

?View Code ACTIONSCRIPT3
if ( downKeyIsBeingPressed )
{
	if ( avatar.currentAnimationState != "MovingDown" )
	{
		avatar.switchState("MovingDown");
		avatar.currentAnimationState = "MovingDown";
	}
	avatar.moveABit( 0, 1 );
}
else if ( upKeyIsBeingPressed )
{
	if ( avatar.currentAnimationState != "MovingUp" )
	{
		avatar.switchState("MovingUp");
		avatar.currentAnimationState = "MovingUp";
	}
	avatar.moveABit( 0, -1 );
}
else if ( leftKeyIsBeingPressed )
{
	if ( avatar.currentAnimationState != "MovingLeft" )
	{
		avatar.switchState("MovingLeft");
		avatar.currentAnimationState = "MovingLeft";
	}
	avatar.moveABit( -1, 0 );
}
else if ( rightKeyIsBeingPressed )
{
	if ( avatar.currentAnimationState != "MovingRight" )
	{
		avatar.switchState("MovingRight");
		avatar.currentAnimationState = "MovingRight";
	}
	avatar.moveABit( 1, 0 );
}
else
{
	//player is not pressing any keys at all
	if ( avatar.currentAnimationState != "StandingStill" )
	{
		avatar.switchState("StandingStill");
		avatar.currentAnimationState = "StandingStill";
	}
}

Looks good to me. Add this code, but don’t bother running it — obviously we’ll get a whole load of errors since our Avatar class doesn’t have a currentAnimationState property or a switchState() function. Let’s add them now:

?View Code ACTIONSCRIPT3
package 
{
	import flash.display.MovieClip;
	public class Avatar extends MovieClip 
	{
		public var currentAnimationState:String;
 
		public function Avatar() 
		{
			currentAnimationState = "StandingStill";
		}
 
		public function switchState( newState:String ):void
		{
			//we'll change the state here
		}
 
		public function moveABit( xDistance:Number, yDistance:Number ):void
		{
			var baseSpeed:Number = 3;
			x += ( xDistance * baseSpeed );
			y += ( yDistance * baseSpeed );
		}
	}
}

If you run this now, you shouldn’t get any errors.

Now for the tricky part: actually changing the state. Any ideas?

Switching States

We could try doing things in the same way the background image gets changed; that is, by simply adding a new movie clip on top of the old one:

?View Code ACTIONSCRIPT3
public function switchState( newState:String ):void
{
	if ( newState == "MovingLeft" )
	{
		addChild( new Avatar_MovingLeftState() );
	}
	else if ( newState == "MovingRight" )
	{
		addChild( new Avatar_MovingRightState() );
	}
	else if ( newState == "MovingUp" )
	{
		addChild( new Avatar_MovingUpState() );
	}
	else if ( newState == "MovingDown" )
	{
		addChild( new Avatar_MovingDownState() );
	}
	else if ( newState == "StandingStill" )
	{
		addChild( new Avatar_StandingStillState() );
	}
}

Try it out:

Snapshot_O05.png
Click to play

Hmm, not quite right is it? The avatars just pile up on top of each other, and it looks a mess.

Alright, so we need to remove the old avatar animation before adding a new one. This means we need some way of accessing the old one — let’s give it an instance name.

Just edit the Avatar symbol, click the Avatar_StandingStillState instance that’s sitting there, and in the Properties panel, set its instance name to currentAnimationMovieClip.

Now, if you’ve turned off Automatically declare stage instances, as I have, you’ll get this error when running the game:

ReferenceError: Error #1056: Cannot create property currentAnimationMovieClip on Avatar.

(If you haven’t turned it off, then do so now, because you’ll need to. Check out Part 12 of my avoider game tutorial for more.)

This error means we need to define currentAnimationMovieClip inside the Avatar class, which means adding a public var currentAnimationMovieClip to the class-level variables. We know that this animation has a class name of Avatar_StandingStillState, so we can use that:

?View Code ACTIONSCRIPT3
package 
{
	import flash.display.MovieClip;
	public class Avatar extends MovieClip 
	{
		public var currentAnimationState:String;
		public var currentAnimationMovieClip:Avatar_StandingStillState;

Now we just need to remove the current animation, replace it with a new one, and add that new one to the Avatar, like so:

?View Code ACTIONSCRIPT3
public function switchState( newState:String ):void
{
	removeChild( currentAnimationMovieClip );
 
	if ( newState == "MovingLeft" )
	{
		currentAnimationMovieClip = new Avatar_MovingLeftState();
	}
	else if ( newState == "MovingRight" )
	{
		currentAnimationMovieClip = new Avatar_MovingRightState();
	}
	else if ( newState == "MovingUp" )
	{
		currentAnimationMovieClip = new Avatar_MovingUpState();
	}
	else if ( newState == "MovingDown" )
	{
		currentAnimationMovieClip = new Avatar_MovingDownState();
	}
	else if ( newState == "StandingStill" )
	{
		currentAnimationMovieClip = new Avatar_StandingStillState();
	}
 
	addChild( currentAnimationMovieClip );
}

Does that make sense? Remember, the currentAnimationMovieClip value doesn’t contain any particular object, it just points to one.

Try it out. Hey, errors:

1067: Implicit coercion of a value of type Avatar_MovingLeftState to an unrelated type Avatar_StandingStillState.
1067: Implicit coercion of a value of type Avatar_MovingRightState to an unrelated type Avatar_StandingStillState.
1067: Implicit coercion of a value of type Avatar_MovingUpState to an unrelated type Avatar_StandingStillState.
1067: Implicit coercion of a value of type Avatar_MovingDownState to an unrelated type Avatar_StandingStillState.

What’s going on here? Well, “implicit coercion” means we’re trying to make a round peg fit in a square hole — in this case, we’re trying to get a variable of type Avatar_StandingStillState to point to an object of type Avatar_MovingLeftState (and then Avatar_MovingRightState, and so on).

Specifically, the first error’s occurring on this line:

?View Code ACTIONSCRIPT3
currentAnimationMovieClip = new Avatar_MovingLeftState();

And that makes perfect sense, when you think about it; currentAnimationMovieClip is an Avatar_StandingStillState, and as far as Flash is aware, that’s nothing like an Avatar_MovingLeftState.

You and I, on the other hand, know that these two things have something in common: they both extend MovieClip, so they both are MovieClips. So let’s try to generalise a little bit. Go back up to the top of Avatar.as, where we defined that class-level variable, and change its type:

?View Code ACTIONSCRIPT3
package 
{
	import flash.display.MovieClip;
	public class Avatar extends MovieClip 
	{
		public var currentAnimationState:String;
		public var currentAnimationMovieClip:MovieClip;

The idea is that now when we run this line:

?View Code ACTIONSCRIPT3
currentAnimationMovieClip = new Avatar_MovingLeftState();

…we shouldn’t get an error, because we’re just saying “set this MovieClip variable to point to this Avatar_MovingLeftState (which is a MovieClip)”. Nothing wrong with that.

Test it out:

Snapshot_O06.png
Click to play

Success!

Wrapping Up

The finished files I used in this tutorial are available in a zip file here.

I didn’t mention it above, but you should add all the state movie clips to the AssetHolder and uncheck Export in first frame if you want them to be preloaded.

Also, I mentioned the way the background image keeps getting a new movie clip added to it every time you change levels. This was the best solution at the time, but now you know how to improve it by removing the old ones. That’s definitely worth changing.

Anyway, now you know two main methods of animation in Flash and AS3. Which do you prefer?

You might have realised that we could use a movie clip with a divided timeline as a state movie clip, and be wondering whether we could simplify things in this way. The answer is yes, and I’d love to write more about it, but unfortunately we’ve reached the end of this tutorial. Look, the comments are right there, just a couple of inches below this line!

{ 16 comments… read them below or add one }

fred June 18, 2009 at 7:08 am

Hey MJW,
I love this animation series, great info everywhere.
Now how bout this:
To go back to the shoot-em-up version of the game. What if I wanted to make the smilies explode when shot, and then disappear? I understand that I would add the explosion as a separate MovieClip, perhaps, but how would I know when to delete the explosion as soon as it finishes?
(my homebrew method used a Timer to count approximate frames of animation when FrameLabels didn’t work out, so I’m curious as to your opinion on this)

Rasmus Wriedt Larsen (Snurre) June 18, 2009 at 11:57 am

You made a little error :) You’re setting the currentAnimationState twice, both in the main loop like this:
if ( avatar.currentAnimationState != “MovingRight” )
{
avatar.switchState(“MovingRight”);
avatar.currentAnimationState = “MovingRight”;
}
and then in the switchState function.

But great post anyway :P

To fred:
I would make a class for the explosion, making it update with a timer. When the explosion is added, on every tick decrease the alpha of the explosion (this gives a nice fade effect) and when the alpha is <= 0, you remove the explosion

Michael Williams June 18, 2009 at 1:09 pm

Cheers guys!

@Fred: You’ve run a little ahead of me :P I’ve actually been working towards getting explosions for the shoot-’em-up, which is why I paused that series temporarily, so I’ll have a full tutorial up eventually.

In the meantime, Rasmus’s method is a pretty simple way of doing things. Even if the animation starts looping, having explosion.alpha at 0 will mean the player can’t see it, and you can remove it at your leisure.

Alternatively, you could add a Event.ENTER_FRAME event listener to your explosion when it starts — this is like the TimerEvent.TIMER, except it fires off at the start of each frame of the animation, rather than every 25ms (or whatever time period). That means you could always be aware of the current frame label or number, and would never miss any out.

Another option would be to get the final frame of the animation to dispatch a custom event, say AnimationEvent.EXPLOSION_FINISHED, which you could listen out for.

@Rasmus: Whoops! Nicely spotted, I’ve corrected that now, thanks. :)

tomdeaap June 19, 2009 at 5:40 pm

You could also make the class for the explosion look a bit like this …

if ( currentFrame == totalFrames )
{
/// stuff here, if you want to use alpha, and the removing.
}

default0 October 22, 2009 at 12:28 pm

Hey guys!

Heres some useful information for anyone wanting to animate some hero guy walking.

Step 1: Create your walking animation in ANOTHER MovieClip

Step 2: Copy this clip in your “Walk” Frame of the actual Hero MovieClip

Step 3: Always when the player presses a Key causing the Hero to walk, go to the “Walk” Frame useing gotoAndStop().

Result: The nested MovieClip will play and you got your animation without breaking concepts of this tutorial! Great :)

Best regards

Michael Williams November 2, 2009 at 11:59 pm

@tomdeaap: whoops, missed your comment somehow! Nice tip :)

@default0: That is also a nice tip! Thanks :) You’ve managed to combine State MovieClips with Divided Timelines; neat :)

Twan May 7, 2010 at 12:53 pm

Again a great tutorial, everything works up untill the last step. As soon as i add the remove child i get the following error as soon as a i press a button to move my character

TypeError: Error #2007: Parameter child must be non-null.

Any idea´s ?

Twan May 7, 2010 at 1:03 pm

Hmm I’m not sure what is going, it has something to do with my onTick and the fact that you remove the avatar before adding the new one i guess. I did manage to fix it though by doing the following:

public function switchState( newState:String ):void {

        if ( newState == "MovingLeft" ) {
            addChild( new Avatar_Left() );
        } else if ( newState == "MovingRight" ) {
            addChild( new Avatar_Right() );
        } else if ( newState == "MovingUp" ) {
            addChild( new Avatar_Up() );
        } else if ( newState == "MovingDown" ) {
            addChild( new Avatar_Down() );
        } else if ( newState == "StandingStill" ) {
            addChild( new Avatar_Default() );
        }
        removeChildAt(numChildren-2);

}

Michael Williams May 7, 2010 at 8:29 pm

Hey Twan,

Nice solution. Not sure what was causing the first one. Did you have your removeChild() at the end of the function, maybe?

Lisa June 10, 2010 at 9:11 pm

I gots a problem. I think I’ve done everything right… but clearly I have not.

I am using CS3 actionscript 3.0 and I am getting this error in the output window. (Corky is my equivalent of Avatar):

TypeError: Error #2007: Parameter child must be non-null.
at flash.display::DisplayObjectContainer/removeChild()
at Corky/switchState()
at Game/onTick()
at flash.utils::Timer/_timerDispatch()
at flash.utils::Timer/tick()
TypeError: Error #2007: Parameter child must be non-null.
at flash.display::DisplayObjectContainer/removeChild()
at Corky/switchState()
at Game/onTick()
at flash.utils::Timer/_timerDispatch()
at flash.utils::Timer/tick()
TypeError: Error #2007: Parameter child must be non-null.
at flash.display::DisplayObjectContainer/removeChild()
at Corky/switchState()
at Game/onTick()
at flash.utils::Timer/_timerDispatch()
at flash.utils::Timer/tick()

Lisa June 10, 2010 at 9:24 pm

In Corky.as when I remove this line:

removeChild(currentAnimationMovieClip);

The currentAnimationClip container displays all instance swaps …so it the switching is working.

Twan June 11, 2010 at 5:36 pm

Have a look at my switchstate I put just above your post. I got the same error and fixed it by removing the old movieclip after adding the new one

Michael Williams June 12, 2010 at 2:02 pm

Cheers, Twan. Did that work, Lisa?

Lisa June 12, 2010 at 11:17 pm

Actually it was the movie clip I was using. I had to cut the frames, paste them into a new movie clip, then drag an instance of the new movie clip back onto the old one so that I could give it an instance name. Somehow I missed that in the instructions. It is working now. Thanks for the help and the great tutorial.

Michael Williams June 22, 2010 at 3:14 pm

Glad to hear it’s working now :)

flashnoob February 12, 2011 at 4:25 am

hey guys .. i know this is an old forum but could someone please help me? i have a bug in my game which occurs rarely but its there. for some reason one enemy will randomly just stop moving while the others are moving, etc just fine.
the animation will continue to loop but the player/avatar can not interact with it and it just sits there.

it doesnt produce and compile errors or outputs so i have no idea how to fix it. i think
it might have something to do with collisions (my avatar has health points).

is there any way of putting in a listener or “if” statement to find the particular enemy and remove it. say:

if( //enemy isn’t moving ){
removeChild(enemy)
}

please help!

Leave a Comment

Writing code? Write <pre> at the start and </pre> at the end to keep it looking neat.

Anti-Spam Protection by WP-SpamFree

{ 2 trackbacks }

Previous post:

Next post: