Tutorial: Create a Tower Defense Game in AS3 – Part 3


Written By MrSun at 8:03 am - Saturday, February 28th, 2009
Categories: Flash

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

Source Files (Zipped)

4 Comments

Desmond:

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?


TDNut:

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.


Samuel Peters:

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


«
»