Hey Flash developers! In this tutorial series we are going to go through the process of developing a very basic Tower Defense game. In this first part of the series, we'll learn how to deploy turrets on the game field, give them the ability to aim at an object (in this case, the mouse) and make them fire particles.
Final Result Preview
Once we complete this tutorial, we are going to have this:
Click a circle to mount a turret on it. Notice how all the turrets rotate so that they point towards the mouse cursor. Click, and all mounted turrets will fire a particle towards the cursor.
Step 1: What is a Tower Defense Game?
Wikipedia's definition sums it up nicely:
The goal of tower defense games is to try to stop enemies from crossing a map by building towers which shoot at them as they pass.
That is essentially what we will be developing in this tutorial series. Remember that we refer to the towers as turrets in this tutorial.
Cursed Treasure is a great example of a TD game, if you're still unsure.
Step 2: Setup - IDE
Before we start developing the game, we need to setup the project on our IDE. I'll be using FlashDevelop here. If you want to read about how to setup the project on Flash Develop, please have a read Steps 1 and 2 of this tutorial, or this full guide to FlashDevelop.
So now you should have a main class, Main.as
, with the following code:
package { import flash.display.Sprite; public class Main extends Sprite { public function Main():void { } } }
Step 3: Understand the Game Elements
For this part, our game will have following game elements:
- Game Field: The area where all game elements will be placed.
- Turret Placeholder: This is a place on the game field, defined to hold a turret.
- Turret: Our weapon in the game that can be placed on turret placeholders.
- Bullet: And finally, the particles that the turrets fire.
All the above elements will be created in the Main.as
except the turrets which will be a separate Turret
class.
Lets start coding now!
Step 4: Creating the Placeholders
First we'll create a function called createTurretPlaceholder()
which will create and return us a placeholder sprite. Add the following to the Main
class:
private function createTurretPlaceholder():Sprite { var placeholder:Sprite = new Sprite(); // draw the graphics var g:Graphics = placeholder.graphics; g.beginFill(0xCE7822); g.drawCircle(0, 0, 20); g.endFill(); return placeholder; }
This function is simply creating a Sprite
variable placeholder
. Then using Actionscript drawing API we create the graphic, which is a simple circle. Finally it returns that sprite.
Step 5: Adding Some Placeholders
Now we'll create three placeholders using the previous function and put them at different positions on the field. Add the following code in the Main()
constructor:
var placeholder1:Sprite = createTurretPlaceholder();
In the above statement we create a variable placeholder1
of type Sprite
which receives a placeholder from the above createTurretPlaceholder()
function.
placeholder1.x = 200; placeholder1.y = 60;
We position the placeholder on the field.
addChild(placeholder1);
And then we add the placeholder to the stage.
Using the same code, we'll add two more placeholders to the field - so your Main()
function should look like this:
public function Main() { var placeholder1:Sprite = createTurretPlaceholder(); placeholder1.x = 200; placeholder1.y = 60; var placeholder2:Sprite = createTurretPlaceholder(); placeholder2.x = 60; placeholder2.y = 260; var placeholder3:Sprite = createTurretPlaceholder(); placeholder3.x = 350; placeholder3.y = 260; addChild(placeholder1); addChild(placeholder2); addChild(placeholder3); }
Step 6: Turret - Creating the Class
As I mentioned before, the turret is going to be a separate class. This is because turrets need to have specific properties and methods of their own, and to be extended in future to create different type of turrets. This makes them a perfect candidate to be defined in a separate class.
Go on and create a new class called Turret
, derived from Sprite
, in a file named Turret.as
. It should have the following basic code:
package { import flash.display.Sprite; public class Turret extends Sprite { public function Turret() { } } }
Step 7: Turret - The Graphics
Now that we have base structure of the Turret
class, the next step is to give the turret some graphics. For that we create a new function called draw()
in the class. So put the following function just below the constructor:
private function draw():void { var g:Graphics = this.graphics; g.beginFill(0xD7D700); g.drawCircle(0, 0, 20); g.beginFill(0x800000); g.drawRect(0, -5, 25, 10); g.endFill(); }
As you might have noticed in the code, we draw a circle and a rectangle on it. That's how our turret is going to look. Now we call this function from the constructor itself.
public function Turret() { draw(); }
Step 8: Creating a Ghost Turret
The next thing we do is to display a ghost turret when we hover the placeholders that we have put on the game field. What is a ghost turret? Well, its just a transparent turret that appears when hovering the mouse on the placeholders, to tell the player that a turret can be deployed there.
To begin with this, we need a ghost turret. Go ahead and declare a variable for it in the Main
class.
private var ghost_turret:Turret;
Now create a new turret in the Main()
constructor:
ghost_turret = new Turret();
Give the ghost turret some properties so that it looks like a ghost:
ghost_turret.alpha = 0.5; ghost_turret.mouseEnabled = false; ghost_turret.visible = false;
In the above code we lower down the opacity of the turret to half (0.5) and set the mouseEnabled
property of the turret to false
so that the ghost turret does not receive any mouse events. Why? We will see that later. And as the ghost turret will be invisible by default, we hide it.
Finally, add the turret to the display list:
addChild(ghost_turret);
Your Main
constructor should look something like this:
public function Main() { var placeholder1:Sprite = createTurretPlaceholder(); placeholder1.x = 200; placeholder1.y = 60; var placeholder2:Sprite = createTurretPlaceholder(); placeholder2.x = 60; placeholder2.y = 260; var placeholder3:Sprite = createTurretPlaceholder(); placeholder3.x = 350; placeholder3.y = 260; addChild(placeholder1); addChild(placeholder2); addChild(placeholder3); ghost_turret = new Turret(); ghost_turret.alpha = 0.5; ghost_turret.mouseEnabled = false; ghost_turret.visible = false; addChild(ghost_turret); }
If you run the movie now (Ctrl+Enter), all you'll see is three placeholders on the stage. Boring huh? Lets add some interactivity.
Step 9: Showing/Hiding the Ghost Turret
We want the ghost turret to appear when the mouse hovers over any placeholder. So let's attach mouse listeners to each placeholder in the createTurretPlaceholder()
function just before it returns the placeholder
variable.
private function createTurretPlaceholder():Sprite { var placeholder:Sprite = new Sprite(); // draw the graphics var g:Graphics = placeholder.graphics; g.beginFill(0xCE7822); g.drawCircle(0, 0, 20); g.endFill(); placeholder.addEventListener(MouseEvent.MOUSE_OVER, showGhostTurret, false, 0, true); placeholder.addEventListener(MouseEvent.MOUSE_OUT, hideGhostTurret, false, 0, true); return placeholder; }
The code attaches listeners to MOUSE_OVER
and MOUSE_OUT
events.
Next we define the two handler functions for the same. Add the following two functions below the createTurretPlaceholder()
function:
private function showGhostTurret(e:MouseEvent = null):void { var target_placeholder:Sprite = e.currentTarget as Sprite; ghost_turret.x = target_placeholder.x; ghost_turret.y = target_placeholder.y; ghost_turret.visible = true; } private function hideGhostTurret(e:MouseEvent = null):void { ghost_turret.visible = false; }
hideGhostTurret()
just hides the ghost turret but whats going on in the showGhostTurret
function? Lets see.
var target_placeholder:Sprite = e.currentTarget as Sprite;
We get the reference of the placeholder on which mouse is present using the MouseEvent
's currentTarget
property, typecasted to Sprite
.
ghost_turret.x = target_placeholder.x; ghost_turret.y = target_placeholder.y; ghost_turret.visible = true;
This is simple...we position the ghost turret to the placeholder's cordinates and make it visible. Run the movie and you should see the ghost turret appear when hovering over the placeholders. Nice!
Step 10: Deploying Turrets
Our next objective is to deploy a turret when a placeholder is clicked. For that we need a CLICK
listener on the placeholder. But before that we need an Array
variable which will hold all our turrets, so we can reference them at any time later. Make one in the Main
class.
private var turrets:Array = [];
Then attach another listener just below the two previous listeners we added in the createTurretPlaceholder()
function:
placeholder.addEventListener(MouseEvent.MOUSE_OVER, showGhostTurret, false, 0, true); placeholder.addEventListener(MouseEvent.MOUSE_OUT, hideGhostTurret, false, 0, true); placeholder.addEventListener(MouseEvent.CLICK, addTurret, false, 0, true);
Declare the addTurret()
handler function below the hideGhostTurret()
function:
private function addTurret(e:MouseEvent):void { }
Now lets write the code for the function. Add the following code to the addTurret()
function.
var target_placeholder:Sprite = e.currentTarget as Sprite;
We first get the reference to the placeholder that was clicked just like we did in the showGhostTurret()
function.
var turret:Turret = new Turret();
The we create a new turret in a variable named turret
.
turret.x = target_placeholder.x; turret.y = target_placeholder.y;
Next, we position the turret
at the coordinates of the target_placeholder
.
addChild(turret); turrets.push(turret);
When the turret is created, we add it to the stage and push it onto the array.
Your addTurret
function should look like this in the end:
private function addTurret(e:MouseEvent):void { var target_placeholder:Sprite = e.currentTarget as Sprite; var turret:Turret = new Turret(); turret.x = target_placeholder.x; turret.y = target_placeholder.y; addChild(turret); turrets.push(turret); }
One thing to note here. Remember we set the mouseEnabled
property of the ghost turret to false
? If we hadn't, the ghost turret in between the placeholder and mouse would have captured the click event, thus preventing the event from reaching the placeholder. As a result the CLICK
listener attached to the placeholder would not have get called.
You might want to remove the CLICK
listener from the placeholder. I haven't done it here since the new turret blocks any clicks, but if you use a different turret design, it's a good idea.
Well thats all we need to place our turrets. Try running the movie and you should be able to deploy turrets on placeholders.
Step 11: Making the Turret Move
For this tutorial we will make the turret rotate to face the mouse. We'll keep the rotation functionality in a separate method of the Turret
class. This method will be called update()
.
Lets add this method to Turret
now:
public function update():void { var angle:Number = Math.atan2(stage.mouseY - this.y, stage.mouseX - this.x) / Math.PI * 180; this.rotation = angle; }
All we do in ths function is calculate the angle of the mouse from the turret and set the turret's angle to it. (Take a look at Trigonometry for Flash Developers if you're not sure how this works.)
But notice that we have not called this function from anywhere and so nothing happens yet. This function will be called from the game loop that we define next. Lets go!
Step 12: The Game Loop
Whats a game loop? Its a function that is attach to the ENTER_FRAME
event and so it gets called on every frame. It updates all the elements in the game...in our case, Turrets. More details in this article.
Create a function called gameLoop()
below the Main()
constructor which will be a listener to the ENTER_FRAME
event of the movie:
private function gameLoop(e:Event):void { }
Now that we have the listener function defined, we need to attach it to the corresponding event. We do so in the Main()
constructor. Add the following line to the Main()
function:
public function Main() { var placeholder1:Sprite = createTurretPlaceholder(); placeholder1.x = 200; placeholder1.y = 60; var placeholder2:Sprite = createTurretPlaceholder(); placeholder2.x = 60; placeholder2.y = 260; var placeholder3:Sprite = createTurretPlaceholder(); placeholder3.x = 350; placeholder3.y = 260; addChild(placeholder1); addChild(placeholder2); addChild(placeholder3); ghost_turret = new Turret(); ghost_turret.alpha = 0.5; ghost_turret.mouseEnabled = false; addChild(ghost_turret); stage.addEventListener(Event.ENTER_FRAME, gameLoop); }
Step 13: Updating All the Turrets
Lets put some code in our gameLoop()
function.
for each(var turret:Turret in turrets) { }
We iterate over the turrets
array, which has references to all turrets on the stage, using a for each...in
loop.
for each(var turret:Turret in turrets) { turret.update(); }
And call the update()
function of each turret. And we are done. If you run the movie now, you should be able to deploy turrets which always face the mouse. Something like this:
Step 14: Making the Turrets Shoot
Our next objective is to make the turrets shoot bullets. At whom? For this tutorial, we'll make the turrets shoot towards any point we click on the stage. We'll do this like so:
- Add a click listener to the stage.
- Iterate over all the turrets in the above listener.
- Calculate the angle from the clicked point to the turret.
- Create a new bullet and move it in the appropriate direction.
Lets declare the listener function named shoot
Add the function to Main
class:
private function shoot(e:MouseEvent):void { }
And then attach the above listener to the CLICK
event of stage in the Main()
constructor:
stage.addEventListener(MouseEvent.CLICK, shoot); stage.addEventListener(Event.ENTER_FRAME, gameLoop); }
Step 15: Creating the Bullet
Before we proceed to writing the code for shooting in the shoot()
function, we will define a new function for creating a bullet, just like we did for creating a placeholder. So put the following function below the createTurretPlaceholder()
:
private function createBullet():Sprite { var bullet:Sprite = new Sprite(); // draw the graphics var g:Graphics = bullet.graphics; g.beginFill(0xEEEEEE); g.drawCircle(0, 0, 5); g.endFill(); return bullet; }
Nothing much here. We just create a new Sprite
, draw an off-white color circle inside it, and return it. Now let's continue to define our shoot()
function.
Step 16: How to Shoot?
Time to add some code to the shoot()
.
for each(var turret:Turret in turrets) { }
First, we iterate over all the turrets on the stage using the for each...in
loop.
var new_bullet:Sprite = createBullet();
Now for every turret we create a bullet using the function we created previously.
new_bullet.rotation = turret.rotation;
Here we store the value of turret's rotation property in bullet's rotation. Why? Well...rotating the bullet is not what we want. The bullet needs to continue moving in direction determined by the turret, and for this we need the turret's rotation value from the time it shot the bullet. We just store this as the bullet's rotation for future use.
new_bullet.x = turret.x + Math.cos(new_bullet.rotation * Math.PI / 180) * 25; new_bullet.y = turret.y + Math.sin(new_bullet.rotation * Math.PI / 180) * 25;
These two lines set the bullet's initial position, which is 25 pixels away from the turret in the direction of facing (remember the rotation
property). Again, read up on trigonometry if this is unfamiliar to you.
addChild(new_bullet);
And as the usual last step, we add the bullet to the stage's display list.
This is how your shoot()
function should look like:
private function shoot(e:MouseEvent):void { for each(var turret:Turret in turrets) { var new_bullet:Sprite = createBullet(); new_bullet.rotation = turret.rotation; new_bullet.x = turret.x + Math.cos(new_bullet.rotation * Math.PI / 180) * 25; new_bullet.y = turret.y + Math.sin(new_bullet.rotation * Math.PI / 180) * 25; addChild(new_bullet); } }
If you run you game now and click on any placeholder, the turret will get get deployed - but oops... we have a problem here. An extra bullet also gets created with the turret. Lets fix this.
Step 17: Why Did That Extra Bullet Appear?
To understand this we need to understand event propagation in AS3.
Any event which gets generated passes through three phases: capturing, target and bubbling. The events starts from the topmost parent of the target which generated the event. It passes through all the inner child elements, which is the capturing phase. Then it reaches the actual target which is the target phase. Then the event goes back to the top, passing through same elements in reverse, which is the bubbling phase. In the bubbling phase, all the elements which have a listener defined for the propagating event get triggered and their listeners are executed.
We have a CLICK
listener bound to the stage and the placeholders. But the placeholders are also children of the stage. So when we click the placeholder, a CLICK
event gets generated with the target as the placeholder. The event propagates from the stage towards the placeholder - the capture phase. It reaches the placeholder and its CLICK
handler, the addTurret()
function, gets executed and we have a turret on the stage. Now the event propagates backwards - the bubbling phase - and when it reaches the stage again it finds a CLICK
listener for it as well, which gets executed. As a result the shoot()
function gets executed and a bullet is added to the stage.
So thats the problem, but how to solve it? What we need to do is stop the event's further propagation when it reaches the target. That means in the addTurret()
we stop the event's propagation. So go ahead and add a line at the end of addTurret()
:
private function addTurret(e:MouseEvent):void { var target_placeholder:Sprite = e.currentTarget as Sprite; var turret:Turret = new Turret(); turret.x = target_placeholder.x; turret.y = target_placeholder.y; addChild(turret); turrets.push(turret); e.stopPropagation(); }
The line we added stops the event's propagation and it doesn't reaches the stage. For a more comprehensive understanding of event framework in Actionscript 3.0 read the AS3 101 post. Let's continue with the game.
Step 18: Making the Bullet Move
We create the bullet on clicking the stage but it doesn't move still. Our next step is to add an event listener to the bullet which gets called on every frame and moves it. First, we declare a variable for the bullet's speed. Add the variable where the other variables are declared:
private var ghost_turret:Turret; private var turrets:Array = []; private var bullet_speed:uint = 3;
Then, add the following listener function to the Main
class:
private function moveBullet(e:Event):void { var bullet:Sprite = e.currentTarget as Sprite; bullet.x += Math.cos(bullet.rotation * Math.PI / 180) * bullet_speed; bullet.y += Math.sin(bullet.rotation * Math.PI / 180) * bullet_speed; }
What we do in this listener is:
- Get the reference of the bullet whose listener was triggered.
- Increment the bullet's position by
bullet_speed
amount in the direction of the bullet's rotation.
Lastly, attach the listener we just created to the ENTER_FRAME
event of the bullet in the shoot()
function:
private function shoot(e:MouseEvent):void { for each(var turret:Turret in turrets) { var new_bullet:Sprite = createBullet(); new_bullet.rotation = turret.rotation; new_bullet.x = turret.x + Math.cos(new_bullet.rotation * Math.PI / 180) * 25; new_bullet.y = turret.y + Math.sin(new_bullet.rotation * Math.PI / 180) * 25; new_bullet.addEventListener(Event.ENTER_FRAME, moveBullet, false, 0, true); addChild(new_bullet); } }
If you test you game now, you should see bullets moving when you click on the stage. But if you look carefully, you will notice that the bullets keep moving once created...even after they go out of the visible stage area. Our next step is to destroy the bullets as soon as they leave the movie boundaries.
Step 19: Destroying the Bullets
Add the following code in the end of moveBullet()
function:
private function moveBullet(e:Event):void { var bullet:Sprite = e.currentTarget as Sprite; bullet.x += Math.cos(bullet.rotation * Math.PI / 180) * bullet_speed; bullet.y += Math.sin(bullet.rotation * Math.PI / 180) * bullet_speed; if (bullet.x < 0 || bullet.x > stage.stageWidth || bullet.y < 0 || bullet.y > stage.stageHeight) { bullet.removeEventListener(Event.ENTER_FRAME, moveBullet); bullet.parent.removeChild(bullet); bullet = null; } }
Here we check whether the bullet is out of the stage boundaries. If it is, we remove its ENTER_FRAME
listener and remove the actual bullet from the stage. We also set the bullet
variable to null
so that the bullet has no reference left and is available for garbage collection.
Conclusion
We have completed our basic tower defense game in which you can deploy turrets on specific placeholders which shoot towards any point we click on the stage. Cool, huh?
In the next part we'll add enemies to the game and intelligence to the turrets so that they can do what they are suppose to do: defend. Also we'll add some more elements that make the game look more complete and cool. Till then, try adding some more features to this on your own.
Comments