Managing Game Objects and Rendering a Game World

This is the 3rd article in the series Building a Game Structure. Be sure to read the introduction which also has links to the rest of the articles in the series.

There comes a time when working on a game that you realize the need for an effective way to manage all of your entities and game objects. Also, not all games consist of just one simple non-scrolling screen so you must devise a robust way to deal with the problems of displaying your entities in the correct position based on where in the world you want the view to be centered, not to mention handling the mouse’ world position. Not only that, but it would certainly be nice to have some basic, simple, and reusable classes for rendering your entities so that you don’t ever have to deal with it again.

These are the issues I will be covering in today’s article, and of course I will wrap up by showing the code that I use and handing you the source to play with. I’ve made a few slight changes to some of the classes which I will talk about below.

Game World: A Central location for managing your objects

One of the benefits of creating a manager for the game’s objects is that you can eliminate errors caused by adding or removing entities in the middle of some logic. The manager should queue up additions and removals and then handle them all at once at the end of the update function.

The GameWorld itself is an Entity. It’s dimensions are the size that you wish your world to be, whether that be the size of the screen or a larger scrolling area.

Collision detection can be handled through the GameWorld class as well. What I have done is set up a hook function that calls an class: ICollisionResponse. If you wish to handle collisions within the GameWorld, you can simply create a class that does this by plugging into that interface.

Finally, We will use the Game World class to set up our GameCamera and activate a helpful “worldMousePos” variable within the Input class. Those I will talk about shortly.

The code is quite short and simple. Here is the Interface:

public class GameWorld extends Entity
{
        // Add collision response to the Game World
	public function set collisionResponse( a_value:ICollisionResponse ) : void { m_collisionResponse = a_value; }
 	public var camera:GameCamera; // The camera is left public since it may be accessed often.

        // The Constructor will initialize the camera and the collision response if necessary.
	public function GameWorld( a_worldSize:Vector2D, a_cameraSize:Vector2D, a_collisionResponse:ICollisionResponse = null )

        // We can set the size of the world here
 	public function setSize( a_width:int, a_height:int ) : void

        // Here we update the entities, process add and removes, and handle collisions
 	public override function update( a_timePassed:int ):void

 	public override function dispose():void

        // We override these and queue up the operations rather than doing them immediately
 	public override function addChild(a_entity:Entity):void

 	public override function removeChild(a_id:String):void

}

Also the collision response interface is quite small, requiring only one function to operate:

public interface ICollisionResponse
{
	function handleCollision( a_entity1:Entity, a_entity2:Entity ) : void;
}

Using a Game Camera to Assist With Rendering

In order to display the position of entities in a scrolling world, an inexperienced programmer might choose to simply move all other objects in relation to the main character. This is a very inflexible way to handle things. The more sensible approach would be to create a camera entity which all other entities will render thier locations based off of.

The camera class may also allow for nice effects such as movie style camera panning and zooming. For this version of the camera class, we will create a restraint that makes sure that the camera does not pan past the bounds of the game world. Also we will give it a function if called will cause it to follow a position by passing reference to a Vector2D object ( one of the many benefits of using the class, rather than a basic x/y variable )

Once again, the interface is fairly simple:

public class GameCamera extends Entity
{
	public static const MOVE:String = "camera_move_event";

	public function GameCamera( a_gameWorld:GameWorld, a_width:Number, a_height:Number )

	public function setSize( a_width:int, a_height:int ) : void

	public override function update( timePassed:int ):void

        // The camera will lock onto this position
	public function followPos( a_pos:Vector2D ):void

}

Simple Reusable Rendering Classes

Obviously with this game model the actual rendering of the entities has been seperated from the data and logic. In order to speed up and simplify production of any new projects as well as testing we create several rendering classes to make our lives easier.

These next few basic renderers will handle rendering different data types such as bitmaps and sprites. Also I have written somewhat of a “debug” entity renderer which simply renders the radius and rotation of the entity. ( We don’t always have graphics to attach to our objects right away! )

First off, I use an abstract Renderer function to supply the interface and the common functions that are used for most renderers. The abstract class will use hook functions that are expected to be overridden by subclasses to provide the full functionality. Also, Renderers are considered “Game Objects” so they will inherit that interface.

What the renderer class does is take whatever graphics you wish to display for the entity and place it inside of a “container.” Each update the container is positioned on the screen relative to the camera based on the entities position in the world. If the entity is scaled, the container will handle that scaling operation. Also on creation the renderer will center the registration point to the graphics being displayed, since that is the most common and easiest way to deal with graphics in games.

If the viewable area of the entity is off of the screen then the renderer will set it’s visible property to false, to increase rendering speed. Ultimately the only thing you have to worry about is setting up the renderer initially and it will take care of the rest. That is the idea.

Here is the interface for the abstract renderer class:

public class ARenderer extends EventDispatcher implements IGameObject
{	

        // the a_canvas variable will be the layer which the container is added to.
	public function ARenderer( a_entity:Entity, a_camera:GameCamera, a_canvas:DisplayObjectContainer )

        // It is not recommended to override this function unless absolutely necessary.
	public function update( timePassed:int ):void

	public function dispose():void

	// -- PRIVATE --

        // Handle any updating of the entity representation here
	protected function draw():void

	/**
	 * Update values m_viewBoxX, m_viewBoxY, m_viewBoxWidth, m_vewBoxHeight
	 * so that the culling will work correctly.
	 *
	 */
	protected function updateViewBox():void

        // The view box was left as individual variables rather than using
        // the AABB data structure in order to speed up the renderer class.
  	protected var _viewBoxX:Number;
  	protected var _viewBoxY:Number;
  	protected var _viewBoxWidth:Number;
  	protected var _viewBoxHeight:Number;
        // The canvas is the sprite which will be rendered to the screen
  	protected var _canvas:DisplayObjectContainer;
        // The container is added to the canvas, and contains the actual graphics used
        // to represent the entity.
	protected var _container:Sprite;
  	protected var _entity:Entity;
}

The three renderers I’ve created for this article are the EntityRenderer (for debug rendering), Sprite Renderer, and the BitmapRenderer. They are pretty basic:

public var boundsColor:uint;

public function EntityRenderer( a_entity:Entity, a_camera:GameCamera, a_canvas:DisplayObjectContainer )
public function BitmapRenderer( a_entity:Entity, a_camera:GameCamera, a_canvas:DisplayObjectContainer, a_bitmapData:BitmapData )
public function SpriteRenderer( a_entity:Entity, a_camera:GameCamera, a_canvas:DisplayObjectContainer, a_sprite:Sprite

Game World Renderer

Finally, the GameWorldRenderer will contain the main canvas which all of the containers should be added to. Also, it it will manage entity layers for us. Here is the interface:

public function BitmapRenderer( a_entity:Entity, public class GameWorldRenderer extends ARenderer
{
	private const MAX_LAYERS:int = 16;

        // a_renderSurface should be the stage, or any other display object on stage
	public function GameWorldRenderer( a_world:GameWorld, a_renderSurface:DisplayObjectContainer )

        // Use this function to get the a_canvas param for the ARenderer constructor
	public function getLayer( value:int ) : Sprite

        // If the canvas is not to be rendered at 0,0 then move it here.
	public function moveCanvas( x:int, y:int ) : void

	public override function update( timePassed:int ):void

	public override function dispose():void

}

Changes to preexisting code

I’ve made some slight modifications to the Input, Entity, and MovingEntity classes. I added a “worldMousePos” variable to the Input class, which can be activated once the GameWorld is created by calling:

public function activateWorldMouse( a_gameWorld:GameWorld, a_canvasX:int, a_canvasY:int ) : void

I added the “layer” and “isCollidable” variables to the Entity class, and fixed a problem with the MovingEntity’s rotation.

Flying Dogs!

For the example I just used some images from google to represent a background and some entities. Hold down the mouse to cause chaos! The camera will lock on and follow the last entity generated.

Be sure to take a look at the source code and the example to get a better understanding of how everything is working. You can download all of the files at the google project page here.

So what’s next?

Now that we have greatly simplified rendering and managing all of our game objects, the next thing for us to do is to centralize all of our object creation to factory classes. As of now, it is quite tedious to create our entities, and prone to errors and problems if later down the line we wish to change how objects are created. I will be discussing this in the next article.

Until then, be sure to comment with your thoughts and questions.

1 Star2 Stars3 Stars4 Stars5 Stars (7 votes, average: 4.57 out of 5)
Loading ... Loading ...

1 Comment so far »

  1. Christian said

    am December 7 2008 @ 9:54 pm

    great articles! next please ;)

Comment RSS · TrackBack URI

Leave a comment

Name: (Required)

eMail: (Required)

Website:

Comment: