Part 2: Enemies
Now that we have a Player that shoots Missile(s) and a ScoreBoard to track them, we need to add something on the screen to shoot at. To start, we will need to create a class that will define the properties and actions of the enemies in the game. We will call this class Enemy and put it in a file named Enemy.as. Object of the Enemy class will share many of the same attributes as the Missile class from Lesson 1.
- They will utilize EventDispatcher
- They will calculate movement in a Move() function
- They will display their movement in a Render() function
- They will have setLocation() and setSpeed() functions
However, the Enemy class must also perform some very unique functions that we have not created prior:
- Set a “face” for the enemy type (graphic)
- Track the number of “hits” an enemy can take, how many hits an enmy has taken, and display those hits in way that will be interesting and useful to the player.
- Set a unique “movement type” or “movement pattern” for the enemy.
- Allow for the Enemy to enter a “disappear” mode that will be used to elegantly remove it from the screen.
- Set a “fire rate” for the enemy that will determine how many “shots” it takes at the player.
Setting the “face” of the enemy is the easiest one of these tasks, but may also be the most important as it will be the part the player sees. In the library there is a movieclip name FEnemy that looks like this:

There are four different faces for enemies in the game. each on a different frame. This is a very easy way to create multiple different enemies in your game, but use single movieclip to hold them all. You could also make separate movieclips for each enemy if you so desired, but you would have to modify to code a bit to make it work.
The “Life” bar above the face will be used to display the “power” of an Enemy that takes more hits than one. To make use of this movieclip, we will first create a variable to hold information about the face of the enemy. We will also create a placeholder varable that represents the “life” movieclip in FEnemy. We need to do this so we can reference it in our code, without the Flash compiler throwing an error.
var face:Number = 1; var life:MovieClip;
Next we will create some static variables that will define the possible faces for the enemies. This will double as the “jump” frames to display the proper face for that enemy.
static var ENEMY_TI:Number = 1; static var ENEMY_APPLE:Number = 2; static var ENEMY_64:Number = 3; static var ENEMY_IBM:Number = 4;
Next we will create a function that let’s us set the face value, and also jump to the proper frame to display it using a gotoAndStop(). We will also create a getter function to retrieve the value of face.
public function setFace(val:Number) {
this.gotoAndStop(val);
face=val;
}
public function getFace():Number {
return face;
}
Next we want to track the number of hits an enemy has taken , and how many hits they can take. This will give us the flexibility to use a single Enemy class to create harder and harder targets for the Player to shooot. The first thing we need to do is create some variable to hold hits and maxhits. Along with those variables, we will be creating some variables to support “blink”. Blink is a way to show that an enemy that takes multiple hits has been shot, but not destroyed. We will use a the Boolean blink to tell us if the Enemy is currently blinking, and blinkframes to track how long the blink lasts.
var maxhits:Number = 0;
var hits:Number = 0;
var blink:Boolean=false;
var blinkframes:Number =0;
We also need to create getter and setter functions for hits and maxhits:
public function setMaxhits(val:Number) {
maxhits = val;
if (maxhits > 1) {
life._visible=true;
}
}
public function getMaxhits():Number {
return maxhits;
}
public function getHits():Number {
return hits;
}
public function setHits(val:Number) {
blink=true;
blinkframes=4;
this.filters=[new flash.filters.GlowFilter(0xFF0000 ,50,25,25,3,2,true,false)];
hits = val;
var lifeval:Number = 100/maxhits;
life.bar._xscale = Math.ceil((maxhits-hits)*lifeval);
}
The setMaxHits() function does two things. First, sets maxhits to the value passed in as val. Then, it sets the life bar visible if the enemy takes more than hit. Why do this? Well it’s a simple design decison. Why show the bar if it means nothing. The bar is only useful if the enemy take multiple hits.
The other interesting function here is setHits(). setHits() is called when the enemy has been hit by a missle shot by the player. We are going to perform a couple actions when the enemy is hit by a player’s missile to make the game play more like an arcade shooter. First, we will have the enemy “blink” with a red tint to show that it was hit. We do this by setting the blink Boolean to true, and setting the number of blinkframes for how long the blink action will last.
blink=true;
blinkframes=4;
We then add a GlowFilter to this instance of Enemy that will turn the Enemy red until the amount of frames specified in blinkframes has passed.
this.filters=[new flash.filters.GlowFilter(0xFF0000 ,50,25,25,3,2,true,false)];
Then, we will re-size the life-bar to show how much more life the enemy has left. We will do both of these things to add visual feed-back to the player. We will also show an explosion if the enemy is destroyed, but that action will be performed by the HomeWars class.
life.bar._xscale = Math.ceil((maxhits-hits)*lifeval);
Next we will set the firerate (the amount of shots the Enemy will fire at the Player), the speed at which the Enemy will travel on the screen, and the movementType (vertical, diagonal left, diagonal right) of the Enemy. All of these will be essential when we create the levels and balancing in the next lesson. For this lesson they will all be random.
public function setSpeed(val:Number) {
speed = val;
}
public function getSpeed():Number {
return speed;
}
public function setMovementType(val:Number) {
movementType = val;
switch(movementType) {
case MOVEMENT_TYPE_VERTICAL:
yunits = speed;
xunits = 0;
break;
case MOVEMENT_TYPE_DIAGONAL_RIGHT:
xunits = speed/2;
yunits = speed/2;
break;
case MOVEMENT_TYPE_DIAGONAL_LEFT:
xunits = -(speed/2);
yunits = speed/2;
break;
}
}
public function getMovementType():Number {
return movementType;
}
The last thing we need to do is create our Move() and Render() functions for the Enemy.
public function Move() {
nextx = _x+=xunits;
nexty = _y+=yunits;
switch(movementType) {
case MOVEMENT_TYPE_VERTICAL:
break;
case MOVEMENT_TYPE_DIAGONAL_RIGHT:
if (nextx > Stage.width) {
nextx = 0-(this._width/2);;
}
break;
case MOVEMENT_TYPE_DIAGONAL_LEFT:
if (nextx < 0) {
nextx = Stage.width+(this._width/2);
}
break; break;
}
}
The Move() function is very similar to the one we created for the Missile class from Lesson 1. The only difference is that we need to handle the diagonal movement of the enemies. The switch() statement above checks the MOVEMENT_TYPE, and then checks to see if the Enemy has moved off the left or right side of the sceen, then we replace it on the opposite side of the screen and it continues to move.
public function Render() {
if (!disappear) {
_x = nextx;
_y = nexty;
if (_y > Stage.height) {
this.dispatchEvent({type:"EventEnemyOffScreen", enemy:this});
} else {
if ( Math.floor(Math.random() * 100) < firerate ) {
this.dispatchEvent({type:"EventEnemyFireMissile", startx:this._x, starty:this._y});
}
}
if (blink) {
blinkframes--;
if (blinkframes <=0) {
blink=false;
this.filters=[null];
}
}
} else {
this._alpha -= 10;
if (this._alpha <= 0) {
this.dispatchEvent({type:"EventEnemyOffScreen", enemy:this});
}
}
}
function setDisappear(b:Boolean) {
disappear = b;
}
The Render() function works somewhat like the Render() function we created for Missile in Lesson 1, but with several very significant differences. The first difference is that no movement happens in Render() if the disappear boolean is set to true. Instead, we will fade the enemy from the screen and dispatch the EventEnemyOffScreen event to clean it up. setDisappear() is called from the HomeWars class when the Enemy has been destroyed.
If the enemy is not currently in disappear mode, then we set the x and y postion on the screen. We check to see if the enemy is off the screen. If so, we dispatch the same EventEnemyOffScreen event that we would if the Enemy had disappeared. We could have used teo seperate events for this, but since they are essentially the same thing, we don't have to create more than one.
Render() is also where we calculate if the Enemy should fire a bullet at the Player. We use a very simple calculation for this. We simply get a random number and check it against the firerate variable. If the random number is less than firerate (the higher the firerate, the higher the chance of a bullet being fired), we dispatch the EventEnemyFireMissile with the added parameters of startx and starty so the HomeWars class will know to place the EnemyMissile on the screen near the Enemy that fired it, making it appear to have been fired by that Enemy.
Finally, we test the blink. Recall that blink is true if the Enemy has been hit, but is not destroyed and is currently counting down blinkframes. We decrement blinkframes and then test to see if there are any blinkframes left. If not, we clrear the red GlowFilter by setting this.filters=[null];
Here is the final code for the Enemy.as class:
import mx.events.EventDispatcher;
class Enemy extends MovieClip {
var maxhits:Number = 0;
var hits:Number = 0;
var firerate:Number = 0;
var speed:Number = 0;
var nextx:Number = 0;
var nexty:Number = 0;
var xunits:Number = 0;
var yunits:Number = 0;
var movementType:Number = 1;
var blink:Boolean=false;
var blinkframes:Number =0;
var face:Number = 1;
var disappear:Boolean = false;
var life:MovieClip;
static var MOVEMENT_TYPE_VERTICAL:Number = 1;
static var MOVEMENT_TYPE_DIAGONAL_RIGHT:Number = 2;
static var MOVEMENT_TYPE_DIAGONAL_LEFT:Number = 3;
static var ENEMY_TI:Number = 1;
static var ENEMY_APPLE:Number = 2;
static var ENEMY_64:Number = 3;
static var ENEMY_IBM:Number = 4;
public function Enemy() {
EventDispatcher.initialize(this);
maxhits = 1;
hits = 0;
firerate = 1;
speed = 5;
movementType = 1;
blink=false;
blinkframes=0;
var face=1;
life._visible=false;
}
function setDisappear(b:Boolean) {
disappear = b;
}
public function setFace(val:Number) {
this.gotoAndStop(val);
face=val;
}
public function getFace():Number {
return face;
}
public function setLocation(xp:Number, yp:Number) {
_x = xp;
_y = yp;
}
public function setMaxhits(val:Number) {
maxhits = val;
if (maxhits > 1) {
life._visible=true;
}
}
public function getMaxhits():Number {
return maxhits;
}
public function getHits():Number {
return hits;
}
public function setHits(val:Number) {
blink=true;
blinkframes=4;
this.filters=[new flash.filters.GlowFilter(0xFF0000 ,50,25,25,3,2,true,false)];
hits = val;
var lifeval:Number = 100/maxhits;
life.bar._xscale = Math.ceil((maxhits-hits)*lifeval);
}
public function setFirerate(val:Number) {
firerate = val;
}
public function getFirerate():Number {
return firerate;
}
public function setSpeed(val:Number) {
speed = val;
}
public function getSpeed():Number {
return speed;
}
public function setMovementType(val:Number) {
movementType = val;
switch(movementType) {
case MOVEMENT_TYPE_VERTICAL:
yunits = speed;
xunits = 0;
break;
case MOVEMENT_TYPE_DIAGONAL_RIGHT:
xunits = speed/2;
yunits = speed/2;
break;
case MOVEMENT_TYPE_DIAGONAL_LEFT:
xunits = -(speed/2);
yunits = speed/2;
break;
}
}
public function getMovementType():Number {
return movementType;
}
public function Move() {
nextx = _x+=xunits;
nexty = _y+=yunits;
switch(movementType) {
case MOVEMENT_TYPE_VERTICAL:
break;
case MOVEMENT_TYPE_DIAGONAL_RIGHT:
if (nextx > Stage.width) {
nextx = 0-(this._width/2);;
}
break;
case MOVEMENT_TYPE_DIAGONAL_LEFT:
if (nextx < 0) {
nextx = Stage.width+(this._width/2);
}
break;
}
}
public function Render() {
if (!disappear) {
_x = nextx;
_y = nexty;
if (_y > Stage.height) {
this.dispatchEvent({type:"EventEnemyOffScreen", enemy:this});
} else {
if ( Math.floor(Math.random() * 100) < firerate ) {
this.dispatchEvent({type:"EventEnemyFireMissile", startx:this._x, starty:this._y});
}
}
if (blink) {
blinkframes--;
if (blinkframes <=0) {
blink=false;
this.filters=[null];
}
}
} else {
this._alpha -= 10;
if (this._alpha <= 0) {
this.dispatchEvent({type:"EventEnemyOffScreen", enemy:this});
}
}
}
public function addEventListener(){/*Interface Stub*/}
public function removeEventListener(){/*Interface Stub*/}
public function dispatchEvent(){/*Interface Stub*/}
}
Read the rest of the series: ‘Anatomy of a Flash Game’
- Anatomy of a Flash Game: Lesson 1 – Setting up the game
- Anatomy of a Flash Game: Lesson 2 – Creating Enemies & The Game Environment
- Anatomy of a Flash Game: Lesson 3 – MochiAds, MochiBot and MochiAds Leaderboards

very good