Tutorial: Create a Tower Defense Game in AS3 – Part 3
Categories: Flash
Table of Contents
Step 3: Adding Enemies
Welcome back. In this part of the tutorial, we are going to add enemies to the field and we’re going to program them to move through the paths.
Let us begin by first creating an Enemy class. Do this by creating a new external ActionScript file saved as “Enemy.as” and adding the following code to it:
package{
//imports
import flash.display.MovieClip;
import flash.display.Sprite;
import flash.events.*;
//defining the class
public class Enemy extends MovieClip{
private var _root:MovieClip;
public var xSpeed:int;//how fast it's going horizontally
public var ySpeed:int;//how fast it's going vertically
public var maxSpeed:int = 3;//how fast it can possibly go
public function Enemy(){
this.addEventListener(Event.ADDED, beginClass);
this.addEventListener(Event.ENTER_FRAME, eFrameEvents);
}
private function beginClass(e:Event):void{
_root = MovieClip(root);//defining the root
//checking what the start direction is
if(_root.startDir == 'UP'){//if it's starting up
this.y = 300;//set the y value off the field
this.x = _root.startCoord;//make the x value where it should be
this.xSpeed = 0;//make it not move horizontally
this.ySpeed = -maxSpeed;//make it move upwards
} else if(_root.startDir == 'RIGHT'){//and so on for other directions
this.x = -25;
this.y = _root.startCoord;
this.xSpeed = maxSpeed;
this.ySpeed = 0;
} else if(_root.startDir == 'DOWN'){
this.y = -25;
this.x = _root.startCoord;
this.xSpeed = 0;
this.ySpeed = maxSpeed;
} else if(_root.startDir == 'LEFT'){
this.x = 550;
this.y = _root.startCoord;
this.xSpeed = -maxSpeed;
this.ySpeed = 0;
}
//draw the actual enemy, it's just a red ball
this.graphics.beginFill(0xFF0000);
this.graphics.drawCircle(12.5,12.5,5);
this.graphics.endFill();
}
private function eFrameEvents(e:Event):void{
//move it based on x and y value
this.x += xSpeed;
this.y += ySpeed;
//checking what direction it goes when finishing the path
if(_root.finDir == 'UP'){//if it finishes at the top
if(this.y <= -25){//if the y value is too high
destroyThis();//then remove this guy from the field
}
} else if(_root.finDir == 'RIGHT'){//and so on for other directions
if(this.x >= 550){
destroyThis();
}
} else if(_root.finDir == 'DOWN'){
if(this.y >= 300){
destroyThis();
}
} else if(_root.startDir == 'LEFT'){
if(this.x <= 0){
destroyThis();
}
}
//remove this from stage when game is over
if(_root.gameOver){
destroyThis();
}
}
public function destroyThis():void{
//this function will make it easier to remove this from stage
this.removeEventListener(Event.ENTER_FRAME, eFrameEvents);
this.parent.removeChild(this);
}
}
}
Now that's a lot of code. The next thing we have to do is to add a function in the main "source.fla" that will add these guys to the field. But, let's first create a bunch of variables that'll help with the process. It'll be simple. Just define them variable where all others are defined:
var currentEnemy:int = 0;//the current enemy that we're creating from the array
var enemyTime:int = 0;//how many frames have elapsed since the last enemy was created
var enemyLimit:int = 12;//how many frames are allowed before another enemy is created
var enemyArray:Array = new Array();//this array will tell the function when to create an enemy
var enemiesLeft:int;//how many enemies are left on the field
enemyArray = [//defining the array
[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],//1's will just represent an enemy to be created
[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],//another row means another level
[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1]
];
Sweet. Now we can add an enterFrame function that'll create the enemies for us. Add this to the bottom of your code:
addEventListener(Event.ENTER_FRAME, eFrame);//adding an eFrame function
function eFrame(e:Event):void{
makeEnemies();//we'll just make some enemies
}
function makeEnemies():void{//this function will add enemies to the field
if(enemyTime < enemyLimit){//if it isn't time to make them yet
enemyTime ++;//then keep on waiting
} else {//otherwise
var theCode:int = enemyArray[currentLvl-1][currentEnemy];//get the code from the array
if(theCode == 1){//if it's set as 1
var newEnemy:Enemy = new Enemy();//then create a new enemy
enemyHolder.addChild(newEnemy);//and add it to the enemyholder
}
currentEnemy ++;//move on to the next enemy
enemyTime = 0;//and reset the time
}
}
Also, we have to create an enemyHolder. We should do this after the road is put down, so all the enemies will appear on top of it. Add this code right after we run the makeRoad() function:
var enemyHolder:Sprite = new Sprite();
addChild(enemyHolder);
Next, we have to count the amount of enemies that are going to be created. Add this code to the startGame() function:
for(var i:int=0;i
Now, there is one final thing to do in order to make these enemies work. Find the "DirectBlock.as" file for me, would you? Add this code to the beginClass() function:
if(directType == 'START'){
if(directType == 'START'){//if this is a start block
//then define the startDir and StartCoord based on it's coordinates
if(this.x == 0){
_root.startDir = 'RIGHT';
_root.startCoord = this.y;
} else if (this.y == 0){
_root.startDir = 'DOWN';
_root.startCoord = this.x;
} else if (this.x == 525){
_root.startDir = 'LEFT';
_root.startCoord = this.y;
} else if (this.y == 275){
_root.startDir = 'UP';
_root.startCoord = this.x;
} else {
//this level won't work if not any of these values
}
} else if (directType == 'FINISH'){//if this is a finish block
//then define the finDir based on it's coordinates
if(this.x == 0){
_root.finDir = 'LEFT';
} else if (this.y == 0){
_root.finDir = 'UP';
} else if (this.x == 525){
_root.finDir = 'RIGHT';
} else if (this.y == 275){
_root.finDir = 'DOWN';
} else {
//this level won't work if not any of these values
}
}
Next, add this code to the eFrame() function:
if(directType != 'START' && directType != 'FINISH'){//if this isn't a start of finish block
//then it'll act as a directioning block
for(var i:int = 0;i<_root.enemyHolder.numChildren;i++){//create a loop
var enTarget = _root.enemyHolder.getChildAt(i);//this will hold a certain enemy
//if the enTarget's coordinates are too close to this block
if(this.x >= enTarget.x - enTarget.width*.5 && this.x <= enTarget.x + enTarget.width*.5
&& this.y >= enTarget.y - enTarget.height*.5 && this.y <= enTarget.y + enTarget.height*.5){
//then move the enemy's direction based on what direction this block points to
if(directType == 'UP'){
enTarget.xSpeed = 0;
enTarget.ySpeed = -enTarget.maxSpeed;
} else if(directType == 'RIGHT'){
enTarget.xSpeed = enTarget.maxSpeed;
enTarget.ySpeed = 0;
} else if(directType == 'DOWN'){
enTarget.xSpeed = 0;
enTarget.ySpeed = enTarget.maxSpeed;
} else if(directType == 'LEFT'){
enTarget.xSpeed = enTarget.maxSpeed;
enTarget.ySpeed = 0;
}
}
}
}
Phew! That was a lot of code, wasn't it? Well, what these snippets of code do is simply direct the enemies to where they should start and where they should go.
That's all for this part of the tutorial. Next time, we'll make the turret detect and attack the enemies!
Final Product
I encountered an error which I do not know how to solve.
in the directblock.as file,
line 61 1031: The private attribute may be used only as class property definitions. private function eFrame(e:Event):void{
anyone able to help me out here?
October 26th, 2009 at 5:00 am
Ok, I know this is old, but
I have copied and pasted, and I dont have a green selection square and the corner blocks are not letting the enemies go over them.
Anyone still monitor this? Im liking the tutorial, it just needs a tiny bit more code checking.
April 22nd, 2010 at 10:32 pm
hi
May 17th, 2010 at 9:06 am
on the making a enemy section for the left movement their needs to be a – before the enTarget.maxSpeed; other wise it just goes right
June 18th, 2010 at 5:21 am