Page 1 of 1

Making flash games for the non-flash developer Part III Continues to discusses how to start developing games using Flex 3 and

#1 coden4fun  Icon User is offline

  • if(coden4fun.NotTired){ ContinueToCode(); }
  • member icon

Reputation: 27
  • View blog
  • Posts: 696
  • Joined: 27-February 09

Posted 14 March 2009 - 02:57 AM

Preface and a Follow up
This is part 3 of an 8 part series of writing a finished flash game using Flex and ActionScript 3. Here I will discuss precisely what was mentioned in step 3 for our Current Goals in completing this project, and in great detail discuss how I got there. Upon completing this tutorial you will have learned how to create an entity management class, an abstract entity class (which will be the abstract class all enemies, weapons, the player will extend from), create the abstract class that will be the base class all game entities will extend from, create the player and make the player move by pressing the left and right keys on the keyboard. That's a lot, so let's quickly share some knowledge of some resources I used to make this part of the tutorial.

First, I want to thank Mathew Casperson, and his tutorial, which can be found here. His tutorial shows how to create a double buffer, and a graphics resource, which grabs files throws them in a class, and then are converted to a BitmapData, which then can be drawn onto the screen using what is called a double buffer.

Woo :blink: That's a lot, and importantly not my work, but I did find his tutorial quite helpful, and even using his graphic resource class, which I give him credit in my work as well, and instead of going through how the graphics resource class works and having redundant tutorials on the net I admonish all to go to that page for further knowledge if you so seek it, and especially give him credit. His tutorial saved me hours!

Now let's get back to discussing what this tutorials is about, and dive right into it.

First The Design, so we can get an idea of where we're going
The following is an image of how I see this game to be designed pertaining to how the classes are designed, and interact with each other. It's not an exact model, for I'm sure it will change, but the overall idea of how things will go are well labeled above. As you can see above everything is either being extended from the AbstractEntity, which means uses it's methods and has the base class of that specific class/object AbstractEntity. This means that you can make different game entities from using AbstractEntity, for all game entities share similar methods (functions as they're called in AS 3), which means they all share a similar pattern rather it is being created, being drawn on the screen, and quick update, or throwing a way the entity. Now since our main theme is going to be around this AbstractEntity, for it's the heart of this project as every class we make has to either extend from this, or manage it let's start there as we discuss in detail what this diagram is really saying, and how important it is as a coder to have something visual like this in front of us as we're coding.
Posted Image

We're basically creating the AbstractEntity class to be the base class of all game entities in the game, and this makes sense, for a couple of reasons.

1.) Make our code organized, readable, and easier to update in the future if needed.
2.) Saves us time! Why do I want to draw all entities if I can create a base class and run a loop?

What the diagram is saying above is all entities will need to be initialized, drawn on the screen, update, and shutdown once it comes time for them to do so. The player is extending the abstract entity class just like all the enemies, which is doing so by extending the AbstractEntity class, and all the bosses, who are extending the AbstractBoss class, which is then extending the AbstractEntity class, which is then extending the AbstractEntity class. Why all this extending you say?

Well, I have found it very important to organize all game entities that will be on the screen, and then label, and take note to what all the entities will be doing in the game. If I see a common pattern with a good amount of certain game entities then I will make a base class for them that will cover all similarities those entities have in common, and that is why I have both AbstractEnemy, and AbstractBoss. Even though we're definitely not there, and won't be until the next couple of tutorials it's important now to get a vague if not a good specific design of how these will interact now as we're making the base class for all entities, so we can quickly create them when the time is needed. That is why I have a base class for all entities (since all need to be drawn and updated), and then all enemies, and bosses (since they all share similarities, and specific similarities with each other).

Now to the AbstractEntity class.


The AbstractEntity Class

This class as you've realized will take in consideration all entities in the game that will be drawn in game. This means from the player, to enemies, so as you can guess it serves a very important role, but also because it must be true to all game entities in the game it's vague, which means it's very simple in code, and there's hardly any code.

/*
*	Class:			Abstract Entity
*	Date:			03/13/09
*
*	Description:	This serves as the abstract entity class that all game entities will extend.
*					gives the entity position, graphic, a boolean to see if it's in use.
*					Then will draw all entities onto the double buffer "back screen"
*/

package
{
	import flash.display.BitmapData;
	import flash.events.KeyboardEvent;
	import flash.geom.Point;
	
	public class AbstractEntity
	{
		public var position:Point;
		public var Order:int = 0;
		public var graphics:GraphicsResource = null;
		public var inUse:Boolean = false;
		
		public function AbstractEntity()
		{
			
		}
		
		public function init(graphics:GraphicsResource, position:Point, order:int = 0):void
		{
			if(!inUse)
			{
				this.graphics = graphics;
				this.position = position.clone();
				this.Order = order;
				this.inUse = true;
				EntityManager.Instance.addEntity(this);
			}
		}
		
		//get rid of the entity
		public function shutdown():void
		{
			if(inUse)
			{
				graphics = null;
				inUse = false;
				EntityManager.Instance.removeEntity(this);
			}
		}
		
		//draw the entity to the screen
		public function drawEntity(doubleBuffer:BitmapData):void
		{
			doubleBuffer.copyPixels(graphics.bitmap, graphics.bitmap.rect, position, graphics.bitmapAlpha, new Point(0,0), true);
		}
		
		//update entity
		public function update(seconds:Number):void
		{
			
		}
	}
}



This code is very easy to understand as it deals with very little variables, which are the following;

Position - holds the x, y points of that specific entity to be drawn onto the screen at that specific point on the screen.
Order - defines the order which that specific entity will have pertaining to the order we draw it compared to other entities on the screen.
graphics - the graphics resource that holds the actual bitmap graphic of that entity again Thank Mathew for his work.
inUse - to see if the graphic will be on the screen, if it's in use at all

Then we have the functions that are called that just manages these variables.

init This initializes the entity, and set's it's values acts as a constructor, and then it adds that specific entity to an array collection, which is in the EntityManager class. that's all it does. So as you create a player and you want to initialize it. You'd call super and then init and provide it with the specific variables of positin, graphic, and order and it is auto added into the array collection to be used. Pretty neat!

shutdown - This kills the entity by deallocating in memory that entity, by referencing it's main variables to null, which means delete it. then it adds that specific entity to the removeEntity array collection in the EntityManager, which goes through as a loop and checks to see if it's there, and if so delete it for good. That's really easy isn't it?

drawEntity - This passes a parameter of the double buffer, which we'll see later in the EntityManager, and draws the entity onto that double buffer. A double buffer is simply an image that other images gets drawn onto and then that image is drawn on the screen. Think of it like a back screen that doesn't get drawn onto the screen, or shown until it's ready to be seen.

Update - Then there's the udpate function, which is left blank, because each entity in the game might need to be updated differently, so leaving this blank we don't assume all entities will need to be updated a certain way (some may need to move, while others might not), and so that will let each specific entity (like the player) who needs to check for movement update on it's own by overriding this function once it extends AbstractEntity.

Ok, well since we've just discussed about the AbstractEntity class, and it's all about being a base class to other game entities, and our main goal for this tutorial is to create the player, and make it move let's discuss the player class now.

The Player

OK, now are you glad we made a UML model of this game? Even if it's a little vague and incomplete at this time it at least gives us a little idea of how we can start the player class. It's going to extend from AbstractEntity, it's going to need to move, so it's going to have it's own personal update, and well at least for now that will be it.
/* 
	Class:		Player
	@author:	C.S. Finch (a.k.a codne4fun)
	
	Purpose:	extend from the Abstract Entity class as the player. Main character, and only character, which
				the player can actually control using the left and right arrow keys as well as the spacebar to shoot.
				Update method makes sure the player has boundaries, and keyHanlder checks for keyboardEvents
 */

package
{
	import flash.display.BitmapData;
	import flash.events.KeyboardEvent;
	import flash.geom.Point;
	
	import mx.core.*;
	
	public class Player extends AbstractEntity
	{
		
		public function Player()
		{
			
		}
		
		//initialize the player
		public function initPlayer():void
		{
			super.init(ResourceManager.GalagaShipGraphics, new Point(Constants.width / 2 , Constants.height - 60));
		}
		
		
		override public function shutdown():void
		{
			super.shutdown();
		}
		
		//update the player.  if the player goes past the boundaries reset the player's position
		override public function update(seconds:Number):void
		{
			if(position.x < 0) position.x = 0;
			if(position.x + graphics.bitmap.rect.width > Constants.width) position.x = Constants.width - graphics.bitmap.rect.width;
			super.update(seconds);	
		}
		
		//give the player some movement
		public function keyHandler(event:KeyboardEvent):void
		{
			if(event.keyCode == 37) 	 position.x -= 20;
			else if(event.keyCode == 39) position.x += 20;
		}
	}
}



Let me guess you're surprised to see only 4 specific functions for this class aren't you? Well, now you can see why having a base class is so important and time saving in game development! ;)

Ok let me break it down for you if you haven't already figured it out.

initPlayer() - All entities in the game will need to first be initialize. And, well since we're extending AbstractEntity, and we already have an init() function we create a separate initPlayer() specifically initializing the player, and then we call init() instead of overriding it, so we can construct the entity to take form as we need it to be the player. Giving it positions of middle of the screen and 50 pixels less than the entire height of the screen puts our player precisely where we need him when we start Galaga.

Note: Since earlier on we created the application's width/height 100% it's good practice when dealing with the screen at all to continue to refer to the applications.width and height, so we have a good proportion of graphics compared to the size of the screen. Also I created a constants class and set the width and height of the applications width and height to these constants. That's so I didn't have to call on Application.application.width .... each time i wanted to use them.

so when I call the super init, and give it a position of the Constants.width / 2, Constants.height - 50, I'm referring to the applications.width and height.

shutdown() - This shutsdown our player specifically by calling it's abstract class shutdown, and checks to see if we're inuse, and since we are (if we have initialized ourselves) then we make all of our variables null, which terminates the player.

update() - This simply updates the player. Remember when we created update() in our first application starting this game, and I said it once the function called each time the screen is redrawn? Well guess what? We call this function from it's abstract class, which is passed from that update() function we created in our first app to update the screen for all entities. Remember how I said we left the update() function in the AbstractEntity class empty, for each entity class may need to be updated depending on what they were? Well, the player only needs to check if he's moving out from the screen, and if so then reposition the player to the edge of the screen, so we don't have the player to move 1000+, or -1000+ pixels away from the game That would be bad :crazy: so we simply each time we redraw the screen check to see if this occurs.

keyHandler() - We pass the player the keyboardEvent, and check to see if any specific keys are being pressed rather it is 37 which is the left arrow key on your keyboard, or 39 which is the right arrow key, and if so then we move the player it's original x position += 20 pixels. This is as close as I could mimic the original Galaga movement of the ship. In the source below as I provide source code for all projects you'll have the graphics for the ship, and if you follow these steps carefully you can see the player move on your screen much like the following image below.
Posted Image

:pirate: Aarghh matey! The image doesn't justify your hard earn work, and that is why you must test out the game in the source below, but that's a good representation of the player on the screen.

Quickly let's reference the constants class

Constants Class
package
{
	import flash.display.*;
	import mx.core.*;
	
	public class Constants
	{
		public static var width:Number = Application.application.width;
		public static var height:Number = Application.application.height;	
	}
}



Again, we're simply just creating a constants class to refer to when we need it by having static variables with shorter names, and/or global variables that will be needed throughout the project. Now... to the EntityManager class.

The EntityManager Class
This is the class that manages all the entities on the screen. Even though the AbstractEntity is the abstract class that all game entities will extend from we still need a class to manage them. I choose to use the Singleton design pattern, which means create a single instance, and make it where you can only create a single instance of a specific object. this pattern has held up good for many games I have made in the past, and from what I have learned with other tutorials I have read rather it be Java, C# if it pertains to managing game resources 99% of them use the Singleton design pattern. Singleton is a design pattern it has no language specifics, and if you'd like anymore information on Singleton google it.

/*  
	Class:		EntityManager
	@author:	C.S. Finch (a.k.a coden4fun)
	
	Purpose:	EntityManager is a Singleton design entity management class that provides a way to keep track
				of all entities in the game by grabbing, drawing, updating, deleting all entities in the game,
				which all are extended entities from the AbstractEntity class.  This provides for an overall 
				structure to edit entities in the game, and make, and reuse code when needed.
*/

package
{
	import flash.display.*;
	import flash.events.KeyboardEvent;
	
	import mx.collections.*;
	import mx.core.*;
	
	public class EntityManager
	{
		public var doubleBuffer:BitmapData;
		public var clrScreen:uint = 0x000000;
		protected var lstFrame:Date;
		protected static var instance:EntityManager = null;
		
		//array collections to keep track of all entities in game, being put in the game, or being removed from game
		protected var Entities:ArrayCollection = new ArrayCollection();
		protected var newEntities:ArrayCollection = new ArrayCollection();
		protected var removedEntities:ArrayCollection = new ArrayCollection();
		
		public var player:Player;
		
		//create instance of the EntityManager (this is what's called a Singleton instance)
		static public function get Instance():EntityManager
		{
			if(instance == null)
				instance = new EntityManager();
				
			return instance;
		}
		
		public function EntityManager()
		{
			if(instance != null)
				throw new Error("Can only create one Singleton at a time");
				
			//create the double buffer
			doubleBuffer = new BitmapData(Constants.width, Constants.height, false);
			player = new Player();
		}
		
		//initialize all entities in the game
		public function init():void
		{
			lstFrame = new Date();
			player.initPlayer();
		}
		
		//shutdown entities of the game
		public function shutdown():void
		{
			
		}
		
		//update the game
		public function update():void
		{
			var currentFrame:Date = new Date();
			var seconds:Number = (currentFrame.getTime() - lstFrame.getTime()) / 1000.0;
			lstFrame = currentFrame;
			
			insertEntities();
			removeEntities();
			
			//update entities that are in use "on the screen"
			for each(var entity:AbstractEntity in Entities)
			{
				if(entity.inUse)
					entity.update(seconds);
			}
			
			//draw entities
			drawEntities();
		}
		
		//draw the entities onto the screen
		protected function drawEntities():void
		{
			doubleBuffer.fillRect(doubleBuffer.rect, clrScreen);
			
			for each(var entity:AbstractEntity in Entities)
			{
				if(entity.inUse)
				{
					entity.drawEntity(doubleBuffer);
				}
			}
		}
		
		//add entity to the entities array collection.
		public function addEntity(entity:AbstractEntity):void
		{
			newEntities.addItem(entity);	
		}
		
		//remove entity 
		public function removeEntity(entity:AbstractEntity):void
		{
			removedEntities.addItem(entity);
		}
		
		//insert new entity in the array collection of Entities
		public function insertEntities():void
		{
			for each (var entity:AbstractEntity in newEntities)
			{
				for (var i:int = 0; i < Entities.length; ++i)
				{
					if (Entities.getItemAt(i).Order > entity.Order ||
						Entities.getItemAt(i).Order == -1)
						break;
				}
				Entities.addItemAt(entity, i);
			}
			newEntities.removeAll();
		 }
		 
		 //insert entity to the removedEntity array collection, then remove all in  array
		 public function removeEntities():void
		 {
			for each (var removedEntity:AbstractEntity in removedEntities)
			{
				var i:int = 0;
				for (i = 0; i < Entities.length; ++i)
				{
					if (Entities.getItemAt(i) == removedEntity)
					{
						Entities.removeItemAt(i);
						break;
					}
				}	
			}
			removedEntities.removeAll();
		  }
		  
		  protected function shutdownAll():void
		  {
				for each(var entity:AbstractEntity in Entities)
				{
						var found:Boolean = false;
						
						for each(var removedentity:AbstractEntity in removedEntities)
						{
							if(removedentity == entity)
							{
								found = true;
								break;
							}
						}
						
						if(!found)
							entity.shutdown();
				}			  
		  }
	}
}



This is a pretty straightforward class as well, so let's get to the meat of it all.

doubleBuffer - This is a BitmapData, think of it as an image that other images are drawn onto, and then finally the doubleBuffer is drawn onto the screen. Sounds weird :blink: ? Well, tough that's exactly what a double buffer is, so naming it doubleBuffer just makes a lot of sense.

clrScreen - This is really an unsigned integer as a hexadecimal color in disguise. It simply paints the screen black, and once you see it in the works you'll know what it is.

lstFrame - This finds the time of the last frame that was drawn. As in gives a specific time of when the last frame was drawn. We use it in update.

instance - Ah instance the EntityManager inside EntityManager well that's how Singelton classes are created. We instantiate this class, and return this instance, so we can use it in other classes, and so we know that we only have one instance of this object we'll only have one instance of all objects in the game (unless we specify otherwise).

The array collections, which I won't write them down, so I can save time are for all possibly states an entity can be in. In the game, being put in the game, or being removed from the game. We simply create these array collections so we can quickly find any that need to be in or out and make the desired request for that game entity.

player - gives us a specific instance of player, so we can later pass on the keyboardEvent to that player. This is so we don't give all entities a keyboardEvent, or pass the EntityManager the keyboardEvent when only the player needs to the keyboard.

//create instance of the EntityManager (this is what's called a Singleton instance)
		static public function get Instance():EntityManager
		{
			if(instance == null)
				instance = new EntityManager();
				
			return instance;
		}
		
		public function EntityManager()
		{
			if(instance != null)
				throw new Error("Can only create one Singleton at a time");
				
			//create the double buffer
			doubleBuffer = new BitmapData(Constants.width, Constants.height, false);
			player = new Player();
		}



This above code is simple to follow. We create an instance of EntityManager and return that instance. This, is so we can call on EntityManager in other classes if we need to. This is also to only have one instance of this class (Singleton remember this design).

Then the actual construction of this class checks to see that we only have one instance of this at turn, and then creates the double Buffer by creating a bitmap image with width and height equal to the applications.

//initialize all entities in the game
		public function init():void
		{
			lstFrame = new Date();
			player.initPlayer();
		}
		
		
		public function shutdown():void
		{
			
		}



All classes need to initialize something, so when we initialize EntityManager we want to get the precise date of today. Why? Well when we want to calculate the fps or update a screen at a specific amount of milliseconds, or seconds we get the lastframe soon as we initialize, and then as soon as we update we get the date again, and then we get both times of lastframe, and currentframe subtract the 2, and that's how fast we're rendering, updating our screen. This is simply to initialize anything that needs to initialize when we first start the game, so that would be all entities that need to be initialize at game time like the player.

//update the game
		public function update():void
		{
			var currentFrame:Date = new Date();
			var seconds:Number = (currentFrame.getTime() - lstFrame.getTime()) / 1000.0;
			lstFrame = currentFrame;
			
			insertEntities();
			removeEntities();
			
			//update entities that are in use "on the screen"
			for each(var entity:AbstractEntity in Entities)
			{
				if(entity.inUse)
					entity.update(seconds);
			}
			
			//draw entities
			drawEntities();
		}



Updates all entities, and manages the AbstractEntity class using array collections. At the beginning of this function we get an update from the screen in seconds of how fast we're updating the screen. then we're inserting/deleting any entities that needs to be inserted or deleted by calling the insertEntities and removeEntities functions respectively.

We then go through a loop and check to see in the Entities array collection, which entities are inuse, and if they're in use then we pass them our calculations in seconds our fps, and update the entity that fast.

we then draw all entities by calling draweEntities().

//draw the entities onto the screen
		protected function drawEntities():void
		{
			doubleBuffer.fillRect(doubleBuffer.rect, clrScreen);
			
			for each(var entity:AbstractEntity in Entities)
			{
				if(entity.inUse)
				{
					entity.drawEntity(doubleBuffer);
				}
			}
		}



We first clear the screen, by painting the entire screen black... Remember clrScreen? it's an unsigned integer disguised as a color? Remember it now?

We then go through a simple loop and check if any entity in the Entities array collection is inUse (as in they've been initialized and want to be drawn on the screen) then we call their specific drawEntity function, and pass it the doubleBuffer, which is the bitmap "double buffer" image that draws all entities onto the screen.

OK, that was pretty easy to understand wasn't it? :D

//add entity to the entities array collection.
		public function addEntity(entity:AbstractEntity):void
		{
			newEntities.addItem(entity);	
		}
		
		//remove entity 
		public function removeEntity(entity:AbstractEntity):void
		{
			removedEntities.addItem(entity);
		}



This either adds an entity to the Entities arrayCollection, so we can use it, or to the removedEntities array collection, so we can go through our loops and either add or delete that specific entity.

//insert new entity in the array collection of Entities
		public function insertEntities():void
		{
			for each (var entity:AbstractEntity in newEntities)
			{
				for (var i:int = 0; i < Entities.length; ++i)
				{
					if (Entities.getItemAt(i).Order > entity.Order ||
						Entities.getItemAt(i).Order == -1)
						break;
				}
				Entities.addItemAt(entity, i);
			}
			newEntities.removeAll();
		 }



We create a loop and check which entities in the Entities array Collection match those that we added in the newEntities array collection when we added that entity to the newEntities array collection when calling addEntity, and since this function (insertEntities) is being called at each update every possible 0.0083 seconds we're checking to see what entities are wanting to be added to the screen. And if any entities are in this newEntities collection we add that specific one to the Entities array collection, and then remove all entities in the newEntities collection.. Yup, we empty it out, so we don't add multiple entities in the game that don't need to be added.

Then...

//insert entity to the removedEntity array collection, then remove all in  array
		 public function removeEntities():void
		 {
			for each (var removedEntity:AbstractEntity in removedEntities)
			{
				var i:int = 0;
				for (i = 0; i < Entities.length; ++i)
				{
					if (Entities.getItemAt(i) == removedEntity)
					{
						Entities.removeItemAt(i);
						break;
					}
				}	
			}
			removedEntities.removeAll();
		  }



Pretty much does the samething except it checks which entities are in the removedEntities collection, and in the Entities collection, and then deletes any that are duplicates, and then removes all in the removedEntities collection, so we don't just continue to remove all the entities on the screen each time we redraw the screen. Also, we add entities to the removedEntities collection when we call the function removeEntity(), so we're safe! :P

now we need a function that will shutdown all entities as in remove them all from all arrays, and make all their variables null, and delete them from memory. Luckily we have a function that does that, and it's the following shutdownAll()!

protected function shutdownAll():void
		  {
				for each(var entity:AbstractEntity in Entities)
				{
						var found:Boolean = false;
						
						for each(var removedentity:AbstractEntity in removedEntities)
						{
							if(removedentity == entity)
							{
								found = true;
								break;
							}
						}
						
						if(!found)
							entity.shutdown();
				}			  
		  }



This function works sorta like the removeEntities function except that we check to see which entities in the Entities collection are that of the removedEntities collection, and if we find a match we simply break, for it's already been/being deleted, but if not then we remove that entity by calling it's specific shutdown() function.

OK, now for the main Application to see what little tweaks were made to get this going.

The Main Application
[code]
<?xml version="1.0" encoding="utf-8"?>
<!-- http://blog.flexexamples.com/2007/08/07/creating-full-screen-flex-applications/ -->
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml"
layout="absolute"
applicationComplete="init()"
frameRate = "120"
width="100%"
height="100%"
enterFrame="update(event)"
currentState="Menu"
backgroundColor="#000000">

<mx:Style source="components/style.css" />

<mx:Script>
<![CDATA[

import flash.display.StageDisplayState;

private var inGame:Boolean = false;
private var requestMouseOver:URLRequest = new URLRequest("../media/sfx/btnClick.mp3");
private var requestClick:URLRequest = new URLRequest("../media/sfx/click.mp3");
private var requestGame:URLRequest = new URLRequest("../media/sfx/energy.mp3");
private var sound:Sound;

//init initialize first function called when we start our app
private function init():void
{
stage.addEventListener(KeyboardEvent.KEY_D

Is This A Good Question/Topic? 0
  • +

Page 1 of 1