Learn ActionScript 3 by Following this Simple Avoider Game Tutorial

by Michael Williams on September 17, 2008 · 239 comments

in Avoider Game Base,Tutorial

(This tutorial is also available in Spanish, Polish, and Italian.)

Introduction

A lot of Flash developers find the jump from ActionScript 2 to ActionScript 3 very daunting. Conversely, a lot of programmers of other languages find ActionScript 3 quite intuitive, but hate ActionScript 2! This tutorial does not require you to know AS2, or even have used Flash before. You’ll find it easier if you already know the basics of some programming language, like variables, if statements, loops, and functions, but don’t worry if that’s not the case. I will be pointing out some of the major differences between AS2 and AS3 for the benefit of those Flash developers making the switch, but I’m not going to focus on these changes.

Frozen Haddock has been kind enough to let me rewrite his ActionScript 2 Avoiding Game Tutorial in ActionScript 3. Thanks, Frozen Haddock! This is not a direct port of his tutorial, however. I’ll be redesigning the code to follow good AS3 practices, which means:

  • One frame
  • One layer
  • No items on the Stage
  • No code in the timeline
  • No code in any symbols

(If you’ve never used Flash to create a game before, the above will probably mean nothing to you. Trust me, that’s a good thing!)

These rules might have to be bent a little when it comes to adding a preloader. We’ll cross that bridge when we come to it.

We’ll also be using classes and events and OOP and all sorts of nice programmer things. But I’m getting ahead of myself. In this part of the tutorial, we’ll just be setting everything up and putting in some very basic game mechanics. To see just how basic, check it out by clicking the image below:

click here to see what we'll be working towards

And by the end of this 12-part tutorial, it’ll look like this, with keyboard controls, multiple levels, sounds, and more:

screenshot

Setup

Let’s start off simple. Boot up your copy of Adobe Flash CS3 (or later, if you’re reading this from the future) and select File > New > Flash File (ActionScript 3.0).

Click File > Save and create a new folder somewhere where you can store all the game’s files. I recommend using your name or initials in the folder’s name as this will make it easier to share with other people later. For reference, I’m calling mine AvoiderGame-MJW. Create a new folder, inside this folder, called Classes. Finally, enter a name for your FLA file (I used AvoiderGame-MJW.fla) and save it — in the main folder, not the Classes folder.

screenshot

There are some default settings we need to alter before we can start on the game proper. Click File > Publish Settings. Click the Flash tab, if it’s not already selected, then click the Settings button next to the ActionScript Version drop-down list:

screenshot

Click the little plus icon above the Classpaths box, and type ./Classes/ in the textbox that appears. This tells Flash that we are going to store the code that we write in the Classes folder we made earlier, so it should check in there if it can’t find any code that it needs.

screenshot

Also, tick the box marked, “Automatically declare stage instances”, if it’s not already ticked.

Next we’ll alter the properties of the game itself. Click Modify > Document and set it up however you like. Frozen Haddock gave his a size of 300 by 300 pixels, and a grey background. I’ve stuck with the grey, but gone for the oldschool-TV-ratio dimensions of 400 by 300 pixels. You can always change this later, so don’t fret too much over it. I do recommend sticking with a frame rate of 24 fps, though. Here’s what I did:

screenshot

OK, that’s the dull setup out of the way. Let’s create our enemy!

Creating the Enemy

The enemy is the object that you, as a player, will have to avoid. (Hence, Avoider Game.) Let’s draw it before giving it life. Click Insert > New Symbol and in the box that appears, give it a Name of Enemy (with a capital E!) and a Type of Movie Clip (in case we want to animate it later). Click OK. The Library will now show your new Enemy object:

screenshot

If you can’t see a Library, make sure Window > Library is checked. By default you will be automatically editing the Enemy — you can tell what you’re editing by the bar over the editing window:

screenshot

If you’re not editing the Enemy for some reason, just double-click it in the Library (or right-click it and select Edit). Draw your Enemy. Frozen Haddock picked a smiley face for his bad guy. Who knows why? Maybe it’s got something to do with those blasted banner adverts. I’m going to do the same. You can make anything you want, but I’m going to assume that we’re all using a roughly circular Enemy, so bear in mind that if you draw a stickman, some parts of this tutorial won’t apply to you. Also, if you’re not used to the Flash drawing environment, it can be a little unintuitive. I recommend this quick drawing tutorial if that applies to you. Here’s my guy:

screenshot screenshot screenshot screenshot

Pretty awesome. There’s a bit of a problem though. You see that little black crosshair with the white background? That’s called the registration point, and when we start writing code to say, “put the Enemy at such-and-such a position”, it’s this point that Flash is going to actually place at that position. (It’s hard to explain in words; later on this will become much clearer.) For this reason, I’m going to move the face around so that the registration point is dead centre. Here’s how to do it:

First, make sure you’re editing your Enemy, and then select everything (click Edit > Select All). A crosshatch pattern will appear:

screenshot

Now, group everything together by clicking Modify > Group Together. A blue box will appear. If you now click and drag anything inside that box, the entire enemy will move as one. (If you had tried this before, you would have ended up dragging his eye out.)

screenshot

Go to the Align panel. If you can’t see it, make sure Window > Align is checked. With the Enemy still selected (i.e., with the blue box still around it), make sure the To stage button is in, and click the two central Align buttons:

screenshot

Now that we’ve finished with the Enemy’s design, we can get out of editing mode. Click Scene 1 on the bar above the editing box. This would be a great time to save our work, so hit File > Save.

The Enemy’s Instructions

So far, nothing we’ve done has been significantly different from what we would have done in any previous version of Flash. That’s about to change. It’s time to start writing code to control the Enemy’s behaviour. As I mentioned at the start, this code is not going to go in either the timeline or the Actions of the Enemy symbol. Instead, it’s going to be kept in an external file. There are a number of benefits to this:

  • The code is completely separate from the artwork, so you can give your artist the FLA file without having to give them the source code.
  • Likewise, you can give a programmer parts of the code without having to send them all your artwork and sounds — or even the rest of the code.
  • If you have a number of programmers each working on separate parts of the code, it’s much easier to put it all back together later.

Click File > New and select ActionScript File. A blank text-editing window will appear. Immediately save the file, as Enemy.as, in the Classes folder you created earlier. This file is going to contain the class — a programming template — for our antagonistic smiley faces.

It’s around this point in an AS3 tutorial that most AS2 programmers start to complain about how much harder it is to use AS3, because there’s so much more that has to be written just to get an object to do something. Please bear with me, and you will see that, although there are indeed more lines of code needed to set this up, they aren’t very difficult to understand, and the approach we take in writing them will really pay off in the long run.

Begin by typing the following:

?View Code ACTIONSCRIPT3
1
2
3
4
package 
{
 
}

The package keyword simply says that everything between the next pair of curly braces is part of a single… well, package. In this case, everything in this package concerns the Enemy object.

As part of this package (so, between the curly brackets), we need to define the Enemy’s class — the template that specifies what it can do, and what we can do with it:

?View Code ACTIONSCRIPT3
1
2
3
4
5
6
7
package 
{
	public class Enemy extends MovieClip 
	{
 
	}
}

Let’s examine this bit by bit:

  • class Enemy — “everything between the next pair of curly brackets makes up the Enemy class.”
  • public — “this class can be accessed by any other piece of code that knows about it.” The alternative would be internal, meaning that only code inside the same package could access this class.
  • extends MovieClip — “I can do anything a MovieClip class can do — and more besides.” So, for example, since we can write code that makes a MovieClip move to the next frame of its animation, we can do the same with an Enemy (or at least we could, if it contained more than one frame). On top of that, we can give this Enemy an HP count (say) which a MovieClip doesn’t have, thus extending the functionality.

The thing is, even though MovieClip is such an important and frequently-used class of object, Flash still has to be told where it is. So, we write in line 3, below:

?View Code ACTIONSCRIPT3
1
2
3
4
5
6
7
8
package 
{
	import flash.display.MovieClip;
	public class Enemy extends MovieClip 
	{
 
	}
}

Finally, the Enemy class needs what’s known as a constructor function. This is just a function that runs whenever a new Enemy is created. All constructor functions must have the same name as the class they belong to, so:

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

There’s that word public again. Note that the Enemy function has an empty pair of parenthesis directly after it; more on that later.

So now we have our Enemy class set up, we need to link it to our Enemy graphic. Save the AS file and go back to the FLA file (it’ll be on the tab bar at the top of the window, or in the Window menu, assuming you haven’t closed it). Right click the Enemy in the Library, and select Properties. Click the Advanced button, if it’s there, to show the Linkage panel. Check the Export for ActionScript box, and make sure that the Class reads “Enemy”. If everything’s linked up fine, then clicking the little pencil icon should open up the Enemy class that we were just working on.

screenshot

Click OK to close the Properties box.

Now we need to code in the Enemy’s actual behaviour. What does it need to do? At this point, all we need it to do is:

  • Appear above the top of the screen when it’s created
  • Keep moving downward from that time onward

We already have a space to put code that needs to execute upon the creation of the Enemy — the constructor function. So:

?View Code ACTIONSCRIPT3
1
2
3
4
5
6
7
8
9
10
11
12
package
{
	import flash.display.MovieClip;
	public class Enemy extends MovieClip
	{
		public function Enemy()
		{
			x = 100;
			y = 0;			
		}
	}
}

Since MovieClips have x and y properties, so does our Enemy class.

Those of you familiar with Cartesian coordinates are probably saying, “whoa, Michael! I thought we wanted the Enemy to appear at the top of the screen, but you’ve set the y-coordinate to zero, which is surely the bottom.” I’m afraid that Flash is weird in that respect: y increases as you go down the screen. For my 400 by 300 pixel stage, the coordinates look like this:

screenshot

You get used to it.

I’m going to handle the movement by having something else tell the Enemy to move downward, just a little bit, every split-second. We need to add this movement functionality to the Enemy class, since MovieClips don’t already have something that can do that, so let’s create a new function:

?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 Enemy extends MovieClip 
	{
		public function Enemy() 
		{
			x = 100;
			y = 0;			
		}
 
		public function moveDownABit():void 
		{
			y = y + 3;
		}
	}
}

Let’s break this down again:

  • function moveDownABit() — everything between the following pair of curly brackets makes up a function called moveDownABit. Again, we have that empty pair of parenthesis after the function name.
  • public — another part of the game is going to actually run this function, so it needs to be public.
  • :void — this function does not return any feedback. Don’t dwell on this; I’ll explain it when we make a function that does return something.
  • y = y + 3; — increase the y-coordinate of the Enemy by 3 pixels (remember, this will move the enemy downwards, not upwards).

Making an Enemy

Save your Enemy.as file and go back to the FLA file. Our next step is to get an actual enemy into the game; up till now we’ve just been dealing with a template of an Enemy, not an actual enemy itself. It’s like we’ve been working on a blueprint, and now we need to use that blueprint to build an actual, physical house.

It’s important to remember that Flash started off as a tool for creating animating movies. Even though it’s developed a lot since then, Flash still considers everything you create with it to be a MovieClip. As we know, MovieClips have a constructor function that runs when they’re created, and therefore so does your game. We’re going to extend this function to make it create an Enemy within the game when the game is started. But how do we access the game’s constructor? If you said, “another AS file,” you’re correct.

Create a new AS file, and save it in the Classes folder with the name AvoiderGame.as. Add these lines to the file, and save it:

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

Look familiar? Apart from the names of the class and the constructor, it’s exactly the same as our Enemy AS file started out. Let’s link it to our FLA file.

Move back to the FLA file and make sure you’re not still editing the Enemy. Find the Properties panel — if you can’t see it, make sure Window > Properties > Properties is checked. There’s a box marked Document class; enter AvoiderGame. It’s important to use capital letters in the right places here — since we’ve called the file AvoiderGame.as and the class within the file AvoiderGame, we have to follow suit with the name of the document class. Clicking the little pencil icon should bring up the AvoiderGame class file.

screenshot

Great, so now we can edit the actual game’s constructor to make it create an enemy at startup:

?View Code ACTIONSCRIPT3
1
2
3
4
5
6
7
8
9
10
11
package 
{
	import flash.display.MovieClip;
	public class AvoiderGame extends MovieClip 
	{
		public function AvoiderGame() 
		{
			enemy = new Enemy();
		}
	}
}

Watch your capitalisation here! Enemy with a capital E is the class file that defines features common to all Enemies; enemy with a small e is a specific example — an “instance” — of an Enemy. It’s like, Person is a class, but I am an instance of that class, and you are another instance of that class.

At the minute, this instance, enemy, of Enemy is only available to the constructor function, because it is first referred to within that function. We need it to be available throughout the entire game, so add this line of code (line 6):

?View Code ACTIONSCRIPT3
1
2
3
4
5
6
7
8
9
10
11
12
13
package 
{
	import flash.display.MovieClip;
	public class AvoiderGame extends MovieClip 
	{
		public var enemy:Enemy;
 
		public function AvoiderGame() 
		{
			enemy = new Enemy();
		}
	}
}
  • public — you know what this means.
  • var — “the following is a new variable that I am defining.”
  • enemy:Enemy — “the variable is called enemy, and it is an instance of the Enemy class.”

Since the enemy variable is defined within the AvoiderGame class, it’ll be available throughout the class.

So now we’ve created an enemy, but it’s just sort of floating about in the aether. We need to tie it down to the actual game:

?View Code ACTIONSCRIPT3
8
9
10
11
12
		public function AvoiderGame() 
		{
			enemy = new Enemy();
			addChild( enemy );
		}

Here’s a new function: addChild(). We use this to make objects actually appear on-screen. Any object that is “addChild()-ed” to the document class (remember, the AvoiderGame class is our document class) will appear on-screen — well, as long as its x- and y-coordinates are inside the rectangle that make up the screen!

We say that the enemy is now a child of the AvoiderGame class, and that the AvoiderGame class is the enemy’s parent. An object can only have one parent, but it can have as many children as you like.

At last, we can run the game and expect some results! Save everything, then hit Control > Test Movie. You’ll get something like this:

screenshot

Hooray! Only… we want all of the enemy to start above the window, not just half of it. What Flash has done is position the enemy so that its registration point is positioned at the point (100,0). We could move the registration point to the bottom of the enemy, but I think that’s a sloppy solution. Instead, let’s just change the enemy’s starting y-position; alter the constructor in the Enemy.as file like so:

?View Code ACTIONSCRIPT3
6
7
8
9
10
		public function Enemy() 
		{
			x = 100;
			y = -15;
		}

You’ll need to alter that y-value depending on the height of your Enemy (mine’s 30 pixels tall).

Bringing the Enemy to Life

Although we’ve built in some functionality to let the enemy move downwards, we haven’t written any code that actually tells the enemy to do so. Let’s do that next.

The basic idea, as I outlined above, is to have the game tell the enemy to move a few pixels down every split-second. Frozen Haddock’s tutorial used a couple of neat methods for achieving this, but we don’t need to use either of those, because AS3 has a new solution: the Timer class that does it all for us.

Edit the game’s constructor function to create a new instance, gameTimer, of the Timer class. This is a built-in class, so you don’t need to create a new AS file; just edit AvoiderGame.as like so::

?View Code ACTIONSCRIPT3
8
9
10
11
12
13
14
		public function AvoiderGame() 
		{
			enemy = new Enemy();
			addChild( enemy );
 
			gameTimer = new Timer( 25 );
		}

Note that this time the parenthesis after the class name are not empty. That number 25 says that we want to set the interval of the timer to 25ms (milliseconds; 1000ms = 1 second), i.e., we want it to do something every 25ms.

We want this gameTimer to last throughout the game, of course, so we need to make it available to the entire class (line 7):

?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 AvoiderGame extends MovieClip 
	{
		public var enemy:Enemy;
		public var gameTimer:Timer;
 
		public function AvoiderGame() 
		{
			enemy = new Enemy();
			addChild( enemy );
 
			gameTimer = new Timer( 25 );
		}
	}
}

…and Timer is like MovieClip, in that Flash has to be told about it before we can use it (line 4):

?View Code ACTIONSCRIPT3
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
package 
{
	import flash.display.MovieClip;
	import flash.utils.Timer;
 
	public class AvoiderGame extends MovieClip 
	{
		public var enemy:Enemy;
		public var gameTimer:Timer;
 
		public function AvoiderGame() 
		{
			enemy = new Enemy();
			addChild( enemy );
 
			gameTimer = new Timer( 25 );
		}
	}
}

(By the way, don’t worry about how I know that it’s flash.utils.Timer and flash.display.MovieClip; I looked it up.)

All right, so we have a Timer that goes off every 25ms. But at the minute it’s not connected to anything; it’s not telling the enemy to move down the screen. So, add the following (line 17) to your game’s constructor:

?View Code ACTIONSCRIPT3
11
12
13
14
15
16
17
18
		public function AvoiderGame() 
		{
			enemy = new Enemy();
			addChild( enemy );
 
			gameTimer = new Timer( 25 );
			gameTimer.addEventListener( TimerEvent.TIMER, moveEnemy );
		}
  • gameTimer.addEventListener — “apply an event listener to the gameTimer with the following parameters.” 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. In this case, the event is…
  • TimerEvent.TIMER — this event goes off every time a Timer completes an interval; in our case, this event will be triggered every 25ms, because that is the interval of the gameTimer.
  • moveEnemy — this is the function that’s going to be run every time the TimerEvent occurs. We haven’t written this function yet; we’ll get to that in a minute.

TimerEvent is yet another thing that Flash has to be told about, so import it at the top (line 5):

?View Code ACTIONSCRIPT3
1
2
3
4
5
package 
{
	import flash.display.MovieClip;
	import flash.utils.Timer;
	import flash.events.TimerEvent;

Now we need to write that moveEnemy function that’ll actually be run every 25ms (lines 21-24):

?View Code ACTIONSCRIPT3
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
	public class AvoiderGame extends MovieClip 
	{
		public var enemy:Enemy;
		public var gameTimer:Timer;
 
		public function AvoiderGame() 
		{
			enemy = new Enemy();
			addChild( enemy );
 
			gameTimer = new Timer( 25 );
			gameTimer.addEventListener( TimerEvent.TIMER, moveEnemy );
		}
 
		public function moveEnemy( timerEvent:TimerEvent ):void 
		{
 
		}
	}

Again, we’ve got something inside the parenthesis. This allows the event listener to pass information about the event through to the moveEnemy function, in the form of an instance (timerEvent) of the class TimerEvent. We don’t need to know any of that information, however, so we won’t be referring to it again. Also, there’s void again.

All we have to do now is use this new function to tell the enemy to move. Remember the moveDownABit function we put inside Enemy earlier? Time to make use of it:

?View Code ACTIONSCRIPT3
21
22
23
24
		public function moveEnemy( timerEvent:TimerEvent ):void 
		{
			enemy.moveDownABit();
		}

So this tells the enemy instance to run its moveDownABit function. Save everything, and run the game (Control > Test Movie, remember).

Nothing happens.

We have to tell the gameTimer to start! Go ahead and edit your game’s constructor as below (line 19):

?View Code ACTIONSCRIPT3
12
13
14
15
16
17
18
19
20
		public function AvoiderGame() 
		{
			enemy = new Enemy();
			addChild( enemy );
 
			gameTimer = new Timer( 25 );
			gameTimer.addEventListener( TimerEvent.TIMER, moveEnemy );
			gameTimer.start();
		}

Save everything, and run it again. Success! The enemy is moving downscreen, at a healthy rate of 3 pixels per 25ms (or 120 pixels/second).

Now we need to add some amount of interactivity, otherwise this is just a movie and not a game.

Get your Head in the Game

First we have to make our player’s avatar. In your FLA file, create a new symbol, of type Movie Clip, called Avatar. (Check out the section above on creating the enemy if you can’t remember how to do this.) Draw whatever you want to represent the player. Frozen Haddock defied convention again by picking a skull for his protagonist. I want to get in on some of this action.

screenshot

Don’t feel bad if your drawings aren’t anywhere near as good as mine. After all, I do have a GCSE in graphic design.

Alright, so once you’ve made your Avatar, centred the registration point, got out of editing mode, and saved the FLA, what’s next? You will probably not be surprised to hear that we’re going to create a new AS file for the Avatar. Save it as Avatar.as in the Classes folder, and start editing it:

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

Same old story. Go ahead and link it to the Avatar in the Library.

What is our avatar going to do? At the minute, it’s just going to follow the mouse around the screen — effectively, it’ll be a mouse pointer. We can actually control the avatar’s position from within the AvoiderGame class, so we don’t need to add any more code to the Avatar class right now.

Switch to editing the AvoiderGame.as file, and let’s add an instance of the Avatar class to the game. I’ll put in all the code at once, this time, since it’s pretty much the same as when we did it for the enemy. (The following is in the AvoiderGame.as file) (New code: lines 10, 18, & 19):

?View Code ACTIONSCRIPT3
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
	public class AvoiderGame extends MovieClip 
	{
		public var enemy:Enemy;
		public var avatar:Avatar;
		public var gameTimer:Timer;
 
		public function AvoiderGame() 
		{
			enemy = new Enemy();
			addChild( enemy );
 
			avatar = new Avatar();
			addChild( avatar );
 
			gameTimer = new Timer( 25 );
			gameTimer.addEventListener( TimerEvent.TIMER, moveEnemy );
			gameTimer.start();
		}
 
		public function moveEnemy( timerEvent:TimerEvent ):void 
		{
			enemy.moveDownABit();
		}
	}

If you run the game now, you’ll see that the avatar instance appears in the top left corner of the screen — i.e., at position (0,0). We want it to start off wherever the mouse cursor is. Flash has two properties we can use for this: mouseX, which gives the x-coordinate of the mouse cursor, and mouseY, whose functionality you can probably guess. As mentioned above, anything that extends a MovieClip will have an x and a y property that we can edit. So (lines 20 & 21):

?View Code ACTIONSCRIPT3
13
14
15
16
17
18
19
20
21
22
23
24
25
26
		public function AvoiderGame() 
		{
			enemy = new Enemy();
			addChild( enemy );
 
			avatar = new Avatar();
			addChild( avatar );
			avatar.x = mouseX;
			avatar.y = mouseY;
 
			gameTimer = new Timer( 25 );
			gameTimer.addEventListener( TimerEvent.TIMER, moveEnemy );
			gameTimer.start();
		}

If you run this, the avatar will indeed appear at the mouse cursor (this is easier to see if you use the keyboard shortcut for Test Movie which is Ctrl-Enter on Windows and Command-Enter on Macs). Since we use the gameTimer to alter the enemy’s position every split-second, let’s also use it for the avatar’s position (lines 31 & 32):

?View Code ACTIONSCRIPT3
28
29
30
31
32
33
		public function moveEnemy( timerEvent:TimerEvent ):void 
		{
			enemy.moveDownABit();
			avatar.x = mouseX;
			avatar.y = mouseY;
		}

Oh, wait, moveEnemy isn’t really a suitable name for the function now. How about moveEnemyAndAvatar?

?View Code ACTIONSCRIPT3
28
29
30
31
32
33
		public function moveEnemyAndAvatar( timerEvent:TimerEvent ):void 
		{
			enemy.moveDownABit();
			avatar.x = mouseX;
			avatar.y = mouseY;
		}

We’ll have to update the gameTimer’s event listener to point to this function with its new name, or it’ll get confused:

?View Code ACTIONSCRIPT3
24
			gameTimer.addEventListener( TimerEvent.TIMER, moveEnemyAndAvatar );

Save and run the game, and bask in the glory of your new, skull-shaped mouse cursor!

screenshot

Putting the “Avoid” in “Avoider Game”

You might have noticed that if you do happen to run your avatar into the enemy… absolutely nothing happens. This isn’t very good for an avoider game, so let’s correct that.

How will we know if the avatar has hit the enemy? MovieClips have a built-in function called hitTestObject which detects whether the MovieClip is touching another specified MovieClip. Naturally our Avatar and Enemy classes have this as well. Since the objects only move when the gameTimer goes off, we should check for a collision in the gameTimer’s event listener’s connected function (lines 34-37):

?View Code ACTIONSCRIPT3
28
29
30
31
32
33
34
35
36
37
38
		public function moveEnemyAndAvatar( timerEvent:TimerEvent ):void 
		{
			enemy.moveDownABit();
			avatar.x = mouseX;
			avatar.y = mouseY;
 
			if ( avatar.hitTestObject( enemy ) ) 
			{
 
			}
		}
  • avatar.hitTestObject( enemy ) — this will return a value of true if the avatar is touching the enemy
  • if — “if the statement between the next pair of parenthesis is true, then run the code between the next pair of curly brackets

At the minute there’s nothing between the curly brackets, so nothing will happen upon a collision. Before we add anything there, we should really change the name of the moveEnemyAndAvatar function, since once again it is no longer accurate. I can see that we’ll be adding more and more to this function in future parts of this tutorial, so let’s not bother with a cumbersome name like moveEnemyAndAvatarAndAlsoCheckToSeeIfTheyHaveCollided. Instead, I’m going to define a new word:

  • Tick — every time the gameTimer goes off, that’ll be called a Tick from now on. Everything that needs to be updated or checked will occur once per Tick.

So now we can rename the function to onTick:

?View Code ACTIONSCRIPT3
28
29
30
31
32
33
34
35
36
37
38
		public function onTick( timerEvent:TimerEvent ):void 
		{
			enemy.moveDownABit();
			avatar.x = mouseX;
			avatar.y = mouseY;
 
			if ( avatar.hitTestObject( enemy ) ) 
			{
 
			}
		}

Don’t forget to change the gameTimer’s event listener to point to the new name. We won’t need to alter it again after this.

Finally, let’s make the game actually do something when the enemy hits the avatar. Since I haven’t covered lives, we haven’t made a game over screen, and there’s no scoring system yet, I’ll take this as an opportunity to show you something.

You know how we had to start the gameTimer before anything happened? Well, add this to your onTick event (line 34):

?View Code ACTIONSCRIPT3
28
29
30
31
32
33
34
35
36
		public function onTick(event:TimerEvent):void {
			enemy.moveDownABit();
			avatar.x=mouseX;
			avatar.y=mouseY;
 
			if (avatar.hitTestObject(enemy)) {
				gameTimer.stop();
			}
		}

Now save and run the game, and see what happens when you run into the enemy. Don’t worry, the game hasn’t crashed; we’re just no longer running any Tick code. Two things we can notice from this:

  1. The collision detection is terrible
  2. A pause feature is going to be very easy to implement later

Wrapping Up

So there’s your very basic Avoider Game. If you want to modify it and give all the code to someone else, just zip up the main folder (mine was called AvoiderGame-MJW, remember) and send it to them. You can download mine from this link. Alternatively, if you just want to give them the game itself, without them being able to edit it, switch to your FLA, select File > Export > Export Movie, give a name to your SWF file, and send that instead.

I realise that what we’ve got so far is very crude, both in terms of code and gameplay. Now that we’ve got the basic framework down, future parts of the tutorial will be able to add more in less time. In the next part, we’ll add multiple enemies, and after that we’ll be adding a clock, and a game over screen, and lives, and a scoring system, and, well, everything that Frozen Haddock’s series has.

Thanks for reading!

Edit: The next part is available!

{ 226 comments… read them below or add one }

Michael Williams July 5, 2010 at 7:08 pm

@Jordan: What’s the actual error you’re getting?

@Jake: I’m sure ayumilove helped you out there :) But for future reference, all the code is included in the zip file at the end of the post.

@ayumilove: Thanks for helping out again :)

Anton July 15, 2010 at 9:47 pm

Hey Michael, thanks a lot for the great tutorial. However I have a question, and despite my attempts to google an answer I found nothing. The question is, what to do to prevent enemies spawning on eachother or close to eachother?

Thank you.

Anton July 15, 2010 at 10:30 pm

Heh, disregard the previous comment about asking how.. I figured out what approximately to do, but cant get it to work! :(
Any tips on what am I doing wrong? When executing the code with that while loop nothing happens, no avatar, no enemies:

            if (Math.random()<0.05)
            {
                var randomX:Number = Math.random()400;
                var newEnemy:Enemy = new Enemy(randomX, -10);
                army.push(newEnemy);
                while (newEnemy.hitTestObject(enemy)
                {
                    newEnemy.x = Math.random()400;
                    newEnemy.y = 0
                }
                addChild(newEnemy);
            }

Anton July 15, 2010 at 11:16 pm

I should learn to think a bit before posting, sorry for the spam.. Anyways, nailed down issue to the following error: D:\Develop\Flash\First game\Classes\first.as, Line 41 1067: Implicit coercion of a value of type Class to an unrelated type flash.display:DisplayObject.

Code:

if (Math.random() < 0.05)
            {
                var randomX:Number = Math.random() * 400;
                var newEnemy:Enemy = new Enemy(randomX, -10);
                army.push(newEnemy);
                if (Enemy)
                {
                    while (newEnemy.hitTestObject(Enemy))
                        {
                            newEnemy.x = Math.random() * 400;
                            newEnemy.y = 0;
                        }
                }
                addChild(newEnemy);
            }

bob July 20, 2010 at 9:03 am

thanks! this is my first as3 tutorial and i think that it is no doubt the best! i have started other ones but they didnt prove helpful :/

Michael Williams July 23, 2010 at 11:46 am

@Anton: No worries about the “spam;” I find I often figure things out immediately after asking about them :)

Anyway, I take it line 41 is while (newEnemy.hitTestObject(Enemy))?

The problem is, Enemy (capital E) refers to the class, the “blueprint,” for an enemy object. You’re asking Flash to check whether the new enemy you’ve just created is colliding with this blueprint. That doesn’t make any sense, because the blueprint isn’t an actual enemy object.

Instead, think of this solution:

  • Does the new enemy collide with the first enemy in the army array? If so, move it.
  • If not, does the enemy collide with the second enemy in the army array? If so, move it.
    • If not, does the enemy… etc.

See where I’m going with this?

By the way, good call on using a while-loop for that :) You’re going about this in the right way, just a little confused on the particulars.

@Bob: Awesome, thanks so much :)

Anton July 23, 2010 at 1:33 pm

Thanks for the hints Michael, I made some changes and it looks like works now. You were right, I mixed up the enemy class with the actual objects on screen, so I changed that, thanks!

Instead of collision I check for the distance between the avatar and each of the enemies in my array list. Seems to work pretty decent.. Hopefully it wont cause issue later on! ^^

Michael Williams July 26, 2010 at 3:45 pm

You’re welcome, Anton :)

Good idea using a distance check for the collisions. Later on we’ll look at another way to handle collisions, but it looks like you’ll be more than capable of coping with that!

Joshua July 30, 2010 at 3:52 am

Hi Michael.
I have two computers, one I’m doing your tutorial, the other I’m using your tutorial to make my own game.

To start off, I got a main character and an enemy(Zombie)
I got the zombie to follow the character.

When I got to using the for each loop, things kinda died.
Every zombie is appearing at 0,0 and they speed up far to fast.\

Here’s the code that’s bugging me, and I know why they move too fast, I just don’t know how to fix it.

public function moveEntities(timerEvent:TimerEvent):void
        {
            if (Math.random()<0.1)
            {
                var randomX:Number = Math.random()*400;
                var newStandardZombie:StandardZombie = new StandardZombie(randomX, -15);
                zombies.push(newStandardZombie);
                addChild(newStandardZombie);
            }
            playerOne.x = mouseX;
            playerOne.y = mouseY;

        //Zombie movement
        for each (var standardZombie:StandardZombie in zombies)
        {
            speedY = speedY + aY;
            speedX = speedX + aX;
            speedY = speedY * friction;
            speedX = speedX * friction;
            if (playerOne.x > standardZombie.x)
            {

            aX = aX + .2;
            standardZombie.standardZombieMove(speedX,speedY);
        }

        if (playerOne.x &lt; standardZombie.x)
        {
            aX = aX - .2;
            standardZombie.standardZombieMove(speedX,speedY);
        }

        if (playerOne.y &gt; standardZombie.y)
        {
            aY = aY + .2;
            standardZombie.standardZombieMove(speedX,speedY);
        }

        if (playerOne.y &lt; standardZombie.y)
        {
            aY = aY - .2;
            standardZombie.standardZombieMove(speedX,speedY);
        }
        //hittest
        if (playerOne.hitTestObject(standardZombie))
        {
            gameTimer.stop();
        }
    }

James July 30, 2010 at 6:01 pm

@Joshua, have you checked in your newStandardZombie class to make sure you are actually using the x & y parameters of the constructor to place the zombie at the desired location

Also, the reason that they are speeding up so fast is because it looks like you are storing the zombie movement info like speedX and speedY as variables in your gameScreen, but this means that per frame for every zombie you have you are increasing the acceleration of every zombie. The speed/acceleration variables should all be stored within the zombie class, that way each zombie is only affecting it’s own movement, not the movement of everyone.

James July 30, 2010 at 10:39 pm

change

x = speedX;
y = speedY;

to

x += speedX;
y += speedY;

otherwise, if speed is 10, then that will just mean the zombie’s x position will be 10 rather than being moved by 10.

Joshua July 30, 2010 at 10:58 pm

Um.. no, that would just increase the speed even more.
It’s accelerating when speedX = speedX + aX.

What’s causing it to go to fast is that when it adds aX to speedX, it does that the first time fine. When the next zombie spawns, it does it again…twice. for each enemy on the screen.
They also react based on the first zombie’s position to the player, rather than each on their own. so if the original is to the right of the player, he’ll move left, but so will any zombies to the left of the player.

to see what I mean, check out the first post here: http://www.horizonshadow.org

I still can’t get them to spawn randomly -.-

Joshua July 31, 2010 at 3:30 am

I tried to fix some things…
I think they’re going too fast because they’re speeding up for each zombie that spawns, so I don’t know where to put the if statements.

I got the vars in the zombie class, and figured out why they didn’t spawn correctly, but don’t know how to fix it.
If I do this:

public function standardZombieMove(aX:Number,aY:Number):void
        {
            speedY = speedY + aY;
            speedX = speedX + aX;
            speedY = speedY * friction;
            speedX = speedX * friction;

        //x = speedX;
        //y = speedY;
    }

Then they spawn fine, they just don’t move.
This is my zombie class as of right now:

package  {

import flash.display.MovieClip;
import flash.utils.Timer;
import flash.events.TimerEvent;

public class StandardZombie extends MovieClip
{
public var moveTimer:Timer;
//physics vars
public var speedX:Number=0;
public var speedY:Number= 0;
public var friction:Number = .98;

public var aX:Number = 0;
public var aY:Number=0;

public function StandardZombie(placeX:Number,placeY:Number)
{
    x = placeX;
    y = placeY;

    moveTimer = new Timer(25);
    moveTimer.addEventListener(TimerEvent.TIMER,movement);
    moveTimer.start();

}

public function movement (timerEvent:TimerEvent):void
{

}

public function standardZombieMove(aX:Number,aY:Number):void
{
    speedY = speedY + aY;
    speedX = speedX + aX;
    speedY = speedY * friction;
    speedX = speedX * friction;

    x = speedX;
    y = speedY;
}

}

}

Thanks for your help… I’m sure once I get the hang of this I won’t be asking such silly questions >.>

ayumilove July 31, 2010 at 9:49 am

@Joshua

Use this…

public function standardZombieMove(aX:Number,aY:Number):void
{
    x = aX * friction;
    y = aY * friction;
}

Delete these properties from your class

public var speedX:Number=0;
public var speedY:Number= 0;

Joshua July 31, 2010 at 4:32 pm

great, you eliminated the acceleration factor. Except I need that to make the zombies move smoothly.
What you just told me to do makes them simply STOP, and then go the other way. I want it to round, make a corner.

What I don’t want it to do is multiply the speed by every zombie on the screen, and for them to spawn randomly on the top of the screen… which works when I remove everything related to moving.
For the sake of examples, I went looking for an example of what I want the zombies to do at the moment. http://hiddenspartan.deviantart.com/#/d2r2fcl
I’d fix this on my own but I don’t know enough about the language

Joshua July 31, 2010 at 4:41 pm

I dunno if it helps at all, but I can get them moving without speeding up so fast, they just stop reacting to the players position.

Josh July 31, 2010 at 8:44 pm

I think I’m doing this wrong headdesk
I appologize for the spam, but is there a particular peice of code that just makes an object move towards a position?
That would be so much easier >.>

atm they can only move in 8 directions, but a full range of movement would be so much better.

James August 3, 2010 at 12:47 am

I’m folowing your tutorial but i can’t get passed the part where we mess around with the properties of the enemy.Why? Because the base class part doesn’t want to save as flash.display.MovieClip!!!!!!!

James August 4, 2010 at 3:07 am

I got tmy problem fixed.

The problem was because, tryig to make sure I don’t copy paste anything(and learn the syntax) I don’t use the exact same names(but it didn’t give any errors, it simply did the pre described problem).

Tim August 11, 2010 at 9:52 am

I got to the end of ‘Get your head in the Game’ and the output box gave mne this:

TypeError: Error #1009: Cannot access a property or method of a null object reference.
at AvoiderGame()
Help?

Michael Williams August 21, 2010 at 12:23 am

Seems I let these comments pile up again. Sorry about that.

@Joshua: Check out Daniel Sidhion’s tutorial, Moving with mouse and using speed. I expect you can modify the code to make the zombies follow the player, rather than making the avatar follow the mouse.

@James, @Ayumilove: Thanks for helping Joshua out! Your comments were helpful :)

@James (different James?): Ah, I think later versions of Flash changed things so that you just leave that field blank instead of typing flash.display.MovieClip. It’s frustrating, I know :/

@Tim: Check out the first part of my Debugging Guide; it’ll help you narrow the problem down which will make it easier for me to understand and fix.

Tim August 25, 2010 at 7:24 am

Forgot to come to this. I did the trace stuff, now I’ve got this:

B
C
A
D
TypeError: Error #1009: Cannot access a property or method of a null object reference.
at AvoiderGame()

Location of “D”:
public function AvoiderGame()
{
trace(“D”)
enemy = new Enemy();
addChild( enemy );
avatar.x = mouseX;
avatar.y = mouseY;

Tim August 25, 2010 at 7:53 am

Nevermind! I decided to just carry on and get the codes, after I finished I tested (I dunno why I bothered) and it worked! Onwards to Part 2!

ayumilove August 25, 2010 at 9:38 am

TypeError: Error #1009: Cannot access a property or method of a null object reference.
at AvoiderGame()

Make sure your avatar is not NULL
Make sure the item that addchild into its container isn’t null.

Btw, use ctrl+shift+enter to get the line number where the error occurs.

Michael Williams August 28, 2010 at 12:08 am

@Tim: Good to hear it’s fixed :)

@Ayumilove: Great tips, as usual :D

Christopher September 2, 2010 at 5:41 pm

PLEASE HELP ME!! ok so im only on part one towards the end were we should have a single enemy moving down the screen. so i have two problems. the first problem is that you know how it starts halfway at the screen so we have to fix it so it starts all the way off the screen? so we changed the coding for y to be – w/e and so i did mine -15 like yours. and nothing changed. so i said hmm maybe its just need to be higher so i put it at -50 and it didn’t change. so then i give up on that and changg the center point thingy to the bottom and even that didn’t work. so the other problem is getting him to move down. so just to make sure i didn’t mess up on the enemy.as i copy and pasted your code. and it stayed the same so i looked at the avoidergame.as over and over and over again and i found a typo or two. one that i thought would help is that i had

Public Class Enemy Extends MovieClip

so i changed the Extends to extends but even that didn’t change a thing what use to happend when i tested would be just it would go to the top cut off halfway and stay there with no error. then i got a error 1046 Type was not found or was not a compile-time constant: Enemy and then i saw that my document Avoidergame.fla was lowercase but my code was upercase so i change that then i got like 50 errors then i change it back and now im just stuck with 1046 again. btw i dunno what those 50 errors were… sorry

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

{ 13 trackbacks }

Previous post:

Next post: