Take the 2-minute tour ×
Game Development Stack Exchange is a question and answer site for professional and independent game developers. It's 100% free, no registration required.

First off, I'm not referring to scene management; I'm defining game state loosely as any sort of state in a game which has implications about whether or not user input should be enabled, or if certain actors should be temporarily disabled, etc.

As a concrete example, let's say it's a game of the classic Battlechess. After I make a move to take another player's piece, a short battle sequence plays. During this sequence, the player shouldn't be allowed to move pieces. So how would you track this sort of state transition? A finite state machine? A simple boolean check? It seems the latter would only work well for a game with very few state changes of this sort.

I can think of a lot of straightforward ways of handling this using finite state machines, but I can also see them quickly getting out of hand. I'm just curious if there's a more elegant way to keep track of game states/transitions.

share|improve this question
    
Have you checked out gamedev.stackexchange.com/questions/1783/game-state-stack and gamedev.stackexchange.com/questions/2423/… ? It's kind of all jumping around the same concept, but I can't think of anything that would be better than a state machine for game state. –  michael.bartnett Jun 6 '11 at 7:42
    
Possible duplicate: gamedev.stackexchange.com/questions/12664/… –  Tetrad Jun 6 '11 at 8:03
    
Accept previous answers. –  The Communist Duck Jun 6 '11 at 14:52

3 Answers 3

up vote 12 down vote accepted

I once came across an article that solves your problem quite elegantly. It is a basic FSM implementation, that is called in your main loop. I have outlined the basic rundown of the article in the rest of this answer.

Your basic game state looks like this:

class CGameState
{
    public:
        // Setup and destroy the state
        void Init();
        void Cleanup();

        // Used when temporarily transitioning to another state
        void Pause();
        void Resume();

        // The three important actions within a game loop
        void HandleEvents();
        void Update();
        void Draw();
};

Each game state is represented by an implementation of this interface. For your Battlechess example, this could mean these states:

  • intro animation
  • main menu
  • chess board setup animation
  • player move input
  • player move animation
  • opponent move animation
  • pause menu
  • endgame screen

States are managed in your state engine:

class CGameEngine
{
    public:
        // Creating and destroying the state machine
        void Init();
        void Cleanup();

        // Transit between states
        void ChangeState(CGameState* state);
        void PushState(CGameState* state);
        void PopState();

        // The three important actions within a game loop
        // (these will be handled by the top state in the stack)
        void HandleEvents();
        void Update();
        void Draw();

        // ...
};

Note that each state needs a pointer to the CGameEngine at some point, so the state itself can decide whether a new state should be entered. The article suggest passing in the CGameEngine as a parameter for HandleEvents, Update and Draw.

In the end, your main loop only deals with the state engine:

int main ( int argc, char *argv[] )
{
    CGameEngine game;

    // initialize the engine
    game.Init( "Engine Test v1.0" );

    // load the intro
    game.ChangeState( CIntroState::Instance() );

    // main loop
    while ( game.Running() )
    {
        game.HandleEvents();
        game.Update();
        game.Draw();
    }

    // cleanup the engine
    game.Cleanup();
    return 0;
}
share|improve this answer
8  
C for Class? Ew. However, that is a good article - +1. –  The Communist Duck Jun 6 '11 at 14:53
    
From what I can gather, this is the sort of thing that the question is explicitly -not- asking about. That's not to say you couldn't handle it this way, as you certainly could, but if all you wanted to do was disable the input temporarily I think it's both overkill and bad for maintenance to derive a new subclass of CGameState which is going to be 99% identical to another subclass. –  Kylotan Jun 6 '11 at 17:59
    
I would think this greatly depends on how the code coupled together. I can imagine a clean separation between selecting a piece and a destination (mainly UI indicators and input handling), and an animation of the chess piece towards that destination (a whole board animation where other pieces move out of their way, interact with the moving piece etc), making the states far from identical. This separates responsibility, allowing for easy maintenance and even reusability (intro demo, replay mode). I think this also answers the question in showing that using a FSM does not need to be a hassle. –  ghost Jun 6 '11 at 19:38
    
This is really great, thank you. A key point you made was in your latest comment: "using a FSM does not need to be a hassle." I had erroneously imagined that using a FSM would involve using switch statements, which isn't necessarily true. Another key confirmation is that each state needs a reference to the game engine; I wondered how this would work otherwise. –  vargonian Jun 7 '11 at 7:00

I start by handling this sort of thing the simplest way possible.

bool isPieceMoving;

Then I'll add the checks against that boolean flag in the relevant places.

If I later find I need more special cases than this - and only it - I re-factor into something better. There are usually 3 approaches I'll take:

  • Refactor any exclusive substate-representing flags into enums. eg. enum { PRE_MOVE, MOVE, POST_MOVE } and add the transitions wherever needed. Then I can check against this enum where I used to check against the boolean flag. This is a simple change but one which reduces the number of things you have to check against, allows you to use switch statements to manage behaviour effectively, etc.
  • Switch off individual subsystems as you need to. If the only difference during the battle sequence is that you can't move pieces, you can call pieceSelectionManager->disable() or similar at the start of the sequence, and pieceSelectionManager->enable(). You still essentially have flags, but now they're stored closer to the object they control, and you don't need to maintain any extra state in your game code.
  • The previous part implies the existence of a PieceSelectionManager: more generally, you can factor out parts of your game state and behaviour into smaller objects that handle a subset of the overall state in a coherent way. Each of these objects will have some of its own state which determines its behaviour but it's easy to manage since it's isolated from the other objects. Resist the urge to allow your gamestate object or main loop to become a dumping ground for pseudo-globals and factor that stuff out!

Generally speaking I never need to go any further than this when it comes to special-case substates, so I don't think there is a risk of it 'quickly getting out of hand'.

share|improve this answer
    
Yes, I imagine there's a line between going all-out with states and just using a bool/enums when appropriate. But knowing my pedantic tendencies, I'm probably going to end up making almost every state its own class. –  vargonian Jun 7 '11 at 7:02
    
You make it sound like a class is more correct than the alternatives, but remember that is subjective. If you start creating too many small classes for things that can be represented more easily by other language constructs then it can obscure the intent of the code. –  Kylotan Jun 7 '11 at 10:39

http://www.ai-junkie.com/architecture/state_driven/tut_state1.html is a lovely tutorial to game state management! You can use it either for game entities or for a menu system like above.

He starts teaching about the State Design Pattern, and then goes on to implement a State Machine, and successively extends it further and further. It's a very good read! Will give you a solid understanding of how the whole concept works and how to apply it to new types of problems!

share|improve this answer

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.