Tutorial: Create a Tower Defense Game in AS3 – Part 3
Categories: Advanced Tutorials, All Tutorials, AS3, Flash, Game Development
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<enemyArray[currentLvl-1].length;i++){ if(enemyArray[currentLvl-1][i] == 1){ enemiesLeft ++; } }
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!
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