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.
Answer
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;
}
No comments:
Post a Comment