Hello,
Before speaking of the design itself, let's see some stuff about the code.
- if you pass a SDL_Event to your process_event method it often suppose that the event structure is already initialized. It means that your cPlaystate::process_event() don't have to poll the events - this task should be done by the game state manager.
- there is no real difference between process_event() and run(). You are probably going to call process_event() and run() subsequently. Moreover, process_event() has to modify the game state, just as run(). As a consequence, removing run from your code should be possible.
Now, from a design point of view, your game state system lacks a lot of functionalities.
First, the game state manager is responsible for nothing. It only stores the current game state, but since you need to tell him what is the current game state I can safely assume that the object that will be responsible for holding the manager will also know what is the current game state.
Nevertheless, a game state manager is still needed, so let's see what it should do.
- it should be responsible for updating the current game state - it also mean that we must have a way to setup the startup game state.
- we should not take care of what is the current game state from outside
- since we don't know what is the current game state from the outside, we need to know when we need to exit.
// I removed all the stuff we don't really care from a design point of viewclass GameStateManager{ GameState *mCurrentGameState;public: // this method will poll the events and update the game state void updateOnce(); // this method is called by the owner to check wether we need to quit or not bool canExit(); // of course, we need to setup the startup game state void setGameState(GameState *gameState);};
Now, let's do the game state class. What will be the needed actions ?
- we must be able to update the game state in response to a particular event
- because a particular action might modify the game state (from example, from the normal game to the equipement game state), the game state is also responsible fro telling to the game state manager which is the next game state (either itself or anotehr one)
- we can (optionnally) implement some kind of enter() and leave() methods that will be called when we enter or leave a particular game state
// this is an abstract classclass GameState{public: // update the game state virtual void updateOnce(const SDL_Event& event) = 0; // the function is called by the manager to get the next game state virtual GameState *getNextGameState() = 0; // optional enter/leave methods virtual void enter() = 0; virtual void leave() = 0;};
Now we can implement our game state manager
void GameStateManager::updateOnce(){ // we don't have anything to do if we don't have any game state if (mCurrentGameState) { SDL_Event event; GameState *gameState; // poll for an event - I'm not sure how SDL_PollEvent works, // I assume that it returns true if an event has been catched. if (SDL_PollEvent(event)) { // update the current game state mCurrentGameState->updateOnce(event); // get the next one gameState = mCurrentGameState->getNextGameState(); // set the new game state setGameState(gameState); } } }bool GameStateManager::canExit(){ // if we don't have a current game state then we can // assume that we want to quit return (mCurrentGameState == NULL);}void GameStateManager::setGameState(GameState *gameState){ // if the new game state is not the current one, we'll have // some additional work to do if (gameState != mCurrentGameState) { // leaving the current game state mCurrentGameState->leave(); mCurrentGameState = gameState; if (mCurrentGameState) { // nextState can be NULL; if it is not NULL, we must // enter the new current game state mCurrentGameState->enter(); } }}
Using this game state manager is rather simple:
GameState *Game::createGameStates(){ // create the different game state GameStateMain *mainGameState; GameStatePlay *playGameState; GameStateHiScore *hiscoreGameState; GameStateNewHiScore *newHiscoreGameState; GameStateCredits *creditsGameState; creditsGameState = new GameStateCredits(); hiscoreGameState = new GameStateHiScore(); newHiscoreGameState = new GameStateNewHiScore(); playGameState = new GameStatePlay(); mainGameState = new GameStateMain(); // setup the game state graph mainGameState->setPlayGameState(playGameState); mainGameState->setCreditsGameState(creditsGameState); mainGameState->setHiscoreGameState(hiscoreGameState); playGameState->setNewHiscoreGameState(newHiscoreGameState); playGameState->setBackGameState(mainGameState); creditsGameState->setBackGameState(mainGameState); hiscoreGameState->setBackGameState(mainGameState); newHiscoreGameState->setBackGameState(playGameState); // finally, return our startup game state return mainGameState;}void Game::init(){ GameState *startupGameState; // get the startup game state startupGameState = createGameStates(); // now, set up the game state manager mGameStateManager.setGameState(startupGameState);}// the main game loopvoid Game::mainLoop(){ do { mGameStateManager.update(); } while (!mGameStateManager.canEcit())}
Of course, one can even make the Game class generic by creating a GameState abstract factory that will be responsible for creating the main game state (it will do everything in createGameStates())
This post was rather long, but I hope you found it informative [smile]
Regards,