Kiedyś natknąłem się na artykuł, który dość elegancko rozwiązuje twój problem. Jest to podstawowa implementacja FSM, która jest wywoływana w głównej pętli. W dalszej części tej odpowiedzi opisałem podstawowe podsumowanie tego artykułu.
Twój podstawowy stan gry wygląda następująco:
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();
};
Każdy stan gry jest reprezentowany przez implementację tego interfejsu. W twoim przykładzie Battlechess może to oznaczać następujące stany:
- animacja wprowadzająca
- menu główne
- animacja konfiguracji szachownicy
- ruch gracza
- animacja ruchu gracza
- animacja ruchu przeciwnika
- menu pauzy
- ekran końcowy
Stany są zarządzane w silniku stanu:
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();
// ...
};
Zauważ, że każdy stan potrzebuje w pewnym momencie wskaźnika do CGameEngine, więc sam stan może zdecydować, czy należy wprowadzić nowy stan. Artykuł sugeruje przekazanie CGameEngine jako parametru dla HandleEvents, Update i Draw.
W końcu twoja główna pętla zajmuje się tylko silnikiem stanu:
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;
}