Skip to content

Development : Mallet Engine : Entry Point

August 3, 2012

When you think about the structure of a typical game engine, it is designed to provide a set of tools for developers to create their games. However, it isn’t a library like SDL, or SFML, as they don’t force the developer to write code in a particular way. So an engine is more like a framework. It provides a set of tools that the developer can extend, add, and modify, but, the developer must adhere to a particular philosophy defined by the engine builders.

Mallet Engine is no different, it provides great flexibility at low levels, but, the developers must adhere to the overall design structure to ensure that their code works as expected and plays nicely with the other systems. As with other applications the entry-point is greatly important. To the Mallet Engine it defines what modules, states, and general settings should be loaded. The entry-point is specified by the game developer and is not strictly defined, however there is typically an order.

  1. Create low-level system.
  2. Load global configuration file and configure low-level system.
  3. Initialise low-level system.
  4. Create game system.
  5. Create appropriate game-states and add them to the game system.
  6. Run game-system

The Mallet Quest entry-point provides an example of this order in action:

public static void main( String _args[] )
{
    GLDefaultSystem system = new GLDefaultSystem() ;
    ResourceManager resources = ResourceManager.getResourceManager() ;

    Settings config = null ;
    config = ConfigParser.parseSettings( ConfigReader.getConfig( "base/config.cfg" ), config ) ;
    resources.setConfig( config ) ;
    displayWidth = config.getInteger( "DISPLAYWIDTH", displayWidth ) ;
    displayHeight = config.getInteger( "DISPLAYHEIGHT", displayHeight ) ;
    renderWidth = config.getInteger( "RENDERWIDTH", renderWidth ) ;
    renderHeight = config.getInteger( "RENDERHEIGHT", renderHeight ) ;
    system.setDisplayDimensions( new Vector2( displayWidth, displayHeight ) ) ;
    system.setRenderDimensions( new Vector2( renderWidth, renderHeight ) ) ;
    system.initSystem() ;
    startGame( system ) ;
}

private static void startGame( final SystemInterface _system )
{
    GameSystem gameSystem = new GameSystem() ;
    QuestGame game = new QuestGame( "GAME" ) ;
    gameSystem.setSystem( _system ) ;
    gameSystem.addGameState( game ) ;
    gameSystem.setDefaultGameState( "GAME" ) ;

    gameSystem.runSystem() ;
}

GL Default System creates and manages the low-level subsystems that handle the resources of the entire engine. It implements the System Interface enabling you to write your own and create specific implementations for different resources. The entry-point quickly moves onto loading the global configuration file and setting the appropriate view dimensions. It then wraps up by calling initSystem to ensure all low-level subsystems have been hooked-up correctly.

The Game System controls game-states, these states contain your game related logic. Only one game-state can be updated at a time. A game-state can make a request to transition to another game-state, for example you can have an Intro State, your Game State, a Menu State, and a Credit State, transitioning between them when it is appropriate. During a transition you can also pass a package to the new state, this package contains relevant information, for instance if the Game State was transitioning to the Menu State it may want to show the game-save page instead of the default character-select page.

The default constructor of a game-state should be bare. The reason for this, is to ensure that a game-state does not consume resource memory when it is not being used. The Credit State, for example, is constructed at the start, but will likely never be seen until the end of the game. We only begin loading and initialising the required resources once the startState function has been called.

// Game State functions used by the State Machine
public void update( final double _dt ) {}

public void startState( final Settings _package ) {} // Called when transitioning to this state
public Settings shutdownState() {}                   // Called when transitioning on a Shutdown request
public Settings pauseState() {}                      // Called when transitioning on a Pause request

public final String getTransition()                  // Name of state to transition too
{
    return transition ;
}
public final int checkTransition()                   // Informs whether state wants to transition
{
    return transitionType ;
}

A game-state has two options when making a transition request, it can either shutdown, or pause. The shutdown request is to allow the state to begin afresh with a blank slate, as if it had just been created. The pause request allows the state to keep track of any relevant data it sees fit. You don’t want your Game State data to disappear when transitioning to the Menu State.

The transition is a linear process which is not very good if you are loading very large game-states as it would produce a visible stall. It would be possible to start the game-state asynchronously by placing the heavy loading on a separate thread, and then move to a loading-screen-state. Once the heavy loading has completed, issue a message that informs the loading-screen-state to transition back to the game-state. I would prefer to implement a more elegant and streamlined process that is less error prone by the developer.

The entry-point process is completed when the runSystem is called, which kicks off the engines game-loop.

public final void runSystem()
{
    double dt = 0.0f ;
    boolean running = true ;
    while( running == true )
    {
        dt = ElapsedTimer.getElapsedTimeInNanoSeconds() ;
        running = system.update() ;
        stateMachine.update( dt ) ;
    }
}

The Mallet Engines’ game-loop is only a few lines of code. Within the while-loop Elapsed Timer is called to get an average delta-time over twenty or so iterations. Afterwards system.update is called, which ticks over the essential subsystems like input, and events, it also returns a boolean to allow the engine to completely shutdown. Finally the state-machine is updated, which in turn updates the currently running game-state.

By allowing the developer to manipulate the entry-point it provides great flexibility to deal with platform specific nuances. It allows the developer to change low-level subsystems to operate better on a specific platform, or change how the engine receives its configuration data, without directly affecting the other platforms, or the game. This design shows its benefits when dealing with Android, as it handles certain concepts differently compared to a traditional Java desktop application.

I plan in the next week or so to go into further discussion on how the system, and game-state operates. If you have any questions please post them in the comments below.

Ross.

Advertisements
No comments yet

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: