AS3 Avoider Game Tutorial, Part 5: A Score and a Clock

by Michael Williams on February 3, 2009 · 210 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, we’ll add a score and a clock, to give the player some mark of their skills. It’s a long part, but there’s a part in the middle that’s a good point for you to break off and come back later, if you don’t feel like doing the whole thing now.

Click the image below to see the result of this part in action:

screenshot

If you’ve not been following the tutorial so far, grab the zip file of the game so far here and extract it somewhere. Otherwise, copy the files you’ve been working on so far to a new folder, as usual.

Open the FLA file, and let’s get started.

Everything Counts

When you think about it, a clock and a score are basically the same thing. Each contains a number — a count of either time passed or points scored — and each provides a visual representation of that number — whether it’s a simple piece of text, an animated stopwatch or a growing progress bar.

If we made a Clock class and a Score class, they would therefore both contain a variable, of type Number, for storing time passed or points scored.

They’d also both need to have three functions in common:

  1. addToValue( amountToAdd:Number ) — we’d run this every tick, or every time the player did something worthy of some points
  2. reset() — this would set the score/time to its original value (i.e., zero); we’d run this when the score/timer was created, but we might alsouse this to reset the time if the player grabbed a certain power up, or remove all the player’s points if he hit an enemy
  3. updateDisplay() — this would make the visual representation show the current value of the score/time, so we’d probably run this every time the value was changed

What they probably won’t have in common is the visual display. This means that the way the updateDisplay() function actually works is likely to be different, too. Does this mean we have to write two separate classes?

No.

We’ve used the extends keyword quite a few times throughout this project, but only ever regarding a built-in class like MovieClip or Event. Now we’re going to use it to extend a class that we’ll write ourselves.

Here’s the idea:

  • We’ll start with a class that contains everything a Clock and a Score has in common — let’s call this class Counter. This will have no visual element, because we’re never actually going to use it.
  • We’ll extend Counter to make a second class, Score, that does have a suitable visual element.
  • Finally, we’ll extend Counter once more to make a third class, Clock that has a different visual element.

Make sense? OK, let’s do it.

Learning to Count… Again

Based on the above, here’s a base for the Counter class:

?View Code ACTIONSCRIPT3
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
package
{
	import flash.display.MovieClip;
	public class Counter extends MovieClip
	{
		public var currentValue:Number;
 
		public function Counter()
		{
 
		}
 
		public function addToValue( amountToAdd:Number ):void
		{
 
		}
 
		public function reset():void
		{
 
		}
 
		public function updateDisplay():void
		{
 
		}
	}
}

Save this new ActionScript file as Counter.as in your Classes directory. I expect you understand it all, so I won’t go through the whole thing. I’d like to draw attention to that extends MovieClip, though. Since MovieClips can contain other displayable objects, this part lets Counter contain displayable objects. (We could use a class like Sprite or DisplayObjectContainer to achieve this, but using MovieClip lets us create the actual Clock and Score objects in Flash, rather than having to draw everything using code.)

You might be wondering why Counter needs to extend MovieClip if Counter itself isn’t going to contain any visual objects. The reason is that a class can’t extend two separate classes at once. If we don’t let Counter extend MovieClip, then Clock and Score won’t be able to extend MovieClip either. We’re just planning ahead based on what we know we’re going to do next.

Let’s make these functions actually do something. First, reset(). It’s pretty obvious what that needs to do:

?View Code ACTIONSCRIPT3
18
19
20
21
public function reset():void
{
	currentValue = 0;
}

addToValue() is pretty obvious, too:

?View Code ACTIONSCRIPT3
13
14
15
16
public function addToValue( amountToAdd:Number ):void
{
	currentValue = currentValue + amountToAdd;
}

I said above that we’d reset() when the score/time was created, so let’s call reset() within the constructor:

?View Code ACTIONSCRIPT3
8
9
10
11
public function Counter()
{
	reset();
}

Finally, and again as mentioned above, we’ll need to update the display every time the internal value is changed. Since that’s only done in the reset() and addToValue() functions, let’s run updateDisplay() from inside those:

?View Code ACTIONSCRIPT3
13
14
15
16
17
18
19
20
21
22
23
public function addToValue( amountToAdd:Number ):void
{
	currentValue = currentValue + amountToAdd;
	updateDisplay();
}
 
public function reset():void
{
	currentValue = 0;
	updateDisplay();
}

It seems kind of strange to run a function that does absolutely nothing, but once more we’re just planning ahead.

Draw the Score

This idea of extending a custom class can be tricky to understand without a concrete example. If you’re finding it confusing, bear with me while we build something with it and I think it will become clear.

Time to do something in Flash itself now. Switch back to the FLA file and Insert a New Symbol. Call it Score and make it a Movie clip. In the Linkage options, export it for ActionScript and give it a class name of Score.

When you click OK, you’ll be in Edit mode for the Score movie clip. (Remember you can get into this mode at any point by right-clicking the Score item in the Library and selecting Edit.)

To keep things straightforward, I’m just going to use a simple piece of text that displays a number for the score:

screenshot

Nothing fancy. I’ve just typed a random number in there to see what it looks like, by the way, there’s no significance to 834.

We’re going to need to be able to edit that number using code, of course. Normal text doesn’t allow this, so we need to change it to Dynamic Text. Do you remember, back in Part 3, I told you to change the Game Over text to Static Text? Well, now we need to do the opposite.

Select the text, then, in the Properties panel, click the drop-down list (shown below) to Dynamic Text:

screenshot

When you deselect the text, it’ll have a dotted border around it:

screenshot

We also need to give the text a name by which we can refer to it in code (its instance name), just as we gave the Start and Restart buttons their own instance names in Part 4. I’ll call it scoreDisplay.

screenshot

Oh, one annoying thing Flash does with dynamic text is it makes it selectable by default, so the players mouse cursor turns into a text cursor when they mouse over it. If you want to get rid of this behaviour (and most people do), just select the text, find the little button that says “Ab” on it, and click it so that it’s not “sticking out”:

screenshot

Feel free to make this look a bit prettier. Add a border, or draw some stars around it, or whatever. Just make sure you’ve got an instance of dynamic text with the name scoreDisplay. I’m going to put a label on mine:

screenshot

(Note that I’ve made the label’s text static rather than dynamic, because I don’t ever need to change it.)

OK, that’s enough of that. Time for some code!

It’s Got its Mother’s Functions, and its Father’s Variables

Start a new AS file, call it Score.as and put it in the Classes directory. Here’s the code to go inside. Note that line 3 imports the TextField class we need because of the dynamic text. Are you getting tired of Flash not knowing all that by default yet?

?View Code ACTIONSCRIPT3
1
2
3
4
5
6
7
8
9
10
11
package
{
	import flash.text.TextField;
	public class Score extends Counter
	{
		public function Score()
		{
 
		}
	}
}

At first glance, this seems a bit odd. Why aren’t we typing in the currentValue variable, or the addToValue() function? Well, we don’t need to; that extends keyword means they’re already there. So, if you were to create an instance of the Score class, you’d be able to run .reset(). You can also access reset() from within the Score() constructor, if you like.

This is called inheritance, and is very powerful. By letting Score extend Counter, we’ve given it every public variable and function that Counter had. The only exception is the constructor… sort of.

Score() doesn’t automatically inherit the contents of the Counter() constructor function. Obviously this could be pretty annoying, since chances are we want Score() to do everything Counter() does, plus perhaps a little more on top. We don’t have to copy and paste the contents of Counter(), though — this is where super comes in.

We used super before, when creating a custom class, but I didn’t explain what it did. It’s actually quite simple; it allows a class to access the functions of the class that it extends. Again, an example should help. Simply change the Score() constructor like so:

?View Code ACTIONSCRIPT3
6
7
8
9
public function Score()
{
	super();
}

Here, super() is running all the code from the Counter() constructor (so in this case, it’s just running reset()).

Alright, so, safe in the knowledge that we’ve already written the code to handle addToValue() and reset(), let’s work on updateDisplay().

There’s a slight problem, though. Like I said, the function updateDisplay() already exists in Score because it already exists in Counter. If we just write:

?View Code ACTIONSCRIPT3
11
12
13
14
public function updateDisplay():void
{
 
}

then Flash will give us an error, because there’ll be two functions called updateDisplay() within one class, and that’s not allowed.

There’s a simple solution to this, too, though: the override keyword. All we have to do is prefix the line declaring the function with it, like this:

?View Code ACTIONSCRIPT3
11
12
13
14
override public function updateDisplay():void
{
 
}

There’s a slight catch, though. By overriding the function, we’ve lost all the contents, just as we did with the constructor — in a way, every class’s constructor overrides the constructor of the class it extends. This isn’t a problem to us right now, because Counter’s updateDisplay() function doesn’t do anything at the minute. But if we want to, say, make it check whether the value had changed since the last time it had updated the display and only update if so (to increase efficiency), we’d have to copy and paste that code into every class that extends Counter.

Except of course we don’t because we can use super again. Like this:

?View Code ACTIONSCRIPT3
11
12
13
14
override public function updateDisplay():void
{
	super.updateDisplay();
}

Now any code that’s in Counter’s updateDisplay() function will run when Score’s updateDisplay() function is run. If you’re still unclear on how override and super work, check out my post on the subject.

At last, we’re ready to write some new code. It’s so simple, too:

?View Code ACTIONSCRIPT3
11
12
13
14
15
override public function updateDisplay():void
{
	super.updateDisplay();
	scoreDisplay.text = currentValue.toString();
}

That’s pretty straightforward, apart from .toString(). Programmers call any sequence of letters and numbers a string, because they’re a string of characters. The .text property of scoreDisplay is a string, so it needs a string value passed to it. currentValue isn’t a string, though; we defined it as a Number. Fortunately, all Numbers have a function called toString() that returns the number in string form.

Hmm… I guess that wasn’t so straightforward after all.

Time to put our score in the game.

(By the way, if you’re low on time, this is the great point at which to stop and come back later that I mentioned at the start of the post.)

The Scores on the Doors

I guess we could place the score display in the game using code in AvoiderGame, like we’re doing with the Avatar and the enemies, but that’s going to be a pain so let’s do it the way we’ve done buttons. Edit the PlayScreen movie clip (in the library) and drag the Score movie clip onto it in a suitable location:

screenshot

We’ll need to give this an instance name, too. Select it, and in the appropriate field in the Properties panel, enter gameScore. (I’ve called it this so as not to confuse it with scoreDisplay, the name of the text field within the movie clip.)

If you save and run the game (Control > Test Movie), it’ll look like this:

screenshot

It’s not increasing, because we haven’t run addToValue() ever. But note that it says “Score: 0″ and not “Score: 834″. This is because its constructor function has been run automatically, which in turn has run reset().

It’s up to you how you want the player to earn points. I’m going to keep things very simple and just make the score increase by ten every time an enemy appears on-screen. I do this in AvoiderGame.as:

?View Code ACTIONSCRIPT3
31
32
33
34
35
36
37
38
39
40
public function onTick( timerEvent:TimerEvent ):void 
{
	if ( Math.random() < 0.1 )
	{
		var randomX:Number = Math.random() * 400;
		var newEnemy:Enemy = new Enemy( randomX, -15 );
		army.push( newEnemy );
		addChild( newEnemy );
		gameScore.addToValue( 10 );
	}

(Line 39 is the new one.) Result:

screenshot

This is actually starting to feel like a game now :) We really need the score to be displayed on the game over screen though. Otherwise, what’s the point? Or, as the player will be asking, “what’re the points?” … I think that’s a really funny joke. You can tell your friends, if you like.

We could put another instance of Score on the GameOverScreen, but that’s really wasteful, since we’re only going to change it once, and don’t need any of the functions we’ve built in to it. Let’s just use another dynamic text field instead. Edit the GameOverScreen movie clip from the library, and add a new dynamic text field:

screenshot

(As you can see, I’ve also added a new label, and I’ve moved the restart button.)

Give your score field an instance name of finalScore, so that we can set it using code.

How are we going to get the final score from the play screen to the game over screen? Well, the best place to do this transfer is the document class. That’s its job, really, to manage the different screens. And the best place within the document class is the onAvatarDeath() function. Open up DocumentClass.as. Here’s how I’d like it to look (lines 21 and 27 are new):

?View Code ACTIONSCRIPT3
19
20
21
22
23
24
25
26
27
28
29
30
31
public function onAvatarDeath( avatarEvent:AvatarEvent ):void
{
	var finalScore:Number = playScreen.getFinalScore();
 
	gameOverScreen = new GameOverScreen();
	gameOverScreen.addEventListener( NavigationEvent.RESTART, onRequestRestart );
	gameOverScreen.x = 0;
	gameOverScreen.y = 0;
	gameOverScreen.setFinalScore( finalScore );
	addChild( gameOverScreen );
 
	playScreen = null;
}

In line 19, we grab the final score from the play screen, and in line 28 we feed it into the game over screen. We haven’t written either of the functions necessary yet, but change onAvatarDeath() as above, anyway, and then we’ll add these new functions in.

For playScreen.getFinalScore(), switch back to AvoiderGame.as. All we need is a new function like the following:

?View Code ACTIONSCRIPT3
55
56
57
58
public function getFinalScore():Number
{
	return gameScore.currentValue;
}

This is getting the current score directly from the Score instance. A little dodgy, I suppose, since we might decide later to have updateDisplay() to show the score as being five times the internal value, and then .currentValue would be incorrect — but I don’t think we need to worry for now.

Now, for gameOverScreen.setFinalScore( finalScore ), open GameOverScreen.as. First thing’s first; import the TextField class:

?View Code ACTIONSCRIPT3
1
2
3
4
5
6
package 
{
	import flash.display.MovieClip;
	import flash.display.SimpleButton;
	import flash.events.MouseEvent;
	import flash.text.TextField;

Now, the new setFinalScore() function we’re going to write is almost exactly the same as updateDisplay() from the Score class, except it accepts a parameter:

?View Code ACTIONSCRIPT3
20
21
22
23
public function setFinalScore( scoreValue:Number ):void
{
	finalScore.text = scoreValue.toString();
}

Save it and run it. Of course, you’ll have to hit an enemy before you see the result:

screenshot

Great! Eight or nine years ago, that would have made the front page of Newgrounds like a shot.

Goodness, is That the Time?

I’ll admit, it seems to have taken a long time just to get the score in the game. But, actually, a lot of that was setting up the Counter (which doesn’t need doing again) and explaining the idea of inheritance. Writing the Clock’s code won’t take long at all, and it’ll be pretty easy.

This time, let’s not use a numeric display. I want to show you why we’re bothering with all this extending and overriding and so forth. We’re going to use an animation instead.

Switch back to the FLA and create a new movie clip (Insert > New Symbol) called Stopwatch. This time, don’t export it for ActionScript or define a class. This is just going to be the animation we use within the actual Clock movie clip.

Make sure you’re editing the new movie clip, and draw a stopwatch (well, you can draw whatever you like, but I’m drawing a stopwatch):

screenshot

Now, you remember in Part 4, when we were creating the buttons, we added new keyframes for each state of the button? We need to do the same here, except each new keyframe will be a new frame of the animation. (If you’re comfortable with using Flash as an animation tool, feel free to throw in some tweens or whatever you want here.) Right-click Frame 2 and select Insert Keyframe:

screenshot

Make some change that indicates the passage of time:

screenshot

Keep adding new frames until your animation can loop back on itself. (You can always just use a two-frame animation if you like; a digital clock blinking “12:00″ might be quite funny.)

We’re going to use this animation within the Clock in much the same way as we use the dynamic text field within the Score. Create another new movie clip and call it Clock. This time, we do want to export it for ActionScript; keep the class’s name as Clock.

Make sure you’re editing the Clock movie clip, then drag the Stopwatch animation from the library. It needs an instance name: call it clockDisplay. You can add a label or other decoration if you like. So, the Clock movie clip is what will go into the playScreen movie clip, and will contain and control the Stopwatch movie clip.

Next, create a new AS file, and save it as Clock.as in the Classes folder. Here’s the contents:

?View Code ACTIONSCRIPT3
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package
{
	import flash.display.MovieClip;
	public class Clock extends Counter
	{
		public function Clock()
		{
			super();
		}
 
		override public function updateDisplay():void
		{
			super.updateDisplay();
 
		}
	}
}

It’s almost identical to how we started with Score.as, except we’re importing MovieClip rather than TextField.

This time, we need the updateDisplay() function to move to the correct frame of the animation. The function we need for this is called gotoAndStop(). To switch the animation to frame 3, for instance, we would call:

?View Code ACTIONSCRIPT3
clockDisplay.gotoAndStop( 3 );

The problem is, we’re going to update the clock’s internal value every tick. The way I plan to do this is to add the number of milliseconds in a tick (25) every time the onTick() function is run. This means that if we just run clockDisplay.gotoAndStop( currentValue ), then it’ll play frame 25, then frame 50, then frame 75… skipping out all the frames in-between. Plus, after a couple of seconds it’ll be looking for frame 2000 — and I don’t know about you, but I ran out of patience at frame 12.

So we need to do two things:

  1. Divide currentValue by another number to get the actual frame to play, in order to avoid skipping over frames
  2. Go back to the first frame once we go past the last frame

With those points in mind, here’s my updateDisplay() function:

?View Code ACTIONSCRIPT3
11
12
13
14
15
16
17
18
19
20
21
22
23
override public function updateDisplay():void
{
	super.updateDisplay();
 
	var frameToSkipTo:Number = currentValue / 1000;
	frameToSkipTo = Math.floor( frameToSkipTo );
	frameToSkipTo = frameToSkipTo + 1;
	if ( frameToSkipTo > 12 )
	{
		frameToSkipTo = frameToSkipTo - 12;
	}
	clockDisplay.gotoAndStop( frameToSkipTo );
}

I’ll go through it:

  • Line 15 creates a new variable, frameToSkipTo, which divides the internal value by 1000 in order to convert the number of milliseconds passed to the number of seconds passed
  • Line 16 uses a function I’ve not mentioned before, Math.floor(), which rounds the number down to the nearest whole number
  • Line 17 adds 1, because otherwise the first frame the code would attempt to play is frame 0, which doesn’t exist (first frame is frame 1)
  • Lines 18-20 checks to see if the new frame is after my last frame (I have 12 frames). If it is, it subtracts 12, so 13 -> 1, 14 -> 2, etc
  • Line 21 moves the animation to the required frame

Actually this isn’t quite right. It works for almost two and a half seconds then stops. Why? It’s because after that, frameToSkipTo is 25 or more, and so subtracting 12 just makes it 13 or more — and there aren’t 13 frames!

To fix this, we’ll simply switch the if for a while (line 18):

?View Code ACTIONSCRIPT3
11
12
13
14
15
16
17
18
19
20
21
22
23
override public function updateDisplay():void
{
	super.updateDisplay();
 
	var frameToSkipTo:Number = currentValue / 1000;
	frameToSkipTo = Math.floor( frameToSkipTo );
	frameToSkipTo = frameToSkipTo + 1;
	while ( frameToSkipTo > 12 )
	{
		frameToSkipTo = frameToSkipTo - 12;
	}
	clockDisplay.gotoAndStop( frameToSkipTo );
}

A while is like an if, except it runs over and over again until the condition within the brackets is no longer true. So, when frameToSkipTo reaches 25, the while will detect that it’s more than 12, and subtract 12, making it 13. The while will then again detect that it’s more than 12, and subtract another lot of 12, making it 1.

Simple! Feel free to alter any of this function to suit your own animation.

Wrapping Up

I could walk you through adding the Clock to the AvoiderGame, then writing the code to add 25 to the clock every tick, and pass the data back through to the GameOverScreen, and so on. But I’m not going to.

If I did, I’d be repeating myself, because it’s almost exactly the same as what we did for the score. Also, because there are no new concepts to introduce, I think it’ll be more useful for you to have a go yourself, rather than just follow exactly what I say.

Hope you don’t think this is a cop-out. If you get stuck, post a comment below, send me an email, or download the zip of the files I’ve been working on and take a look at what I did.

Good luck!

In the next part (available here) we’ll do a bunch of small changes based on suggestions other people have given. The week after that, we’ll add keyboard control.

Thanks for reading :)

{ 209 comments… read them below or add one }

Axiom August 23, 2010 at 4:48 pm

Okay,

There is still something I am doing wrong….
This is pretty much my doing since I didn’t have problems through the entire thing and I thought I’d take a short cut and just ‘borrow’ your clock… now I am having problems…

You reap what you sow I guess.

I am getting this error.

TypeError: Error #1006: addToValue is not a function.
at AvoiderGame/onTick()
at flash.utils::Timer/_timerDispatch()
at flash.utils::Timer/tick()

http://www.filefactory.com/file/b30909d/n/Avoider_Game.rar

so I am guessing I need to add an addToValue Function… but where would that go? In the AvoiderGame class? This is a question for future problems as well… how do you figure out just WHERE you need to add things? And lines like, at flash.utils::Timer I dont think I have seen that before, just what does that mean?

Sorry if I have a lot of questions… my goal is to make Flash games so I hope to actually be able to fix this stuff myself some day instead of looking at it and drooling on myself.

Axiom August 26, 2010 at 2:06 am

Anyone? I’ll be your best friend if you can help me :P

I did a trace right before and after this line… and It hits trace (“A”) and loops and never hits trace B so I think the clock is the problem.. but I am not sure how!

trace(“A”)
gameClock.addToValue( 25 );
trace(“B”)

ayumilove August 26, 2010 at 2:53 am

@Axiom

I have checked your code.
Your gameClock is a MovieClip that resides in AvoiderGame (another MovieClip)
A MovieClip does not have a function called addToValue.
That is the reason why Flash bugs you about it.

You tell Flash to do something (calling the function) but that function does not exist.
In simpler terms, you went to a vegetable market and asked for fish, and the seller bugs you that they don’t have any.

ayumilove August 26, 2010 at 2:57 am

@Axiom

About where the addToValue Function goes, it can be relocated into a custom class called Clock that extends MovieClip. From there, you can call that custom function out. So in your Flash IDE, you need to set the linkage so that it links to a Clock class.

Axiom August 27, 2010 at 5:48 pm

So how did you decide where to put the addToValue?

Originally I had it in AvoiderGame since I thought all the main stuff should go there…. when that didnt work I put it in Counter since its important for the Counter … should go in the counter right? But I guess it needs to go in the Clock… but where? should I put it at the top in the middle? Does it even make a difference?

Michael Williams August 27, 2010 at 11:50 pm

Hey Axiom,

You need to put the call to addToValue() in the AvoiderGame class (like gameClock.addToValue(25)), but the actual addToValue() function needs to go in either Counter or Clock.

Axiom August 31, 2010 at 2:02 am

Hmmm but I have the addToValue function on line 12 of the counter class

    public function addToValue( amountToAdd:Number ):void
    {
        trace("F")
        currentValue = currentValue + amountToAdd;
        updateDisplay();
    }

Okay I am just getting more confused now…

since I have commented out those lines of code and tried to add it to the clock class

and I get the same error.

so the call to addToFunction is on line 34 (or there abouts) of AvoiderGame.as

and the FUNCTION addToValue is in the Counter class and I have tried it in the Clock class

and yet I am still getting that same error….

So correct me if I am wrong, but it sounds like I am setting this up the way I should but I am still getting the same errors… So what am I doing wrong?

Axiom August 31, 2010 at 3:45 am

Here are the files again… (this time I used 7 zip since a friend of mine told me its a lot nicer than winrar.. let me know if you want it in winrar format again

you will see from the traces it keeps hitting ABABABABAB then finally bounces to C D F D I think it is….

I can get it to at least run when I comment out line 34 the
gameClock.addToValue(25) line.

I also got it to at least run when I added the addToValue function to BOTH clock and counter class, but as I stated above if it is in only ONE of those classes it doesnt work.

I hope I am not breaking this even more but I am kind of on my wits end with this… since everything points to it should work (even the advice I was given here).. but it doesnt, so I am obviously doing something wrong.. but what?

Axiom August 31, 2010 at 3:46 am

Leave a Comment

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

CommentLuv Enabled

Anti-Spam Protection by WP-SpamFree

{ 1 trackback }

Previous post:

Next post: