Warning: Declaration of thesis_comment::start_lvl(&$output, $depth, $args) should be compatible with Walker::start_lvl(&$output, $depth = 0, $args = Array) in /nfs/c03/h08/mnt/50298/domains/gamedev.michaeljameswilliams.com/html/wp-content/themes/thesis_18/lib/classes/comments.php on line 0

Warning: Declaration of thesis_comment::end_lvl(&$output, $depth, $args) should be compatible with Walker::end_lvl(&$output, $depth = 0, $args = Array) in /nfs/c03/h08/mnt/50298/domains/gamedev.michaeljameswilliams.com/html/wp-content/themes/thesis_18/lib/classes/comments.php on line 0

Warning: Declaration of thesis_comment::start_el(&$output, $comment, $depth, $args) should be compatible with Walker::start_el(&$output, $object, $depth = 0, $args = Array, $current_object_id = 0) in /nfs/c03/h08/mnt/50298/domains/gamedev.michaeljameswilliams.com/html/wp-content/themes/thesis_18/lib/classes/comments.php on line 0

Warning: Declaration of thesis_comment::end_el(&$output, $comment, $depth, $args) should be compatible with Walker::end_el(&$output, $object, $depth = 0, $args = Array) in /nfs/c03/h08/mnt/50298/domains/gamedev.michaeljameswilliams.com/html/wp-content/themes/thesis_18/lib/classes/comments.php on line 0
AS3 Avoider Game Tutorial, Part 3: Game Over — Michael James Williams

AS3 Avoider Game Tutorial, Part 3: Game Over

by Michael James Williams on October 9, 2008 · 223 comments

in Avoider Game Base,Tutorial

*(This tutorial is also available in Spanish)*

###Introduction

In this part of my AS3 conversion of Frozen Haddock‘s avoiding game tutorial, I’ll show you how to add a game over screen. Click the image below to see how the game will play by the end of this post.

screenshot

Now, I already said in the first part of this tutorial that we’d be using just one frame of the timeline. This means that we can’t just stick a sign saying “game over” on frame 2 and then run a *nextFrame()* command when our character dies, so what can we do?

The answer’s pretty simple: we just slap a big “Game Over” sign on top of everything else when we need it, like pulling a curtain down at the end of a play.

###Making an Overlay

Let’s get started. If you’ve been following the earlier parts of the tutorial, make a copy of your game folder and open the FLA in this new copy. Otherwise, download the zip file from Part 2, extract it somewhere, and open *AvoiderGame-MJW.fla*.

Create a new Movie Clip (*Insert > New Symbol*) called *GameOverText*, and draw something that will indicate to the player that their game is indeed over. Here’s mine:

screenshot

That font is Arial Black, and I’ve coloured it a dark red to make it stand out against the grey background. Note that I’ve centred the text around the registration point — see Part 1 for a reminder on how to do this.

If you’re using text, Flash might decide that it should be *dynamic* — that is, that it can be changed using code. Until we add scores, we don’t need this functionality, so click on your text and in the Properties panel, make sure to change the drop-down list from *Dynamic Text* or *Input Text* to *Static Text*. Otherwise, you’ll get an error:

1046: Type was not found or was not a compile-time constant: TextField.

Next we need to make this symbol available to our code. Find it in the Library, right-click it, and select *Properties*. In the *Linkage* panel, check the *Export for ActionScript* box, make a note of the class name (should be *GameOverText*) and click OK.

Open your document class file, either by directly opening the AS file in the *Classes* folder (mine’s called *AvoiderGame.as*), or by finding the document properties (click on an empty space, and select*Window > Properties* if you can’t see them) and clicking the little pencil icon next to the *Document class* text box. Find the *onTick* function, and then the piece of code that determines what happens if an enemy hits the player’s avatar:

if ( avatar.hitTestObject( enemy ) ) 
{
	gameTimer.stop();
}

So at the minute, this just stops the whole game if the avatar collides with an enemy. Nothing wrong with that, but it looks like the game has just crashed. Let’s make our Game Over text appear as well:

if ( avatar.hitTestObject( enemy ) ) 
{
	gameTimer.stop();
	var gameOverText:GameOverText = new GameOverText();
	gameOverText.x = 200;
	gameOverText.y = 150;
	addChild( gameOverText );
}

This code should be pretty familiar to you after reading Parts 1 and 2. Remember you can change the values *200* and *150* to alter where on the screen the Game Over text will appear.

Save everything and run it (*Control > Test Movie*) and when you hit an enemy, you should get something like this:

screenshot

Excellent.

###Really Rubbing It In

An “overlay” game over notice like this is fine — it worked for Sonic — but what about a whole game over *screen*, like in Frozen Haddock’s game?

We can start by doing the same thing that we’re doing now, but making the symbol fill the whole game screen. Create another new Movie Clip, this time called *GameOverScreen*, and draw a big black rectangle, of any size. Click the rectangle, and change its size to match the dimensions of your stage. (You can change the size of the rectangle by altering its W and H — Width and Height — values in its Properties or Info panel, and you can see the dimensions of the stage by clicking *Modify > Document*.)

This time, we want the registration point of the symbol to be in the top-left corner. Why? Because then we can align it to the stage by setting its location to (0, 0), rather than having to worry about where the centre of the stage is. You can change the registration point by using the *Align* panel again:

screenshot

Alternatively, you can change the X and Y properties of the rectangle to *0.0*, just as you changed the Width and Height.

Let’s add some text:

screenshot

That’ll do. You can pretty it up if you like. When you’re ready, *Export* GameOverScreen for ActionScript, just as we did with GameOverText.

Open the document class again, and find the code we modified before:

if ( avatar.hitTestObject( enemy ) ) 
{
	gameTimer.stop();
	var gameOverText:GameOverText = new GameOverText();
	gameOverText.x = 200;
	gameOverText.y = 150;
	addChild( gameOverText );
}

It’s pretty simple to make this work with our new screen. Have a go yourself if you like. My code’s here:

if ( avatar.hitTestObject( enemy ) ) 
{
	gameTimer.stop();
	var gameOverScreen:GameOverScreen = new GameOverScreen();
	gameOverScreen.x = 0;
	gameOverScreen.y = 0;
	addChild( gameOverScreen );
}

Note that I changed the x and y coordinates to zero. If you save and run the game and hit an enemy, you’ll see something like the following:

screenshot

But we can go further with this. We can put the actual *game* inside its own symbol. This way, rather than dropping a curtain down in front of the stage and leaving all the actors standing around behind it, we can have all the actors in their own glass box, and remove this box from the stage when we drop the game over curtain. This makes the stage much less cluttered, and the actors easier to manage. The only question is, *have I gone too far with this analogy?*

###Adding a Game Screen

Let me clarify this a little. At the minute, our game is set out like this:

screenshot

The document class is controlling pretty much everything. If we continue making it do so, then if we want to add a title screen, a menu screen, a few more levels, a character selection screen, and so on, then the document class is going to get very very bloated.

Let’s separate things a little bit. Here’s what I’m proposing:

screenshot

Much neater! The document class duties only consist of making sure the player is seeing the right screen. Each individual screen manages its relevant parts. Later we might break this down even further, but this is absolutely fine for now.

How do we implement this? Well, you probably won’t be surprised if I tell you that we need to make a new Movie Clip — this time, call it *PlayScreen*.

This new screen is going to do roughly the same thing as our entire game was doing by the end of the last part. That is, it’s going to contain the avatar, and it’s going to generate the enemies and make them move within it. One important difference between using a Movie Clip to contain everything versus using a document (as we were doing before) is that a document has a default background already set up (ours is grey), but a Movie Clip doesn’t — we have to add one ourselves.

Let’s do that now! Edit your *PlayScreen* Movie Clip and draw a filled rectangle. Just as with *GameOverScreen*, make this rectangle the same size as the stage, and set its registration point to the top-left corner. I’ve made mine a very light blue, so as to stand out against the editor’s background:

screenshot

We’re not restricted to using a plain background this time. Because I’m so vain, I’ll demonstrate this by sticking my initials all over the place:

screenshot

Alright great. Obviously you don’t have to use my initials. Or any initials at all, for that matter. You could draw a cave, or a black hole, or a chat window (since the game is about smilies! get it?) or whatever.

Now for the code. I already said that this screen is going to do the same thing as our game already was doing, so you can imagine that it’ll require almost exactly the same code. We could rewrite all this code from scratch. We could copy and paste it, renaming bits as required. Or… we could take advantage of our marvellous object-oriented design.

Remember that the entire game, the entire document, is basically a Movie Clip. When we assign it a document class, this is essentially the same as selecting a Movie Clip from the library and assigning it a Class when we “Export it for ActionScript”. So, there’s no reason that we couldn’t assign *PlayScreen* the same class that has, up till now, been the document class.

Right-click *PlayScreen* in the library, select *Properties*, and check *Export for ActionScript*. This time, instead of accepting the default value, enter the name of your document class. Click OK.

screenshot

Oh, right. First we’ll have to make a new document class, since no two different objects can share the same class. Cancel the *Properties* box.

###A New Class of Document

Hit *File > New* and select *ActionScript File*. Enter the (by now familiar) code:

package 
{
	import flash.display.MovieClip;
	public class DocumentClass extends MovieClip 
	{
		public function DocumentClass() 
		{
			
		}
	}
}

Save this in the *Classes* directory as *DocumentClass.as*. (There’ll be no confusing the purpose of **this** file!) Now, back in your FLA, change the document class to *DocumentClass*.

screenshot

Check everything’s fine by clicking that pencil icon — if it’s all OK, that should bring up the AS file that you just created.

**Now** you can set the *PlayScreen*’s class to *AvoiderGame*. So do so!

If you save and run the game now, nothing will happen — that’s because the document class isn’t pulling in the PlayScreen. Head back to the *DocumentClass* AS file and modify it like so (lines 5, 7, and 8):

package 
{
	import flash.display.MovieClip;
	public class DocumentClass extends MovieClip 
	{
		public var playScreen:AvoiderGame;
		
		public function DocumentClass() 
		{
			playScreen = new AvoiderGame();
			addChild( playScreen );
		}
	}
}

This is pretty much exactly the same code as the *AvoiderGame* AS file uses to create a new Avatar, though here we’re using it to create a whole *game*. Save it and run:

screenshot

Awesome.

###AvoiderGame Once Removed

Actually we’re not quite done yet. Here’s the sketch of my proposed new setup:

screenshot

And here’s our current setup:

screenshot

We’ve got the same problem as before — a bloated class file — only now it’s not the document class. We want the document class to be pulling in the Game Over screen when the player dies. How can we do this?

If we were using AS2, we might use *_root* or *_stage* to tell the document to do something. We’re not going to do that.

In previous versions of this tutorial, I let the *playScreen* run a function in the document class directly. That’s probably the simplest way of handling things, but it gets very messy very quickly. Even with a game as simple as this, I got a few emails letting me know of problems this quick fix had caused. So we’re not going to do that, either.

Back in Part 1, when we were adding the game timer, I said:

> An event listener is like a robot that’s constantly checking to see if a particular “event” has occurred, and which runs another function if so.

The line we used for this was:

gameTimer.addEventListener( TimerEvent.TIMER, onTick );

(If you’ll recall, that line ran a function called *onTick* whenever the Timer “ticked”.)

What I’d like to do now is place a similar event listener on *playScreen* (line 11):

public function DocumentClass() 
{
	playScreen = new AvoiderGame();
	playScreen.addEventListener( AvatarEvent.DEAD, onAvatarDeath );
	addChild( playScreen );
}

AvatarEvent.DEAD isn’t a built-in Flash event, of course, but we’ll get to that later. Right now let’s write the *onAvatarDeath* function into the document class:

public function onAvatarDeath( avatarEvent:AvatarEvent ):void
{
	
}

This is just like the *onTick* function from *AvoiderGame.as*. Just as then, we need to give it some code to run:

public function onAvatarDeath( avatarEvent:AvatarEvent ):void
{
	var gameOverScreen:GameOverScreen = new GameOverScreen();
	gameOverScreen.x = 0;
	gameOverScreen.y = 0;
	addChild( gameOverScreen );
	
	playScreen = null;
}

Lines 17-20 are copied straight from *AvoiderGame.as*. Line 22 introduces a new keyword: *null*. By setting an object equal to *null* you are essentially resetting it. All event listeners are removed, all contained objects are erased, all functions cease to exist. After being set to null, playScreen is in exactly the same state as before the line *playScreen = new PlayScreen();* — this means that the next time we want to use it, we need to run that line again. Nullifying the playScreen will also remove it from view.

Now we need to actually trigger this event.

###A Grand Event

You know how our Avatar, Enemy and AvoiderGame classes *extend* MovieClip so that they can do everything a MovieClip can do? Well, TimerEvent *extends* a class called Event in the same way. This means that we can make our own kind of event (the AvatarEvent I’ve been talking about) by *extending* Event, too.

So, let’s try that out, in the same way that we extended MovieClip all those times. Start a new AS file, AvatarEvent.as:

package  
{
	import flash.events.Event;
	public class AvatarEvent extends Event 
	{
		public function AvatarEvent()
		{
			
		}
	}
}

This looks about right, but actually, there’s a problem. When you create an Event of any kind, it expects you to pass through the *type* of event in the same way that our Enemy class expects you to pass through an x- and y-coordinate — for example, an Event to tell an object to scroll is created like this:

new Event( Event.SCROLL )

We’ve not provided a way to specify the type of event like that in our code, so let’s add that in:

package  
{
	import flash.events.Event;
	public class AvatarEvent extends Event 
	{
		public function AvatarEvent( type:String )
		{
			
		}
	}
}

Now we need to add a type of event for us to use. We can use a *public const* for this; it’s like a *public var* except it can’t be changed after you’ve hit Test Movie:

package  
{
	import flash.events.Event;
	public class AvatarEvent extends Event 
	{
		public static const DEAD:String = "dead";
		
		public function AvatarEvent( type:String )
		{
			
		}
	}
}

Great, now we’ll be able to write:

new AvatarEvent( AvatarEvent.DEAD )

…because *static* consts belong to the *class* AvatarEvent, not to any specific instance of AvatarEvent.

It’s nearly ready to use, but we need to figure out what to do with this *type* that gets passed in to the new AvatarEvent. Well, actually… we don’t! Events already know what to do with a *type* that’s passed in to them, so we just need to “borrow” their code.

We can do this using the *super()* function, like so:

package  
{
	import flash.events.Event;
	public class AvatarEvent extends Event 
	{
		public static const DEAD:String = "dead";
		
		public function AvatarEvent( type:String )
		{
			super( type );
		}
	}
}

What *super( type )* does is, it runs the code from inside the constructor function of the *Event* class, and passes *type* through to that. That means we let the existing code (which we can’t see) inside the *Event* class deal with everything. Fine by me 🙂

(We’ll look at *super* in more detail in Part 5. In the meantime, feel free to check out my post about extends, override, and super.)

Now that we have the event listener and the event class, all that remains is to fire off the event when the avatar dies (i.e., when it hits an enemy). Head back to *AvoiderGame.as* and change this:

if ( avatar.hitTestObject( enemy ) ) 
{
	gameTimer.stop();
	var gameOverScreen:GameOverScreen = new GameOverScreen();
	gameOverScreen.x = 0;
	gameOverScreen.y = 0;
	addChild( gameOverScreen );
}

to this:

if ( avatar.hitTestObject( enemy ) ) 
{
	gameTimer.stop();
	dispatchEvent( new AvatarEvent( AvatarEvent.DEAD ) );
}

Line 49 fires off an AvatarEvent of type DEAD. That’s all there is to it. I removed all the code about the Game Over screen because we’re dealing with that in the document class now.

Save it and run, and hey, what do you know, it all works as it should.

I realise that this last big change didn’t change the game in a way that the player could notice, but reorganising the internal structure like this will certainly make it a lot easier to add new features in the future. I’ll prove this in the next part, where we’ll add a title screen and a reset button with minimal effort — available here.

Also, if you like, you can grab the zip with all the files I’ve been using so far from here.

{ 50 comments… read them below or add one }

Leave a Comment

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

Previous post:

Next post: