Add Screen Transitions to your Flash Game Using AS3

by Michael James Williams on April 1, 2009 · 16 comments

in Avoider Game Extras,Tutorial

Here’s a great quote on game design from this article by (among others) Kyle Gabler:

”Juice” was our wet little term for constant and bountiful user feedback. A juicy game element will bounce and wiggle and squirt and make a little noise when you touch it. A juicy game feels alive and responds to everything you do – tons of cascading action and response for minimal user input. It makes the player feel powerful and in control of the world, and it coaches them through the rules of the game by constantly letting them know on a per-interaction basis how they are doing.

One area where we can insert “juiciness” is the screen transitions, the things that happen when you navigate from, say, the menu screen to the play screen.

In this tutorial I’ll show you how to use the Tween class to do just that. I’m going to use the game we made in my Avoider Game tutorial as a base to experiment with; you can download a zip file with all the relevant classes and the FLA here. Of course, if you followed the tutorial, you can easily use these methods on your own game. And if you didn’t, don’t worry — this should be easy to understand either way.

Click the image below to take a look at the kind of effects I’m talking about:

AvoiderGame_Transitions_04.png

Introducing Tweens

A screen transition could be as simple as a menu fading out, or as complicated as the menu folding up into an animated paper aeroplane that flies off the screen with a satisfying “whoosh” sound. We’ll stick with the simple ones in this tutorial ;)

Take a look at DocumentClass.as. Remember, this class file corresponds to the main timeline, and we use it to handle all the screen switching. In particular, let’s look at the function that’s triggered by the “Start” button:

?View Code ACTIONSCRIPT3
69
70
71
72
73
74
75
76
77
78
79
80
81
public function onRequestStart( navigationEvent:NavigationEvent ):void
{
	playScreen = new AvoiderGame();
	playScreen.addEventListener( AvatarEvent.DEAD, onAvatarDeath, false, 0, true );
	playScreen.x = 0;
	playScreen.y = 0;
	addChild( playScreen );
 
	removeChild( menuScreen );
	menuScreen = null;
 
	stage.focus = playScreen;
}

What if, instead of simply appearing, the play screen slid in from the left?

We could accomplish this by setting up a Timer, adding an event listener to its TIMER event, and increasing the x value of the playScreen every tick until it was at the desired location. But this is irritating.

Flash is an animation tool at heart, and so naturally it includes features for automating this. The most important of these is tweening. “Tween” is short for “in-between”; we tell Flash the positions where we’d like an object to begin and end, and it takes care of moving it between those points.

It’s easiest to show this with an example. Import the Tween class:

?View Code ACTIONSCRIPT3
9
import fl.transitions.Tween;

We need to tell Flash how exactly we want the object to move. Should it slide in at a constant speed, or start by moving slowly and then speed up as it gets closer, or what? The fl.transitions.easing.Regular class includes a few different styles of movement that we can experiment with, so import that too:

?View Code ACTIONSCRIPT3
10
import fl.transitions.easing.Regular;

(FrozenGecko tells me that, in Flex, the classes to import are mx.effects.Tween and mx.effects.easing.Regular, respectively. Thanks, FrozenGecko!)

To actually use this, we just need to add a single line to the onRequestStart() function (place it after :

?View Code ACTIONSCRIPT3
72
73
74
75
76
77
78
79
80
81
82
83
84
85
public function onRequestStart( navigationEvent:NavigationEvent ):void
{
	playScreen = new AvoiderGame();
	playScreen.addEventListener( AvatarEvent.DEAD, onAvatarDeath, false, 0, true );
	playScreen.x = 0;
	playScreen.y = 0;
	addChild( playScreen );
 
	removeChild( menuScreen );
	menuScreen = null;
 
	var playScreenSlideTween:Tween = new Tween( playScreen, "x", Regular.easeIn, -400, 0, 0.25, true );
	stage.focus = playScreen;
}

Line 83 is the new one; let’s break it down:

  • The first and second arguments, playScreen and “x”, define which property of which object should be altered by the tween. In this case, we want the screen to move horizontally, so we’re adjusting the x property of playScreen. Note that we pass “x” as a string, in quotes.
  • The third argument, Regular.easeIn, defines the type of movement we want the screen to follow. This one in particular starts from a standing stop, then accelerates as it gets closer to the end. Regular.easeOut would do the opposite, starting fast and decelerating. For a full list of built-in movement styles, check the LiveDocs.
  • The fourth and fifth arguments, -400 and 0, define the start and end points of the motion. Because the screen is 400 pixels wide, I’ve decided to make it start at x=-400 and end at x=0. The y position won’t be affected, of course.
  • The final two arguments define how much time to spread the movement out over. true means that we should measure this in seconds; if we set it to false we would count in frames instead. 0.25, then, means the motion will take a mere quarter-second.

If you save and run the game, you’ll see it works as expected — click the image below to try it out:

AvoiderGame_Transitions_01.png

Trouble is, the menu screen just disappears immediately, because of that call to removeChild(). Let’s get rid of that.

?View Code ACTIONSCRIPT3
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
public function onRequestStart( navigationEvent:NavigationEvent ):void
{
	playScreen = new AvoiderGame();
	playScreen.addEventListener( AvatarEvent.DEAD, onAvatarDeath, false, 0, true );
	playScreen.x = 0;
	playScreen.y = 0;
	addChild( playScreen );
 
	/*
	removeChild( menuScreen );
	menuScreen = null;
	*/
 
	var playScreenSlideTween:Tween = new Tween( playScreen, "x", Regular.easeIn, -400, 0, 0.25, true );
	stage.focus = playScreen;
}

Actually, we could make the menu screen slide out of the way now, too:

?View Code ACTIONSCRIPT3
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
public function onRequestStart( navigationEvent:NavigationEvent ):void
{
	playScreen = new AvoiderGame();
	playScreen.addEventListener( AvatarEvent.DEAD, onAvatarDeath, false, 0, true );
	playScreen.x = 0;
	playScreen.y = 0;
	addChild( playScreen );
 
	/*
	removeChild( menuScreen );
	menuScreen = null;
	*/
 
	var playScreenSlideTween:Tween = new Tween( playScreen, "x", Regular.easeIn, -400, 0, 0.25, true );
	var menuScreenSlideTween:Tween = new Tween( menuScreen, "x", Regular.easeIn, 0, 400, 0.25, true );
	stage.focus = playScreen;
}

Note that the play screen moves from x=-400 to x=0, whereas the menu screen moves from x=0 to x=400. One enters stage left, the other exits stage right. And because we’ve given the two tweens the same parameters, the screens stay right next to each other as they move, so it looks like the play screen is pushing the menu screen out of the way:

AvoiderGame_Transitions_02.png

Tween Issues

That’s pretty cool. However, now the menu screen isn’t being removed, it won’t be garbage collected — and if you’ve read Part 12 of the base tutorial, you know that’s a bad thing. We need to remove it, but not until the tweens have finished.

Fortunately, tweens always dispatch a TweenEvent.MOTION_FINISH event when they’re done, so we can just listen for this. First we import the TweenEvent class:

?View Code ACTIONSCRIPT3
11
import fl.transitions.TweenEvent;

Then we add the listener (I’m only adding it to one tween because they both end at the same time):

?View Code ACTIONSCRIPT3
86
87
88
var playScreenSlideTween:Tween = new Tween( playScreen, "x", Regular.easeIn, -400, 0, 0.25, true );
var menuScreenSlideTween:Tween = new Tween( menuScreen, "x", Regular.easeIn, 0, 400, 0.25, true );
menuScreenSlideTween.addEventListener( TweenEvent.MOTION_FINISH, onMenuScreenTweenFinished, false, 0, true );

And then the handler:

?View Code ACTIONSCRIPT3
92
93
94
95
96
public function onMenuScreenTweenFinished( tweenEvent:TweenEvent ):void
{
	removeChild( menuScreen );
	menuScreen = null;
}

Speaking of garbage collection, there’s a bug in this code that could cause some confusing problems. Try changing the tweens’ time duration to 5 seconds, instead of 0.25, and running the game. You’ll probably find that the tweens stop part-way, for no apparent reason, and don’t trigger the event handler. Also, this won’t happen in the same place every time. There seems to be no logical explanation.

What’s happening is the onRequestStart() function is being garbage collected. (Remember I said that we couldn’t predict when exactly garbage collection would take place?) Since the tweens are defined in that function, and nowhere else, they are cleaned up as well. To fix this, we need to define the tweens at the class level:

?View Code ACTIONSCRIPT3
13
14
15
16
17
18
19
20
public class DocumentClass extends MovieClip 
{
	public var menuScreen:MenuScreen;
	public var playScreen:AvoiderGame;
	public var gameOverScreen:GameOverScreen;
	public var loadingProgress:LoadingProgress;
	public var playScreenSlideTween:Tween;
	public var menuScreenSlideTween:Tween;

Although of course we can still use the tweens within that function:

?View Code ACTIONSCRIPT3
75
76
77
78
79
80
81
82
83
84
85
86
87
public function onRequestStart( navigationEvent:NavigationEvent ):void
{
	playScreen = new AvoiderGame();
	playScreen.addEventListener( AvatarEvent.DEAD, onAvatarDeath, false, 0, true );
	playScreen.x = 0;
	playScreen.y = 0;
	addChild( playScreen );
 
	playScreenSlideTween = new Tween( playScreen, "x", Regular.easeIn, -400, 0, 5, true );
	menuScreenSlideTween = new Tween( menuScreen, "x", Regular.easeIn, 0, 400, 5, true );
	menuScreenSlideTween.addEventListener( TweenEvent.MOTION_FINISH, onMenuScreenTweenFinished, false, 0, true );
	stage.focus = playScreen;
}

Now it works. As a side note, there’s now enough time to move the avatar out of the play screen and on to the menu screen. We’ll have to fix that in a later tutorial ;)

AvoiderGame_Transitions_03.png

More Tween Effects

We’re not restricted to controlling just movement with tweens; we can alter any property (variable) of an object that we like.

For example, all display objects have an alpha property that controls their transparency. By default, this is set to 1, meaning it’s fully opaque. If set to 0, it’ll be completely invisible. Setting it to a value between 0 and 1 will make it transparent; the smaller the number, the more see-through it’ll be.

Knowing this, we can make a nice cross-fade effect:

?View Code ACTIONSCRIPT3
75
76
77
78
79
80
81
82
83
84
85
86
87
public function onRequestStart( navigationEvent:NavigationEvent ):void
{
	playScreen = new AvoiderGame();
	playScreen.addEventListener( AvatarEvent.DEAD, onAvatarDeath, false, 0, true );
	playScreen.x = 0;
	playScreen.y = 0;
	addChild( playScreen );
 
	playScreenAlphaTween = new Tween( playScreen, "alpha", Regular.easeIn, 0, 1, 0.5, true );
	menuScreenAlphaTween = new Tween( menuScreen, "alpha", Regular.easeIn, 1, 0, 0.5, true );
	menuScreenAlphaTween.addEventListener( TweenEvent.MOTION_FINISH, onMenuScreenTweenFinished, false, 0, true );
	stage.focus = playScreen;
}

(Note that I’ve changed the names of the tweens.)

When run, that looks like this:

AvoiderGame_Transitions_04.png

Curiously, the dynamic text of the score seems not to be affected. Danny tells me that dynamic text only fades properly if it uses an embedded font. Thanks Danny! So, to fix this, we need to edit the Score object, select the dynamic text, click the Embed… button on the Properties panel, and then select the characters we need (I’d recommend choosing the uppercase and lowercase letters, numerals, punctuation, and basic Latin).

We can change more than one property of the same object, too. For example, scaleX and scaleY define how squashed or stretched a display object is compared to its normal size. If scaleX is 0.5, the object will be half its normal width; scaleX=2 will make it twice as fat as usual.

?View Code ACTIONSCRIPT3
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
public function onRequestStart( navigationEvent:NavigationEvent ):void
{
	playScreen = new AvoiderGame();
	playScreen.addEventListener( AvatarEvent.DEAD, onAvatarDeath, false, 0, true );
	playScreen.x = 0;
	playScreen.y = 0;
	addChild( playScreen );
 
	playScreenScaleXTween = new Tween( playScreen, "scaleX", Regular.easeIn, 0, 1, 0.5, true );
	playScreenScaleYTween = new Tween( playScreen, "scaleY", Regular.easeIn, 0, 1, 0.5, true );
	menuScreenScaleXTween = new Tween( menuScreen, "scaleX", Regular.easeIn, 1, 10, 0.5, true );
	menuScreenScaleYTween = new Tween( menuScreen, "scaleY", Regular.easeIn, 1, 10, 0.5, true );
	menuScreenScaleYTween.addEventListener( TweenEvent.MOTION_FINISH, onMenuScreenTweenFinished, false, 0, true );
	stage.focus = playScreen;
}

The above code makes it look a bit like you’re zooming in to the play screen, which was in the top left corner all along. This might be useful for a level select screen that showed thumbnails of all the levels. Check it out:

AvoiderGame_Transitions_05.png

Finally, we might want tweens to run in sequence rather than in parallel, so that when one set finishes, another starts. For example, the menu screen could shrink to a dot, which then grows to the play screen.

First we’ll make the menu screen shrink:

?View Code ACTIONSCRIPT3
77
78
79
80
81
82
83
84
public function onRequestStart( navigationEvent:NavigationEvent ):void
{
	menuScreenXTween = new Tween( menuScreen, "x", Regular.easeIn, 0, 200, 0.5, true );
	menuScreenYTween = new Tween( menuScreen, "y", Regular.easeIn, 0, 150, 0.5, true );
	menuScreenScaleXTween = new Tween( menuScreen, "scaleX", Regular.easeIn, 1, 0, 0.5, true );
	menuScreenScaleYTween = new Tween( menuScreen, "scaleY", Regular.easeIn, 1, 0, 0.5, true );
	menuScreenScaleYTween.addEventListener( TweenEvent.MOTION_FINISH, onMenuScreenTweenFinished, false, 0, true );
}

Note that I removed all the code regarding the play screen. That’s because we’ll add it in the onMenuScreenTweenFinished() function — as well as the tweens to animate it:

?View Code ACTIONSCRIPT3
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
public function onMenuScreenTweenFinished( tweenEvent:TweenEvent ):void
{
	playScreen = new AvoiderGame();
	playScreen.addEventListener( AvatarEvent.DEAD, onAvatarDeath, false, 0, true );
	playScreen.x = 0;
	playScreen.y = 0;
	addChild( playScreen );
	stage.focus = playScreen;
 
	playScreenXTween = new Tween( playScreen, "x", Regular.easeIn, 200, 0, 0.5, true );
	playScreenYTween = new Tween( playScreen, "y", Regular.easeIn, 150, 0, 0.5, true );
	playScreenScaleXTween = new Tween( playScreen, "scaleX", Regular.easeIn, 0, 1, 0.5, true );
	playScreenScaleYTween = new Tween( playScreen, "scaleY", Regular.easeIn, 0, 1, 0.5, true );
 
	removeChild( menuScreen );
	menuScreen = null;
}

What do you think?

AvoiderGame_Transitions_06.png

Wrapping Up

Taking the above further, you could stop the game timer and background music from starting before the play screen is in place — that’s just a matter of adding another event listener to the play screen tweens, and moving the timer and music start() functions to another function, which is in turn run by the play screen tweens’ event handler.

Also, adding sound effects to a transition is a great idea. Go and play any game, and listen to what happens when you click basically any interface element. Think about what you hear when Mario goes down a pipe, or Sonic goes into the Special Zone.

I recommend using these every time you change screens. It’s amazing what an effect it has. Just don’t go overboard and put star wipes everywhere. You’d be doing yourself a huge favour if you wrote a function (or possibly a class) to manage all these transitions, rather than having to copy and paste masses of code all over the place — something like swapScreens( oldScreen:MovieClip, newScreen:MovieClip ). If you come up with any great ideas for this, please post them in the comments :)

You can use tweens for all sorts of tasks besides screen wipes, by the way. I was talking to Paul of RiverMan Media, who has created a similar system, and he had some great ideas for what to change: the position/velocity/acceleration of an object (an enemy?), sound pitch/volume, and my personal favourite, scores. Just think how much more polished it looks to have points “rack up”!

For further reading, check out the LiveDocs pages on Tweens and easings. Also, the FLA and relevant class files can be downloaded here. Hope you found this useful :)

{ 16 comments… read them below or add one }

Danny April 1, 2009 at 5:32 am

Great article.

Yes, dynamic text doesn’t fade properly with system fonts, only embedded ones. To get around this, you can embed the system font in your swf and use that instead.

Mushyrulez April 1, 2009 at 7:00 am

Wow… Finally I know how they make those changescreen animation affects.
So jealous of the good graphics makers xD

FrozenGecko April 1, 2009 at 7:33 am

It’s worth a quick note that for those of us using flex, the tween class is in the package mx.effects.tween.

The easing packages can likewise be found in mx.effects.easing.

Thanks for the great tutorial!

MichaelJWilliams April 1, 2009 at 11:52 am

Thanks all!

Danny, FrozenGecko, thank you for explaining about the fonts and the Flex. I’ve added your tips to the main tutorial :)

gianfun June 2, 2009 at 3:15 am

Wow, this is really useful!
Thanks again!!

Also, I liked the shadowbox embedding of the game!

Mushyrulez June 2, 2009 at 4:18 am

When you first put up the code for the regular move out tween, you made it 1.25, not 0.25 =/

Michael Williams June 2, 2009 at 12:28 pm

Cheers :)

Whoops, nicely spotted Mushrulez… Corrected!

BlackWolf December 17, 2009 at 6:12 pm

This is really useful!
Also, I liked build a flash game framework to help us develop big games so faster!

Thanks

Michael Williams December 20, 2009 at 3:48 am

Thanks, BlackWolf :) Are you working on any big games at the moment?

ThomasK April 21, 2010 at 5:15 pm

Hi Michael, I think there might be a small bug in the part where the screen shrinks and grows back. If I understand correctly, you set the focus on playScreen in the onRequestStart function, but playScreen itself will be created later during the onMenuScreenTweenFinished function. I’m not sure if that’s really a mistake, but it seems strange for me.

Michael Williams April 23, 2010 at 1:12 pm

Wow, nicely spotted, ThomasK! Haha this has been up a year and no-one noticed :)

You’re totally right — I’ve changed the code in the tutorial to fix the bug. That explains why the keyboard controls don’t work in that example.

Daniel K January 23, 2011 at 2:44 pm

Thanks for the great tutorial … I’m implementing it right now in my game.
Great blog BTW .. I found it via Emanuele Feronato’s post about your book (going to get my copy next week).
Bookmarked !

Jos June 15, 2011 at 3:02 am

So… if you use a tween to animate an instance of a class can you listen for the event to complete in some way so you could do stuff like add a “thump” sound effect or pause your playscreen until the event occurs.

Jos June 15, 2011 at 3:05 am

Der. Always read first. Thanks again!

ExplosionsHurt August 21, 2011 at 11:31 am

Just a little glitch in the last one:

If you rapid-fire click on the play button, then weird stuff happens.

fisa November 21, 2012 at 12:57 pm

I played the game and my best score was unlimited! just move the player out from the screen…. The game will not be finished and score increases continuously

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

Previous post:

Next post: