Building a Foundation For Your Games and Handling Screens
This article is part 1 of a series: The Structure of a Game. Read the Introduction here.
What does a game foundation consist of?
The starting point for any well built structure is a strong foundation. It is important that the foundation of your game is able to handle the basic functionality that will apply across all of your games, and does it simply with little to no extra effort on your part.
Also, what do all games have in common? They consist of various screens (Oh yes they do, or your game would be considered a tech demo my friend). With that in mind, our structure should be able to handle the following:
- Context Menu Setup
- Access to the Stage.
- Tweaking of game speed
- Frame Rate Tracking
- Sending updates to your game
- Custom Game Cursor ( if applicable to your game )
- Global game pause
- Ability to step through code 1 update at a time
- Screen switching
In order to implement all of these in a manner that we can easily and quickly transfer over to each and every project and use without any real need of changing the code at all we will create our structure using the following 3 classes:
- IScreenItem - Anything that will be considered a screen, such as MenuScreen, GameScreen, etc…
- AScreen - An Abstract class which handles screen switching.
- Root - Our meat and potatoes of the foundation. Handles the “Basic” functionality discussed above.
Lets build it!
A few concepts for me to mention now before we start are that almost all of our game objects will use an update and dispose method. Updates will filter down from the Root class so as to keep greater control of the game and eliminate the inconvenience and speed hog of creating multiple frame event listeners to the stage. Also it is good practice to clean up your code on removal such as removing any event listeners and nulling any outside references.
Also we will be using a constant time step to update our code. It is quite simple to interchange actually which I can show you, but in my experience a non-constant time step can cause inconsistencies that are difficult to debug.
When I refer to “Global Game Pause” this means that all updates to the game objects and rendering. You may have seperate pause logic within your game screen that pauses only certain features, still allowing menus or other GUI to run.
The IScreenItem and AScreen Classes
Okay, so let’s begin with the IScreenItem class. This will be the interface for any screen used in the game…
public interface IScreenItem extends IEventDispatcher
{
function setScreen(screen:Class):void;
function update(timePassed:int):void;
function dispose():void;
function get parentScreen():IScreenItem;
function get currentScreen():IScreenItem;
}
All screens will have an interface for updating thier logic. We will send the time passed since the last update so that it is calculated in a centralized location ( The Root ). Also each screen is responsible for disposing of itself…if necessary.
We also build an abstract class AScreen which holds the basical functionality of screen switching for us.
public class AScreen extends Sprite implements IScreenItem
{
// Initialization ---------------------------------------------------------------------------
public function AScreen( parentScreen:IScreenItem=null )
{
m_parentScreen = parentScreen;
m_parentScreen == null ? m_currentScreen = null : m_currentScreen = currentScreen;
}
// ------------------------------------------------------------------------------------------
public function get parentScreen():IScreenItem{ return m_parentScreen; }
/**
* Will filter up to the top of the screen structure and return the root current screen.
* This is done so that any screens created down the line may have access to change the
* screen without actual reference to the root.
*
*/
public function get currentScreen():IScreenItem
{
if( m_parentScreen != null )
{
return m_parentScreen.currentScreen;
}
return m_currentScreen;
}
// Override these functions with your subclasses ----------------------------------------------
public function update( timePassed:int ):void
{
if( m_parentScreen==null && m_currentScreen!=null ){ m_currentScreen.update( timePassed ); }
}
public function dispose():void{}
// --------------------------------------------------------------------------------------------
/**
* Changes the root current screen to the screen specified.
* All screen's dispose method are called for cleanup.
*
*/
public function setScreen( screen:Class ):void
{
if ( m_parentScreen != null )
{
m_parentScreen.setScreen( screen );
return;
}
if( m_currentScreen!= null )
{
removeChild( m_currentScreen as Sprite );
m_currentScreen.dispose();
}
m_currentScreen = new screen( this );
addChild( m_currentScreen as Sprite );
}
// -- PRIVATE --
private var m_parentScreen:IScreenItem;
private var m_currentScreen:IScreenItem;
}
The Root Class
Now finally we build the bread and butter, which is the Root class. I’ve left out loading of external data and setting up a custom cursor. We can discuss those topics in a later article. You can see that I handle game speed by using a multiplier on the update function. There is code in the handleInput function to help with some in game debugging.
Note also that I am using my Input Manager class. I HIGHLY recommend that you use some sort of class to manage input in your games, whether it is mine or one of your own creation. If you would like view the article on the Input Class you can go here. To download the input class files visit the google project page here.
public class Root extends AScreen
{
private const DEFAULT_QUALITY:String = "high";
private const TIME_PASSED:int = 20;
private const ONE_SECOND:int = 1000;
private const FRAME_RATE:int = 50;
// -- STATIC --
public static function set gameSpeed( value:Number ) : void
{
m_gameSpeed = value;
if( m_gameSpeed < 0 ) { m_gameSpeed = 0; }
}
public static function get gameSpeed() : Number { return m_gameSpeed; }
public static function get fps() : int { return m_fps; }
public static function get avgFps() : int { return m_avgFps; }
public static var stage:Stage;
/**
* Use to globally pause the game.
*
*/
public static function togglePause() : void
{
m_isUpdateOn = !m_isUpdateOn;
}
/**
* Place in code where you would like a breakpoint. The game will be paused globally
* and the message will be traced.
* @param a_message The message to output when this breakpoint is reached.
* @param a_forced Force this breakpoint to run, even if breakpoints are turned off.
*
*/
public static function breakpoint( a_message:String, a_forced:Boolean ):void
{
if( !m_areBreakpointsEnabled && !a_forced ){ return; }
trace( " Breakpoint: " + a_message );
m_isUpdateOn = false;
}
// -- PUBLIC --
public function Root(stage:Stage=null)
{
m_gameSpeed = 1;
if( stage==null )
{
Root.stage = this.stage;
}
else
{
Root.stage = stage;
}
Root.m_fps = Root.m_avgFps = 0;
Root.stage.quality = DEFAULT_QUALITY;
m_isUpdateOn = true;
m_areBreakpointsEnabled = true;
Input.instance.activate(Root.stage);
// External data will be discussed in a future article
// Load External Data ----------------------------------------
// ExternalData.instance.addEventListener( Event.COMPLETE, onDataLoad );
// ExternalData.instance.load();
// -----------------------
onDataLoad( null );
}
public override function setScreen(screen:Class):void
{
m_screenToSet = screen;
}
// -- PRIVATE --
private function onDataLoad( e:Event ):void
{
// ExternalData.instance.removeEventListener( Event.COMPLETE, onDataLoad );
Root.stage.addEventListener( Event.ENTER_FRAME,onEnterFrame );
Root.stage.showDefaultContextMenu = false;
// Set up custom cursor here -----
// Mouse.hide();
// m_customCursor = new CustomCursor();
m_stopWatch = new StopWatch();
m_deltaTime = 0;
}
private function onEnterFrame( e:Event ):void
{
recordFPS();
if( m_isUpdateOn )
{
update( TIME_PASSED * m_gameSpeed );
}
handleInput();
Input.instance.update();
//handle custom cursor
if( m_screenToSet )
{
super.setScreen( m_screenToSet );
m_screenToSet = null;
}
}
private function recordFps() : void
{
m_deltaTime = m_stopWatch.timePassed;
m_stopWatch.reset();
m_fps = ONE_SECOND / m_deltaTime;
m_fpsCount += Root.m_fps;
m_avgFpsCounter--;
if( m_avgFpsCounter <= 0 )
{
m_avgFps = m_fpsCount / FRAME_RATE;
m_avgFpsCounter = FRAME_RATE;
m_fpsCount = 0;
}
}
/**
* Here we handle code for dealing with breakpoints, global pausing, and stepping.
* Be sure to deactivate this code for the release of your game!
*
*/
private function handleInput():void
{
if( Input.instance.isKeyPressed( KeyCode.END ) )
{
m_isUpdateOn = !m_isUpdateOn;
trace(" Game " + ( m_isUpdateOn ? "unpaused" : "paused" ) );
}
if( Input.instance.isKeyPressed( KeyCode.PAGE_DOWN ) )
{
if( !m_isUpdateOn )
{
trace( " Stepping foward " + TIME_PASSED + " milliseconds." );
update( TIME_PASSED );
}
}
if( Input.instance.isKeyPressed( KeyCode.PAGE_UP ) )
{
m_areBreakpointsEnabled = != m_areBreakpointsEnabled;
trace( " Breakpoints " + ( m_areBreakpointsEnabled ? "enabled" : "disabled" ) );
}
}
private static var m_isUpdateOn:Boolean;
private static var m_areBreakpointsEnabled:Boolean;
private static var m_fps:int;
private static var m_avgFps:int;
private static var m_gameSpeed:Number;
private var m_screenToSet:Class;
private var m_stopWatch:StopWatch;
private var m_newTime:int;
private var m_oldTime:int;
private var m_deltaTime:int;
private var m_fpsCount:int;
private var m_avgFpsCounter:int;
}
Wrapping up
Now that you have the basic structure set up, starting any new game project is extremely simple. Have your preloader call a “Main” class whose sole function is to be the Root screen. The main class will call the first screen in the game, usually the Menu.
public class Main extends Root
{
public function Main(stage:Stage)
{
super(stage);
setScreen( Screen1 );
}
}
Now when creating your screens, just extend the AScreen class:
public class Screen1 extends AScreen
{
public function Screen1(parentScreen:IScreenItem=null)
{
super(parentScreen);
var txt:TextField = new TextField();
txt.htmlText = " Screen 1 ";
txt.x = 100;
txt.y = 100;
addChild( txt );
}
public override function update(timePassed:int):void
{
if( Input.instance.isKeyPressed( KeyCode.ENTER ) )
{
setScreen( Screen2 );
}
}
}
Well, that’s it for your game foundation! You can download the code along with an example project at the google project page.







Jaysin said
am November 19 2008 @ 9:44 pm
Just found your site a few days ago. Trying to wrap my head around all of this. A lot has changed since I last played around in the AS2 days. Thanks for all of your tutorials and I look forward to more.
Scarybug said
am November 22 2008 @ 7:06 pm
Do you have a version of this packaged for FlashDevelop + Flex3SDK? I’m having some trouble getting it to compile and show anything.