Game State Management

Games invariable have many different modes they can be in. Most have a state in which the play back the introduction movie, another mode during which they display the main menu and wait for the user to make a choice and yet another mode in which the game is actually being played.

During all these modes, the game's main loop does entirely different things. Switching between these modes by using if or switch statements in the game's Draw() and Update() methods can turn the code into a maintenance nightmare. Apart from that, games often don't require the same resources for different modes.

To solve this problem, most games employ a technique commonly referred to as Game States. Each mode the game is in becomes a state. A game state in this case is a distinct class to which the game can delegate its Draw() and Update() calls. Switching from the intro movie to the menu screen simply becomes a matter of replacing the active game state.

GameStateManager

The game state manager does what its name says: it manages your game states. Only one game state can be active at a time, but game states can be put on top of each other. For example, the main menu can push the gameplay game state onto the stack. When the game ends, it can pop itself from the stack and the user returns to the menu.

The game state manager's public interface is straightforward
/// <summary>Manages the game states and updates the active game state</summary>
public class GameStateManager {

  /// <summary>Updates the active game state</summary>
  /// <param name="gameTime">Snapshot of the game's timing values</param>
  void Update(GameTime gameTime) { /* ... */ }

  /// <summary>Draws the active game state</summary>
  /// <param name="gameTime">Snapshot of the game's timing values</param>
  void Draw(GameTime gameTime) { /* ... */ }

  /// <summary>Pushes the specified state onto the state stack</summary>
  /// <param name="state">State that will be pushed onto the stack</param>
  void Push(GameState state) { /* ... */ }

  /// <summary>Takes the currently active game state from the stack</summary>
  void Pop() { /* ... */ }

  /// <summary>Switches the game to the specified state</summary>
  /// <param name="state">State the game will be switched to</param>
  /// <remarks>
  ///   This replaces the running game state in the stack with the specified state.
  /// </remarks>
  void Switch(GameState state) { /* ... */ }

  /// <summary>The currently active game state. Can be null.</summary>
  GameState ActiveState { get { /* ... */ } }

}

GameState

A game state takes on the role of the Game class, but only for so long as it is active

This class' public interface is relatively simple, too:
/// <summary>
///   Manages a the state and resources of a distinct state the game can be in
/// </summary>
/// <remarks>
///   <para>
///     This class follows the usual game state concept: Instead of using hard-to-maintain
///     if-else-trees for deciding whether to render the main menu, game scene or
///     credits scroller, each of the game's phases is put in its own game state class. This
///     improves modularity and prevents the mixing of code that normally has nothing to
///     with each other. The game state manager allows multiple states to be active at
///     the same time and manages these active states in a stack, which is useful for
///     realizing ingame menus and nested scenes.
///   </para>
///   <para>
///     Game states can be either active or inactive and will be notified when this state
///     changes by the OnEntered() and OnLeaving() methods. Any game state starts out as
///     being inactive. Game states should only load and keep their resources during their
///     active period and free them again when they become inactive.
///   </para>
///   <para>
///     In addition to being active and inactive, game states also have a pause mode. This
///     mode can only be entered by active game states and is used to put the game state
///     in the back when another game state serves as a means to halt the
///     time in the game state. If multiple game states are active, only the topmost state
///     will be in unpaused mode.
///   </para>
/// </remarks>
public abstract class GameState {

    /// <summary>
    ///   Allows the game state to run logic such as updating the world,
    ///   checking for collisions, gathering input and playing audio.
    /// </summary>
    /// <param name="gameTime">Provides a snapshot of timing values</param>
    virtual void Update(GameTime gameTime) { }

    /// <summary>This is called when the game state should draw itself</summary>
    /// <param name="gameTime">Provides a snapshot of timing values</param>
    virtual void Draw(GameTime gameTime) { }

    /// <summary>Called when the game state has been entered</summary>
    virtual void OnEntered() { }

    /// <summary>Called when the game state is being left again</summary>
    virtual void OnLeaving() { }

    /// <summary>Called when the game state should enter pause mode</summary>
    virtual void OnPause() { }

    /// <summary>Called when the game state should resume from pause mode</summary>
    virtual void OnResume() { }

    /// <summary>Game state manager this game state belongs to</summary>
    protected GameStateManager GameStateManager { get { /* ... */ } }

}

Last edited Nov 5, 2009 at 9:25 AM by Cygon, version 4

Comments

Boinst Aug 1, 2011 at 1:27 AM 
Nice, this code seems a little cleaner than the XNA Creator's Club sample code.