Creating a Simple Shooting Game in Flash
Actionscript 3



What you will learn:

  • how to build a simple shooting game using Flash AS3
  • how to build functions and classes commonly used in game design
  • how to create a game loop
  • how to control and animate objects onscreen

Prerequisites:

  • working copy of Flash CS4 or above
  • working knowledge of how to use the Flash interface
  • basic knowledge of AS3
  • knowledge of document classes (or see link given below)

Goal
We are going to make a simple shooting game using Flash Actionscript 3 (AS3). At the end of this tutorial, you will have a working game that you can use and customize as you wish. The completed code is given at the end of this document. The game that we are going to design is a very simple shooting game; the goal is to quickly learn the minimum that you need to know to get you started in game design in Flash. Along the way, we’ll cover some tips and techniques that will help you to understand more about the process of game design.


You can see an example of the finished game here: http://flashbynight.com/tutes/simplegame/game.html


If you’d like to download the completed project, you can do so at: http://flashbynight.com/tutes/simplegame/game.zip

 

Step 1

The first step is to prepare the basic documents that we will need. We need to create a Flash file named Game.fla and we need to create a document class called Game.as, where we will hold most of our code. We will create one other .as file as we work through the tutorial, but we’ll discuss that later.

*If you are unfamiliar with document classes, there is a quick guide here:  http://www.flashbynight.com/tutes/doc_class
Once you have done this, proceed with step 2. 

Step 2
OK, we’re going to need to set up our .fla file and add some graphics. Our .fla file, Game.fla, will only require one blank frame, believe it or not. Everything in the game will be added dynamically and removed when it is no longer needed. We do not put any code in the main timeline. All the code will be in Game.as, where we can find it easily. (Almost all the code, but more about that later.)


We do want to have a black background for the .fla file (since it is a ‘space’ shooter). And we need to set the frame rate to 35 fps, which will help the motion to look smooth to the human eye. Most games are set somewhere between 25 – 35 fps. Lower than this and the motion will seem jerky. To do this, use CTRL+J and a dialog box will appear. We also need to set the Flash document size to: Width – 800 and Height – 600. This makes the movie nice and big, but it will fit into most laptop screens without scrolling.

Step 3
Now, your blank document class (Game.as) should look like this (note that capitalisation counts in Flash):

package  {
           
            import flash.display.MovieClip;
           
           
            public class Game extends MovieClip {
                       
                       
                        public function Game() {
                                    // constructor code
                        }
            }
           
}

 

What we want to do next is to put the framework of the game into place. Here we go:

package {
            import flash.display.MovieClip;
            import flash.utils.Timer;
            import flash.events.TimerEvent;
            import flash.events.KeyboardEvent;
            import flash.events.MouseEvent;
           
                        public class Game extends MovieClip {
                                   
                                    //common
                                   
                                     
                                     //assets
                                   
                                     
                                     //variables
                                   
                                     
                                     //arrays
                                   
                                   
                                               
                        public function Game() {
                                   
                                    }//game
                                   
                       
                                   
                        public function startscreen(){
                                   
                                   
                        }//sscreen
                       

                       
                        public function SetUp(){
                       
                        }//setup
                       

                        ///////**********************************************//////////////////////
                        public function onTimer(evt:TimerEvent):void{                     
                                               
                                                IntroduceEnemies();
                                                ProcessUserInput();
                                                MoveObjects();
                                                CollisionDetection();
                                                RemoveDeadObjects();
                                                UpdateDisplay();
                                                CheckForGameOver();
                                   
                                                }//GAMELOOP
                        ///////**********************************************//////////////////////
                       
                                   
                                    public function IntroduceEnemies(){}//intro enemies
                                   
                                    public function ProcessUserInput(){}//Process User Input
                                   
                                    public function MoveObjects(){}//move objects
                                               
                                    public function CollisionDetection(){};//CollisionDetection
                                   
                                    public function RemoveDeadObjects(){};//remove dead objects
                                   
                                    public function UpdateDisplay(){};//update display
                                               
                                    public function CheckForGameOver(){};//gameover
                                   
            }
}

 

Don’t compile the flash movie yet; you will get an error at this stage, because we have called a timer without declaring it yet. However, we can see the whole structure of the game laid out here. Let’s walk through it:

1 Immediately after declaring the package, we need some import statements, since we are using a document class. Without going into detail, these import statements give us control over the use of movieclips, mouse input, keyboard input and the timer.

2 Straight after this, you will see that we have left some space to declare variables, which will be organised into categories: ‘common’ variables which we expect to use in any game that we make, ‘assets’ which includes objects and moveclips that we will reference from the library, various variables that we need for this game and any arrays that we will need to use. Starting this way keeps our code organised and easy to read. Our ‘common’ variables can be copied for other game projects – reusability is a key concept in programming and especially in game programming.

3 Following this, we have three functions: Game(), which will run when the Flash Movie runs (we call it a movie even though it’s a game); StartScreen(), which will control our intro screen; and SetUp(), which we will use to kick off the game. I use these functions in every game I make (and I’ve made lots).

4 Next up is our game loop, which is controlled by a timer. The game loop is very important in an arcade game and even professionally made ‘triple A’ style games use a game loop. Game loops are used in pretty much every non-puzzle game and many puzzle games too.

When the user plays a video game, they get the impression that many things are happening at the same time. In fact, that’s not true. The game loop provides a sequence for the actions to happen. Keep in mind that our sequence will be run 35 times per second.

If you read through the functions called in the game loop, it should be pretty clear what each one does. This, by the way, is an important technique in programming. You should write your variables and function names so that they are human readable. Hence, ‘IntroduceEnemies()’ is a good name for a function. ‘InEn4x()’ is a bad name for a function. You might think that this is not important if you’re working alone and nobody else will read your code, but, trust me, you will be glad you followed my advice when you go back to rewrite your code one year from now.

5 Finally, we have the actual functions after the game loop. We will need to write two or three extra functions as we go along. By the way, we add ‘public’ or ‘private’ before each function to specify whether the function can share variables outside of its class. This is important for big projects, less important for small projects.


Step 4
Let’s sort out the intro screen.

Flesh out the Constructor function:

            public function Game() {
                        timer.addEventListener(TimerEvent.TIMER,onTimer);
                        StartScreen();
                        }//game

The only time this function will be called is when the Flash Movie begins. We add a timer called ‘timer’ and attach a function to it, called ‘onTimer’. Then we call the function StartScreen().

We also need to declare the timer, under //common as so:

public var timer:Timer = new Timer(28);

A few notes:

1 Our timer is called ‘timer’ with a small ‘t’ and it belongs to the class Timer with a big ‘t’. This habit of naming something with different capitalisation is very common among flash programmers.

2 Why ‘28’? We want our timer to tick every 28 milliseconds, which will match the 35 frames per second we have in our movie (1000/28 = roughly 35). If these are out of sync, our action will seem jerky again.

Now we need to create two objects: the title of the game and a ‘start’ button. As you’ve noticed, if you’ve clicked the link at the top, we’re going to keep the graphics simple and concentrate on the coding.

For a title, I simply wrote out ‘My Game’ in large font and then right-clicked it to choose ‘convert to symbol’

Give it the name ‘Title’, tick the box ‘export for actionscript’ and make sure the class name is ‘Title’ (see image below). Then go ahead and delete it from the stage. We will add it to the stage dynamically when it is needed.
a

Now do the same thing to create a Start button. Well, we call it a button, but it can be a MovieClip too. A button has the advantage of having easily editable hit states, but that’s out of the scope of this tutorial. Mine looks like this; I used a white box with alpha value 7% behind the text. The class name should be ButtonStart. I find that if I name all my buttons ButtonSomething, it makes them easy to find in the library. Speaking of which, delete the Start button from the screen and we will access it from the library.

b

Time for some more code. Let’s flesh out the StartScreen function. Remember, we will also use this function when the player wants to replay the game.

Under //assets add:

            public var buttonstart:ButtonStart;
            public var gametitle:Title;

Then inside the StartScreen function add:

            gametitle=new Title;addChild(gametitle);
            gametitle.x=400;gametitle.y=100;

This will add the title to the screen. Adjust the x and y coordinates if you wish.

Then add:

            buttonstart=new ButtonStart;addChild(buttonstart);
            buttonstart.x=407;buttonstart.y=327;

This will add our start button to the screen. By the way, purists don’t like to have two bits of code on one line (because it makes it harder to debug; debuggers give a line reference), but I like to group related bits of code together because I think it makes the code more concise and therefore easier to get the big picture.

We need to make our button clickable. To do this, we add a ‘listener’ and then attach a function to the listener:

            buttonstart.addEventListener(MouseEvent.CLICK,buttonstartfunction);
            function buttonstartfunction(eventObject:MouseEvent){
                        removeChild(buttonstart);removeChild(gametitle);
                        SetUp();
                        }//buttonstart

So you can see that our listener checks for a mouse click and then calls buttonstartfunction, which removes the two objects from the screen and calls the SetUp function, which will set up our game. You should test out the movie now.

Step 5
Okay, what do we want to do next? To set up the game, we should add our ‘hero’ to the stage, which in this case will be a spaceship. But wait, we need to create the spaceship graphic first.

As I said, we are not looking at graphic design here, so – don’t laugh – we’ll use a simple triangle to represent our spaceship. I used a triangle that was Width:50 x Height:33. I made sure the apex of the triangle was exactly in the middle and then I converted it to a MovieClip with the class name ‘Hero’.

Make sure the registration point is in the top middle; I’ll explain why later:

a

b

By the way, I always name the player-controlled object ‘Hero’. It means my code is more reusable and I can work faster if I have a set of conventions to go by. I can look at the code to any game I’ve made and quickly find any references to the player controlled object.

Under //Assets add the line:

public var hero:Hero;

This allows us to create instances of the MovieClip Hero, from the library, with the name ‘hero’.

Now in our SetUp() function, you should add these two lines:

            hero=new Hero;addChild(hero);
            hero.x=400;hero.y=500;hero.dead=false;

The first line creates an ‘instance’ of the spaceship and then adds the instance to the stage. The second line sets the x and y coordinates. It also creates an attribute ‘dead’ and sets it to false. Later, when an enemy hits the spaceship, we will set hero.dead=true and this is how we will track that the game ends.

Under //Common, let’s define a variable to track the score:

public var score:int;

And in SetUp():

            score=0;

We will need a way to track the score, so let’s create a score box. I simply created a dynamic text box and gave it the name scoretext:

c

Then, I right-clicked, selected ‘convert to symbol’ and converted it to a Movie Clip with the class name ScoreBox. Then I deleted it from the stage, to access it from the library. Make sure you got that right – scoretext is the text box and ScoreBox is the name of the MovieClip it resides in.

Under //Assets:

            public var scorebox:ScoreBox;

In SetUp():

            scorebox=new ScoreBox;addChild(scorebox);
            scorebox.x=25;scorebox.y=12;
                       

You may need to adjust the x and y coordinates.

Now for something important. We are going to need a way to track the user’s keypresses. We do this by adding two listeners: one for when any key is pressed down and one for when any key is ‘up’ again after being pressed down. We put these two lines in SetUp():

            stage.addEventListener(KeyboardEvent.KEY_DOWN, KeyDownHandler);
            stage.addEventListener(KeyboardEvent.KEY_UP, KeyUpHandler);

These two lines call the functions KeyDownHandler and KeyUpHandler respectively, so we will need to write these functions (not inside SetUp(), but as separate functions):

            public function KeyDownHandler(e:KeyboardEvent){
                        trace(e.keyCode);
                        if(e.keyCode==39){Right=true;}
                        if(e.keyCode==37){Left=true;}
                        }//keydown
                                   
            public function KeyUpHandler(e:KeyboardEvent){
                        if(e.keyCode==39){Right=false;}
                        if(e.keyCode==37){Left=false;}
                        if(e.keyCode==32){Space=true;}
                        }//keyup

In each of these functions, information from the KeyBoardEvent is sent and captured in object ‘e’. We can use e.keyCode to determine which key is pressed. We can use a trace statement - trace (e.keyCode); - to find the code for any key if we don’t know it.

In these two functions, we have used the variables, Right, Left and Space, so they need to be defined under //Variables:

            public var Left:Boolean;
            public var Right:Boolean;
            public var Space:Boolean

Note that we have coded it so that Right = true, when the Right arrow is pressed and Right = false when the arrow key is ‘unpressed’ and the same with the Left arrow key. However, for the Spacebar, it is registered as true when it is unpressed. Why do we do it this way? We’ll discuss that later on.

To finish up our SetUp() function, we need to add two final lines of code:

            BulletArray= new Array;EnemyArray= new Array;
            timer.start();

We will be tracking our bullets and enemies using these respective arrays. Then we need to start the timer, which will control the game loop.

Make sure the arrays are defined under //Arrays:

            public var BulletArray:Array;
            public var EnemyArray:Array;

Then test out the movie. Yes, nothing happens except for the trace that tells us what key is pressed. Nothing much happens yet, but our game is nicely set up and we can start doing stuff with it. All the code we have written until this point is very recyclable. You could save a copy of this project now and use it as the starting point for many arcade-style games.

Step 6
Enemy time. As I mentioned, we are not doing fancy graphics here (although you can if you like). All I did was to take the same triangle that I used for the ‘hero’, flipped it upside down, turned it red, changed the dimensions to W:33 and H:22 and renamed it Enemy1 with the class name Enemy1:

jh

Yes, I called it Enemy1 even though we only plan to have one enemy in our game. But most games have more than one enemy and we always want our code to be reusable.

So under //Assets:

            public var enemy1:Enemy1;

Under //Common we need to define a variable we will use for a random number:

public var rnd1:int;

…and we need to flesh out the function IntroduceEnemies():

            public function IntroduceEnemies(){
                        rnd1=Math.random()*30;
                        if(rnd1==1){
                                    enemy1=new Enemy1;addChild(enemy1);
                                    enemy1.x=Math.random()*800;enemy1.y=-30;
                                    EnemyArray.push(enemy1);
                                    }//if
                        }//intro enemies

Line by line, we choose a random number out of 30 and if this number equals one, we add a new enemy, setting the x and y coordinates and adding the object to the array, EnemyArray. Since each enemy will have the same name, we will use EnemyArray to refer to them from here on. The first one is EnemyArray[0], the second is EnemyArray[1], the third is EnemyArray[2], and so on.

Why did we choose a random number out of thirty? Well, remember, this function will be called 35 times per second, so a random number from thirty will give us a new enemy roughly every one second.

Test the movie and check that it works. The enemies appear just above the stage, so you will need to drag the border of the flash movie to see them. We want the enemies to appear just off stage because it seems odd to the player if they suddenly appear onstage.

As of now, they don’t move, but we will address that next. Why don’t we use the same function to move the enemies and kill two birds with one stone? We want one function to do one job. That keeps the code nice and neat, reusable, easily readable and easy to debug.

 

Step 7
Okay then, here we are going to learn a little programming trick. We have already created a class called Enemy1, now we are going to create a separate actionscript file especially for this class. This means that the constructor function will run whenever an instance of this class is created. We can also attach attributes and functions to this class.

Find Enemy1 in the Library, right-click on it and select Edit Class. Flash will create an .as file for you with some code already in place. Note: I’m using Flash CS5, and the class is given the default name Script-1.as; you will need to rename it Enemy1.as.

Bear with me here. In the document class, in the function MoveObjects, add the line:

            for(i=0;i<EnemyArray.length;i++){EnemyArray[i].action();}

This line will go through each enemy, stored in EnemyArray, and call the function action();

Under //Common, add the variables we will use for basic and nested loops:

            public var i:int;
            public var ii:int;

And in Enemy1.as, add the test function:

            public function action():void {
                        trace("action");
            }//action

Test the flash move if you like.

Can you see what we’ve done here? Whatever code we put in the action() function will be controlled by MoveObjects in the document class (Game.as). That way, we can control the motion of multiple objects at an exact position in the game loop.

Here is the entire code for Enemy1.as:

package {
            import flash.display.MovieClip;
           
public class Enemy1 extends MovieClip {

                        public var yspeed:int=5;public var xspeed:int=5;
                        public var mydirection:String;public var rnd1:int;
                        public var dead:Boolean;
                       
                        public function Enemy1() {
                                    rnd1=Math.random()*2;
                                    if(rnd1==0){mydirection="left";}
                                    if(rnd1==1){mydirection="right";}
                        }

                        public function action():void {
                                    //trace("action");
                                    this.y+=yspeed;
                                    if(mydirection=="right"){this.x+=xspeed;}
                                    if(mydirection=="left"){this.x-=xspeed;}
                                    if(this.x<5){mydirection="right";}
                                    if(this.x>755){mydirection="left";}
                                    if(this.y>610){dead=true;}
                                   
                        }//action
                       
            }
}

Not to go into too much detail, but we use a random variable to determine whether the motion starts off as left or right. We also use variables to track the current direction (mydirection), the x and y speed and we have a variable “dead” which will let the document class know to remove the object from the screen and memory. Observe this line:

            if(this.y>610){dead=true;}

When the enemy goes off the bottom of the screen, it is marked as ‘dead’. We need to be careful about removing objects that we don’t use or we will get ‘memory leaks’ – the flash movie will start consuming more and more memory and it will potentially crash the user’s browser or computer. Removing ‘dead’ objects is sometimes called ‘Garbage Collection’ in programming.

When the enemy hits the right side of the screen, it changes direction and vice versa.

Test the Flash movie now and you should observe the enemies in motion.

Why did we use this method of controlling the enemies with code attached to their class instead of in our document class? The answer, as usual, is that it makes our code easy to find and easy to recycle in other projects. With a large project, it means different people can easily work on different parts of the code. Personally, I like to use this technique with game characters because the ‘AI’ is all in one place.

Step 8
Our hero is just sitting there, but he doesn’t move yet, even though we can detect when the arrow and space keys are pressed (step 5). Let’s make our spaceship move it, move it.

We already have a skeleton function called ProcessUserInput(), so we just need to fill it out:

            public function ProcessUserInput(){
                        if(Right && hero.x<780){hero.x+=5;}
                        if(Left && hero.x>20){hero.x-=5;}
                        if(Space){Shoot();}
            }//Process User Input

Note that this code will generate an error at the moment because we haven’t written our Shoot() function yet. Anyway, basically the code says that if the right arrow key is pressed (Right==true) AND the hero’s x coordinates are less than 780 (the edge of the playing area), to move the hero’s x position by 5 pixels. We can increase the speed of the motion by increasing the number 5. It could also be good to set a variable named ‘speed’ for more complex code. We do the mirror image if the left arrow key is pressed. And we need to call the function Shoot() if the spacebar is pressed, so that we can shoot bullets.

Let’s shoot some bullets. Okay, first we need to create them. Simple graphics again, I took the same triangle used for the hero graphic, changed the dimensions to W:10 H:7 and gave it the class name Bullet in the library.

Under //Assets, declare:

public var bullet:Bullet;

Write the Shoot() function:

 

            public function Shoot(){
                        Space=false;
                        bullet= new Bullet;addChild(bullet);
                        bullet.x=hero.x;bullet.y=hero.y;
                        bullet.dead=false;
                        BulletArray.push(bullet);
            }//shoot

First, we reset Space to false; this means that the user cannot hold down the spacebar to shoot, but must tap it continuously for repeated shots. Next we add the bullet at the same x and y coordinates as our hero – it will appear at the apex of the spaceship because of the way we positioned the MoveClip in Step 5. We add a property ‘dead’ which we will use to track when we need to remove the bullets. Finally, we add the bullet to our BulletArray.

If you test now, the bullets will appear (behind the spaceship), but they won’t move. So let’s go back to MoveObjects() and add the line:

for each (var obj:Object in BulletArray){obj.y-=10;}

I used a ‘for each’ loop here, just to show you a different technique of looping through each object and moving it by changing the y coordinates by minus ten. Change 10 to change the speed of the bullet. Test the movie again.

 

Step 9
Collision detection is a crucial part of game programming. In our game, we need to know when our bullets hit an enemy and we need to know when an enemy hits our spaceship.

In Flash, we know when A hits B because A.hitTestObject(B) is true.

Let’s work on our CollisionDetection() function, by adding this code:

 

             for(i=0;i<EnemyArray.length;i++){
                        if(EnemyArray[i].hitTestObject(hero)){hero.dead=true;}
            for(ii=0;ii<BulletArray.length;ii++){                                                                           
if(EnemyArray[i].hitTestObject(BulletArray[ii])){
EnemyArray[i].dead=true;BulletArray[ii].dead=true;score+=5;}
            }}//i, ii

See what we did? We looped through each enemy (referenced in EnemyArray) and performed a hit test against our hero. If the hit test returned ‘true’, we set the hero property ‘dead’ to true. Later we will use this property to end the game.

In the second bit of the code, we used a ‘nested loop’ (a loop within a loop) to check each bullet against each enemy. If true, we set the enemy to ‘dead’, the bullet to ‘dead’ and increase the score by 5. Yes, all this happens 35 times per second, according to our game loop. Flash can handle hundreds of hit tests per second, no problem.

If you test the movie, nothing seems different, but we have set the collision properties, which we will use in the last couple of functions.

Step 10
Now that we can track when things are ‘dead’, we need to remove them from the screen and from our tracking arrays. We have already set up an array to do this: RemoveDeadObjects(). Now we just need to fill in the code:

            for(i=0;i<BulletArray.length;i++){if(BulletArray[i].dead  || BulletArray[i].y<0){
                        removeChild(BulletArray[i]);BulletArray[i]=null;BulletArray.splice(i,1);}}
            for(i=0;i<EnemyArray.length;i++){if(EnemyArray[i].dead){
                        removeChild(EnemyArray[i]);EnemyArray[i]=null;EnemyArray.splice(i,1);}}

 

So, first we loop through all of our bullets and if any of them are ‘dead’ OR (|| means ‘or’, by the way) the y coordinates are less than zero (meaning they have gone offscreen), we remove them from the screen (removeChild()), set the array value to null and then ‘splice’ them out of the array.

Then we do the same thing with the enemies. We don’t need to specify a y coordinate for the enemies going offscreen because we have already done that in step 7.

Test the movie now. Try shooting enemies and then expand the borders of the movie window to check that the bullets and enemies are removed after going offscreen.

Step 11
In the function UpdateDisplay(), add the line:

            scorebox.scoretext.text="Score: "+score.toString();

This will update the text in the dynamic textbox scoretext in the MovieClip scorebox, 35 times per second.

In a more complex game, we would use this function to update the number of lives, level and so on, but in our simple example, we only need this one single line.

Test the movie and try shooting enemies to see score tracking.

 

Step 12
We have only a couple more things to do. We have one last empty function called CheckForGameOver(). Well, we know when the game is over because hero.dead==true. We want to add a good old ‘Game Over’ message to the screen and we can use this as a button for the user to click on to replay the game.

I used the following graphic:

a

 

You can see that I added a bit of background, to give the user something to click on. It can be tricky to ask the user to click on pure text, because they will end up clicking the spaces in between the letters and nothing will happen.

I gave this the class name GameOverMessage and added, under //Assets:

public var gameovermessage:GameOverMessage;

Now we can finish our CheckGameOver function by adding the following code:

            if(hero.dead){
                        timer.stop();
                        gameovermessage=new GameOverMessage;addChild(gameovermessage);
                        gameovermessage.x=370;gameovermessage.y=200;
                                                            gameovermessage.addEventListener(MouseEvent.CLICK,gameoverfunction);
            function gameoverfunction(eventObject:MouseEvent){
                        RemoveAllObjects();
                        StartScreen();
                        }//go function
}//if hero is dead

 

Line by line: If we detect that the hero is ‘dead’, first we stop the gameloop using timer.stop();. The game is now officially over. Next we add our game over message from the library and set the x and y coordinates – you may need to fiddle with them a bit.

Following that, we make our game over message clickable by adding a listener – just like we did with the START button in step 4. If the button is clicked, we first need to call a function that clears the screen and then we can call the function StartScreen() again to set up the Intro screen again. One problem, the code won’t work yet because we haven’t written the RemoveAllObjects() function. Well, no problem, we can do that now:

 

public function RemoveAllObjects(){
                        for(i=0;i<BulletArray.length;i++){removeChild(BulletArray[i]);
BulletArray[i]=null;}
                        for(i=0;i<EnemyArray.length;i++){removeChild(EnemyArray[i]);
EnemyArray[i]=null;}
                        BulletArray=[];EnemyArray=[];
                        removeChild(hero);
removeChild(gameovermessage);
removeChild(scorebox);
                        stage.removeEventListener(KeyboardEvent.KEY_DOWN, KeyDownHandler);
                        stage.removeEventListener(KeyboardEvent.KEY_UP, KeyUpHandler);
                        }//remove all objects

This function has the following steps:

1 remove all bullets
2 remove all enemies
3 empty the two arrays, BulletArray and EnemyArray
4 remove the other onscreen objects: hero, gameovermessage, scorebox
5 remove the listener that we use to detect keypresses

Test it out. You should have one fully operational game! However, there is one thing that I’m not satisfied with and I wonder if you caught it.

Step 13

When an enemy hits our hero, the game seems to stop prematurely – just before the enemy actually hits. For instance:

a

 

Right. Well, welcome to the final part of the game creation process, where we have to go back and make some changes.

In Flash, hitTestObject tests in this way: the following two objects are said to be hitting each other:

 

a

Can you see that? Flash goes by the height and width coordinates, which causes problems if you have oddly-shaped objects.

Traditionally, game designers use one of these methods to work around this:

1 Design very squarish objects

2 Add explosions to the screen and hope nobody noticed the objects didn’t exactly collide

3 Add a smaller hit zone area (or multiple areas) inside an object.

We’re going to use the smartest method, number 3.

Go to the library and click on Hero to edit it. Then draw a box covering most of the spaceship and convert it to a MovieClip, like so:

a

 

Give the MovieClip the instance name HitZone – (the class name can be HitZone too).

Then change the alpha setting to zero, so it is sitting on top of our Hero, invisible. (Use the Color Effect settings in the Properties panel.)

a

 

Now we can perform a hit test against the invisible area rather than the entire clip. Find this line in CollisionDetection():

if(EnemyArray[i].hitTestObject(hero)){hero.dead=true;}

and change it to:

if(EnemyArray[i].hitTestObject(hero.HitZone)){hero.dead=true;}

Then see if that gives a cleaner ‘hit’.


Step 14

As a matter of fact, that’s it! The game is finished and you should test it out. Hopefully everything works. Now you know all the basic steps needed to create an arcade game, it’s time to go and create your own!

Obviously, this is the simplest game possible. Here are some ideas for enhancing and customizing it:

  • Add ‘hit zones’ to the enemies for better collision detection
  • Try a side scroller rather than a top-down scroller
  • Add more enemies with different behaviours
  • Add ‘lives’
  • Add sound and special effects, such as explosions
  • Add a level-up mechanism which triggers stronger enemies
  • Add a pause/quit function to the game
  • Add nicer graphics and a slick background
  • Add an instructions page

 

 

=====================================================================================

Full code:

Game.as

 

package {
            import flash.display.MovieClip;
            import flash.events.MouseEvent;
            import flash.utils.Timer;
            import flash.events.TimerEvent;
            import flash.events.KeyboardEvent;

                         
            public class Game extends MovieClip {
                                   
                        //common
                        public var timer:Timer = new Timer(28);
                        public var score:int;
                        public var rnd1:int;
                        public var i:int;
                        public var ii:int;
                                     
                         //assets
                        public var buttonstart:ButtonStart;
                        public var gametitle:Title;
                        public var hero:Hero;
                        public var scorebox:ScoreBox;
                        public var enemy1:Enemy1;
                        public var bullet:Bullet;
                        public var gameovermessage:GameOverMessage;
                                   
                         //variables
                         public var Left:Boolean;
                         public var Right:Boolean;
                         public var Space:Boolean;
                                     
                                     
                         //arrays
                        public var BulletArray:Array;
                        public var EnemyArray:Array;
                                   
                                               
            public function Game() {
                        timer.addEventListener(TimerEvent.TIMER,onTimer);
                        StartScreen();
                        }//game
                                   
                       
                                   
            public function StartScreen(){
                        gametitle=new Title;addChild(gametitle);
                        gametitle.x=400;gametitle.y=100;
                        buttonstart=new ButtonStart;addChild(buttonstart);
                        buttonstart.x=407;buttonstart.y=327;
                        buttonstart.addEventListener(MouseEvent.CLICK,buttonstartfunction);
                        function buttonstartfunction(eventObject:MouseEvent){
                        removeChild(buttonstart);removeChild(gametitle);
                        SetUp();
                        }//buttonstart
                        }//sscreen
                       
                                   
                       
                       
            public function SetUp(){
                        hero=new Hero;addChild(hero);
                        hero.x=400;hero.y=500;hero.dead=false;
                        scorebox=new ScoreBox;addChild(scorebox);
                        scorebox.x=25;scorebox.y=12;
                        score=0;
                        stage.addEventListener(KeyboardEvent.KEY_DOWN, KeyDownHandler);
                        stage.addEventListener(KeyboardEvent.KEY_UP, KeyUpHandler);
                        BulletArray= new Array;EnemyArray= new Array;
                        timer.start();
                        }//setup
                       
            public function KeyDownHandler(e:KeyboardEvent){
                        //trace(e.keyCode);
                        if(e.keyCode==39){Right=true;}
                        if(e.keyCode==37){Left=true;}
                        }//keydown
                                   
            public function KeyUpHandler(e:KeyboardEvent){
                        if(e.keyCode==39){Right=false;}
                        if(e.keyCode==37){Left=false;}
                        if(e.keyCode==32){Space=true;}
                        }//keyup
                       
                                   
                       
                       
                       
            ///////**********************************************//////////////////////
            public function onTimer(evt:TimerEvent):void{                     
                                               
                        IntroduceEnemies();
                        ProcessUserInput();
                        MoveObjects();
                        CollisionDetection();
                        RemoveDeadObjects();
                        UpdateDisplay();
                        CheckForGameOver();
                        }//GAMELOOP
            ///////**********************************************//////////////////////
                       
                                   
                                   
            public function IntroduceEnemies(){
                        rnd1=Math.random()*30;
                        if(rnd1==1){
                                    enemy1=new Enemy1;addChild(enemy1);
                                    enemy1.x=Math.random()*800;enemy1.y=-30;
                                    EnemyArray.push(enemy1);
                                    }//if
                        }//intro enemies
                                               
                                   
            public function ProcessUserInput(){
                        if(Right && hero.x<780){hero.x+=5;}
                        if(Left && hero.x>20){hero.x-=5;}
                        if(Space){Shoot();}
                        }//Process User Input
                                   
            public function Shoot(){
                        Space=false;
                        bullet= new Bullet;addChild(bullet);
                        bullet.x=hero.x;bullet.y=hero.y;
                        bullet.dead=false;
                        BulletArray.push(bullet);
                        }//shoot
                                   
                                   
            public function MoveObjects(){
                        for(i=0;i<EnemyArray.length;i++){EnemyArray[i].action();}
                        for each (var obj:Object in BulletArray){obj.y-=10;}
                        }//move objects
                                               
            public function CollisionDetection(){
                        for(i=0;i<EnemyArray.length;i++){
                                    if(EnemyArray[i].hitTestObject(hero.HitZone)){hero.dead=true;}
                        for(ii=0;ii<BulletArray.length;ii++){
                                    if(EnemyArray[i].hitTestObject(BulletArray[ii])){
                                    EnemyArray[i].dead=true;
                                    BulletArray[ii].dead=true;
                                    score+=5;}
                        }}//i, ii
                        };//CollisionDetection
                                   
            public function RemoveDeadObjects(){
                        for(i=0;i<BulletArray.length;i++){if(BulletArray[i].dead  || BulletArray[i].y<0){
                                    removeChild(BulletArray[i]);
                                    BulletArray[i]=null;
                                    BulletArray.splice(i,1);}}
                        for(i=0;i<EnemyArray.length;i++){if(EnemyArray[i].dead){
                                    removeChild(EnemyArray[i]);
                                    EnemyArray[i]=null;
                                    EnemyArray.splice(i,1);}}
                        };//remove dead objects
                                   
            public function UpdateDisplay(){
                        scorebox.scoretext.text="Score: "+score.toString();
                        };//update display
                                               
                                               
            public function CheckForGameOver(){
                        if(hero.dead){
                                    timer.stop();
                                    gameovermessage=new GameOverMessage;
                                    addChild(gameovermessage);
                                    gameovermessage.x=370;gameovermessage.y=200;
                        gameovermessage.addEventListener(MouseEvent.CLICK,gameoverfunction);
                        function gameoverfunction(eventObject:MouseEvent){
                                    RemoveAllObjects();
                                    StartScreen();
                                    }//go function
                        }//if hero is dead
                        };//gameover
                                   
            public function RemoveAllObjects(){
                        for(i=0;i<BulletArray.length;i++){
                                    removeChild(BulletArray[i]);
                                    BulletArray[i]=null;}
                        for(i=0;i<EnemyArray.length;i++){
                                    removeChild(EnemyArray[i]);
                                    EnemyArray[i]=null;}
                        BulletArray=[];EnemyArray=[];
                        removeChild(hero);
                                    removeChild(gameovermessage);
                                    removeChild(scorebox);                                              
                                    stage.removeEventListener(KeyboardEvent.KEY_DOWN, KeyDownHandler);
                        stage.removeEventListener(KeyboardEvent.KEY_UP, KeyUpHandler);
                        }//remove all objects
                                   
            }
}

 

Full code for Enemy1.as:

package {
           
import flash.display.MovieClip;
           
            public class Enemy1 extends MovieClip {
                        public var yspeed:int=5;public var xspeed:int=5;
public var mydirection:String;
public var rnd1:int;
public var dead:Boolean;
                       
            public function Enemy1() {
                        rnd1=Math.random()*2;
                        if(rnd1==0){mydirection="left";}
                        if(rnd1==1){mydirection="right";}
                        }

            public function action():void {
                                    //trace("action");
                                    this.y+=yspeed;
                                    if(mydirection=="right"){this.x+=xspeed;}
                                    if(mydirection=="left"){this.x-=xspeed;}
                                    if(this.x<5){mydirection="right";}
                                    if(this.x>755){mydirection="left";}
                                    if(this.y>610){dead=true;}
                                   
                        }//action
                       
            }
}