Flash AS2 OOP game development
[ January 18, 2004 ] by Steve Happ
This long article documents the development process (from requirements definition to release) of a Flash game written in ActionScript2 using entirely OOP (Object Oriented Programming). The game is a standard space shoot'em-up with 3 enemy ships and the player firing lasers.


INTRODUCTION

This is the documentation of my first AS2 game written entirely in OOP(Object Oriented Programming).
The game will be a standard space shoot-em-up with 3 enemy ships and the player firing lasers. There will be only 1 level at first but the design will leave the possiblility of adding levels easily. There will be a basic Score and Number of lives.
This documentation is for the benefit it my own learning as well as others who are struggling to implement the new Actionscript 2.0 paradigm in Flash MX2004.
Much thanks and respect for Squize from www.1coin1play.com for letting me use his spriteSheet for this example.

I will follow a reasonably traditional OOP design structure:

  • Requirements Doc
  • Use cases
  • Class Diagrams
  • Interaction Diagrams
  • Test First Coding
  • Iterations
  • Testing
  • Release

REQUIREMENTS

Spaceship:

  • Moves by Left, Right, Up, Down Keys
  • Fires lasers by Space Bar

Enemy:

  • Moves right to left at random height
  • Has 3 instances
  • When it goes offscreen at left, comes back on at right

BackGround:

  • Moves from left to right
  • foreground moves faster than background (parallax)

Laser:

  • Fires from spaceship when space bar pressed

Game:

  • If laser hits enemy, player gain points
  • If spaceship collides with Enemies, lose a life
  • If player gains winningAmount, then s/he wins, game restarts
  • If more than 3 lives lost, game over, player loses

USE CASES

Use cases are a diagrammatic representation of the requirements. They show all the different things that will happen in the game, or all the different actions that will take place (see picture 1).

Pic. 1: Use cases

This is all you need to do. Just nut it out roughly on a piece of paper. This diagram could well give a clue to the classes and methods that will finally be used.
After you do this, it is time to work out what classes you will need and what attributes and methods those classes may need.


CLASS DIAGRAMS

Class Diagrams show the relationships between classes. In our diagram below (see picture 2) the Spaceship, Enemy, Laser and Background classes are all contained within the Game class. That means that instances of these classes are declared inside the Game class. This is called aggregation or "has a" relationship as opposed to inheritance which is described as a "is a" relationship. The Game class is aggregated in our Flash file.

Pic. 2: Class Diagram


CLASS DESCRIPTIONS

Spaceship:

Attribute Type Description
speed Number value of x increment
moveShip() method move the ship with keys
setShipX(x) setter accessor method for _x
getShipX() getter return _x
setShipY(y) setter accessor method for _y
getShipY() getter return _y

Enemy:

Attribute Type Description
enSpeed Number value of x increment
moveEn() method move the Enemy randomly
setEnX(x) setter accessor method for _x
getEnX() getter return _x
setEnY(y) setter accessor method for _y
getEnY() getter return _y

Laser:

Attribute Type Description
fire() method start move when space bar pressed
setLaserX(x) setter accessor method for _x
getLaserX() getter return _x
setLaserY(y) setter accessor method for _y
getLaserY() getter return _y

Background:

Attribute Type Description
scroll() method move R to L
setBackX(x) setter accessor method for _x
getBackX() getter return _x
setBackY(y) setter accessor method for _y
getBackY() getter return _y

Game:

Attribute Type Description
score Number player points
lives Number player lives
checkCollisions() method Collision Detection
CheckLives(lives) method if lives = 0, stop game
checkScore() method check and display score


Interaction Diagrams

Interaction diagrams map out the messages passed via methods to accomplish the requirements that we started with. I have no idea at the moment if this plan will work, because I dont know if Flash will deal with composition. The Game class contains the other classes and is a manager of the game. But I am not sure if I can control a class that extends from a MovieClip inside a class. Well here is the plan, let's see if it works or not. Plans are not set in concrete. You are allowed to throw them away if they dont work, or revise, or even start again from scratch. The most important thing is the process. Going through the process crystallises the problems and shows us the way to the solutions. It's like a storyboard for a film. You draw it up to get the feel of the script and the shooting needs. Once you have worked it all out you throw it away and direct from instinct. You know the story backwards because you went throught the storyboard process. Same here.

Here is the first Interaction diagram i drew up (see picture 3). I have even made some changes and realised a few things I had left out of the Class Diagrams. Like that moveship needs a direction (Left, Right, Up, Down) and I could pass a variable "dir" based on what key was pressed. "dir" can be a string ("left") or a number (1,2,3,4). Doesn't matter.

Pic. 3: Interaction diagram


TEST FIRST

I created a new Flash file and made a spaceship_mc, linked it to ship_mc and put "Spaceship" in the class field. Then in the Actions panel i wrote this code:

// attach spaceship_mc 
attachMovie("ship_mc", "myShip",  getNextHighestDepth());
// declare variables 
var enArray:Array;		// empty at the moment
var bulletArray:Array;	// empty 
var myGame = new Game(myShip , enArray , bulletArray);

//  game loop
_root.onEnterFrame = function(){
	myGame.moveShip("right");
}

Then I wrote minimal code so that the classes would just compile.

class Game{
	// Game class - manages the gameplay and 
	// contains the other classes
	// declare variables
	var ship:Spaceship;
	var enemyArray:Array;
	var laserArray:Array;
	
	// constructor
	function Game(_ship:Spaceship, _enArray:Array, _bullArray:Array){
	ship       = _ship;
	enemyArray = _enArray;
	laserArray = _bullArray;
	}
}

Then in the Spaceship.as file I wrote:

class Spaceship extends MovieClip{

}

Then compile, but no errors. But there should be because the moveShip method doesnt yet exist. ?? HuH ? Nothing comes up in the debugger. Pretty crappy error checking... Oh well...
Now i have to write the Game.moveShip method... I will write that and test it.
I wrote the moveship method but realised that I would have to write another method "checkKey" that will check which key is pressed and return dir as a String ("left", etc).

// === in Game class
// check key press method
	function checkKey():String{
		if(Key.isDown(Key.RIGHT)){
		dir = "right";
		}
		if(Key.isDown(Key.LEFT)){
		dir = "left";
		}
		if(Key.isDown(Key.UP)){
		dir = "up";
		}
		if(Key.isDown(Key.DOWN)){
		dir = "down";
		}
		return dir;
		}
		
	// moveship method
	// 1. check for keyPresses
	// 2. assign variable dir as per 1
	// 3 pass dir to spaceship.move
	function moveShip(dir:String){
	ship.move(dir);
	}

Compiled that and got the error: "Line 38: There is no method with the name 'move'."
Perfect, starting to get errors; that is exactly what is supposed to happen. Now to write the "ship.move" method to get rid of the error.
This is the idea of "test first" - to implement the methods in the test suite until they fail and then code the methods one at a time until it meets the requirements and then move on to the next method.
I got the ship to move: here is how I did it...

class Game{
	// declare variables
	var ship:Spaceship;
	var enemyArray:Array;
	var laserArray:Array;
	var dir:String;
	
	// ====   constructor  ============== 
	function Game(_ship:Spaceship, _enArray:Array, 
	_bullArray:Array){
	ship       = _ship;
	enemyArray = _enArray;
	laserArray = _bullArray;
	}
	
	// ====  check key press method =======
	function checkKey():String{
	if(Key.isDown(Key.RIGHT)){
	dir = "right";
	}
	else if(Key.isDown(Key.LEFT)){
	dir = "left";
	}
	else if(Key.isDown(Key.UP)){
	dir = "up";
	}
	else if(Key.isDown(Key.DOWN)){
	dir = "down";
	}
	else dir="stop";
	//this works 
	//trace(dir + " - in game.checkey"); 

	return dir;
	}
	
	// ===  moveship method	=====
	// 1. check for keyPresses
	// 2. assign variable dir as per 1
	// 3 pass dir to spaceship.move
	function moveShip(dir:String){
	ship.move(checkKey());

	}
}	// end class game

And the Spaceship class:

class Spaceship extends MovieClip{
	var dir:String;
	function move(_dir:String){
	dir = _dir;
	if(dir=="left") _x -= 20;
	if(dir=="right")_x += 20;
	if(dir=="up")   _y -= 20;
	if(dir=="down") _y += 20;
	if(dir=="stop"){
		_x +=0;
		_y +=0;
		}
	}
}	// end class Spaceship

I had trouble passing dir to the ship.move method, then I tried passing checkKey direct because it returns dir anyway, so it worked. Then i had to put a stop on it. Whenever any of the keys are up. The motion of the ship is a bit jerky but it works so far and I can smooth out the movement later.
But I know that my design works. I just had to pass the spaceship instance to the game constructor... and hey presto...
What next? hmmm, maybe fire the laser... ok off I go...


FIRING THE LASERS

This took me 2 days. I can't believe it. I kept getting the wrong bullet firing... Here is my first attempt at the fireLaser Method:

// in game class
// 	======	fire laser	========	
	function fireLaser_old(){
	if(Key.isDown(Key.SPACE)){
	   	// set start pt of laser 
		laserArray[bulletNum]._y = ship.getY();
		laserArray[bulletNum]._x = ship.getX(); 
		// if space bar pressed set flag
		hasFired = true;
		bulletNum++;
		if(bulletNum >=5){
			bulletNum = 0;
			}
		}
	}

That was pretty close to what I ended up with. The real trouble was with the "movelaser" method. Oh and yeah, Ii had the "fireLaser" in the enterFrame loop in my fla... Big mistake: it has to go in a onKeyDown event in the .fla. Here it is so far:

// code in .fla - actions panel
attachMovie("ship_mc", "myShip",  getNextHighestDepth());
var enArray     = new Array(3);
var bulletArray = new Array(5);
// attach bullet array
for(var i =0;i < 5;i++){
	attachMovie("laser_mc", "laser"+i, 100 + i);
	bulletArray[i] = _root["laser"+i];
}
// declare and initialise Game class instance
// and pass it ship, enemyArray and bulletArray
var myGame = new Game(myShip , enArray , bulletArray);

// 	=======	Key capture	============	//
someListener = new Object();
someListener.onKeyDown = function () {
	myGame.fireLaser();
	
	};
Key.addListener(someListener);

//  game loop
_root.onEnterFrame = function(){
	myGame.checkKey();
	myGame.moveShip(dir);
	myGame.moveLaser();
}

That was a bit better but still no proper firing of the right bullet. It was firing about 2 later than when the space bar was pressed. Oh yeah, btw, here is the moveLaser method I was using at this time:

//	========= move laser  ==========//
	function moveLaser_old(){
		if(hasFired==true){
		laserArray[bulletNum]._x += 30;
		}
		if(laserArray[bulletNum]._x > Stage.width)
		{
			laserArray[bulletNum]._x = -10;
			hasFired = false;
		}
	}
//	==============	//

Here is the final (for now) of the "fireLaser" method. It is very simple. All it does is get the position of the ship and assign that position to the laser. Then the laser Number is incremented and if that number is greater than 5 it is reinitialised to 0:

//	=========	FIRE LASER	======	//
	function fireLaser(){
		if(Key.isDown(Key.SPACE)){
			// set start point 
			laserArray[bulletNum]._y = ship.getY();
			laserArray[bulletNum]._x = ship.getX(); 
			// increment the bullet number
			++bulletNum;
			// if more than 5 bullets , start again at 0
			if (bulletNum>5) {
				bulletNum = 0;
				}
		}
	}

And here is the final "moveLaser" method. Again very simplified. It goes through the array and if there is a bullet available it moves it. It is still not perfect but it will do for the moment.

//	=======	MOVE LASER	=======	//
	function moveLaser(){
		var bulleti = 0;
		while (bulleti < 6) {
		laserArray[bulleti]._x += 30;
		bulleti++;
		}
	}
//	=============	//

Here is the whole Game class as it stands up to now:

class Game{
	// 	=========	declare variables
	var ship:Spaceship;
	var enemyArray:Array;
	var laserArray:Array;
	var dir:String;
	var bulletNum:Number = 0;
	
	// ====   constructor  ============== 
	function Game(_ship:Spaceship, _enArray:Array, 
		_bullArray:Array){
		ship       = _ship;
		enemyArray = _enArray;
		laserArray = _bullArray;
	}
	
	// ====  check key press method =======
	function checkKey():String{
	if(Key.isDown(Key.RIGHT)){
	dir = "right";
	}
	else if(Key.isDown(Key.LEFT)){
	dir = "left";
	}
	else if(Key.isDown(Key.UP)){
	dir = "up";
	}
	else if(Key.isDown(Key.DOWN)){
	dir = "down";
	}
	else dir="stop";
	
	return dir;
	}
	
	// ===  moveship method	=====
	// 1. check for keyPresses
	// 2. assign variable dir as per 1
	// 3 pass dir to spaceship.move
	function moveShip(dir:String){
	ship.move(checkKey());

	}
	
	//	=========	FIRE LASER	======	//
	function fireLaser(){
		if(Key.isDown(Key.SPACE)){
			// set start pt 
			laserArray[bulletNum]._y = ship.getY();
			laserArray[bulletNum]._x = ship.getX(); 
			// increment the bullet number
			++bulletNum;
			// if more than 5 bullets , start again at 0
			if (bulletNum>5) {
				bulletNum = 0;
				}
		}
	}
	
	//	=======	MOVE LASER	=======	//
	function moveLaser(){
		var bulleti = 0;
		while (bulleti < 6) {
		laserArray[bulleti]._x += 30;
		bulleti++;
		}
	}	//	=============	//
	
		
}	// 	------	END CLASS	-------	//

And the Spaceship class with getX and getY methods:

class Spaceship extends MovieClip{
	var dir:String;
	// ===== move =========
	function move(_dir:String){
	dir = _dir;
	if(dir=="left") _x -= 20;
	if(dir=="right")_x += 20;
	if(dir=="up")   _y -= 20;
	if(dir=="down") _y += 20;
	if(dir=="stop"){
		_x +=0;
		_y +=0;
		}
	}
	//	======	getX	====
	function getX():Number{
	return _x;
	}
	// 	=======	getY	=====
	function getY():Number{
	return _y;
	} 
}	// 	=====	end Spaceship class	

And the Laser class:

class Laser extends MovieClip{
	//	=======	CONSTRUCTOR 	=========	//
	function Laser(x:Number, y:Number){
	_x = x;
	_y = y;
	}
}

Download the files as they are up to here. You may have to make your own sprites.
Next, the baddies!

(continues on page 2)


        
 
 
Name: Steve Happ
Location: Newcastle, Australia
Age: 51
Flash experience: 2-3 years
Job: Computer trainer
Website: http://www.video-animation.com/
 
 
| Homepage | News | Games | Articles | Multiplayer Central | Reviews | Spotlight | Forums | Info | Links | Contact us | Advertise | Credits |

| www.smartfoxserver.com | www.gotoandplay.biz | www.openspace-engine.com |

gotoAndPlay() v 3.0.0 -- (c)2003-2008 gotoAndPlay() Team -- P.IVA 03121770048