/*****************************************************************************//* CStateManager.h (and CStateManager.cpp) *//* Written by Daniel Schroeder *//* Created on July 15 2005 *//* Last Updated August 25, 2005 *//*---------------------------------------------------------------------------*//* The CStateManager class is used to keep track of Game States, or any *//* other States. You can set a function as the current State by calling the *//* SetState() function and the function you want to calls name. In order to *//* set a function as a State however, the function must follow a specified *//* format (see below). Then when Process() is called, it calls the set *//* States function. If the State is being processed for the first time, it *//* calls the function with a Purpose of eInitialization. If the State is *//* being shutdown it calls the function with a Purpose of eShutdown. If the *//* State was previously initialized, but another State was Set, and now the *//* first State is being Set again, the function will be called with a Purpose*//* of eReturning. Otherwise, the funciton will be called with a Purpose of *//* eNormal. *//* *//* State functions must have the following format: *//* static void FunctionName(void* vpOptionalData, EPurpose ePurpose); *//* *//* Examples of State functions: *//* static void GameStateStartMenu(void* vpOptionalDataToPass, *//* EPurpose ePurpose); *//* static void GameStatePlay(void*, EPurpose); *//* *//* Example of Initializing and Setting a State: *//* CStateManager cStateManager; *//* cStateManager.SetState(GameStateStartMenu, vpOptionalDataToPass); *//* *//* Then just call cStateManager.Process() each game loop, and if it returns *//* false then exit the game loop. First the GameStateStartMenu() function *//* would be called. Then from within the GameStateStartMenu() you could call *//* for example, cStateManager.SetState(GameStatePlay()); when the player *//* selects to play the game, and on the next game loop the GameStatePlay() *//* function will be called. *//*****************************************************************************/#ifndef CSTATE_MANAGER_H#define CSTATE_MANAGER_H// Types of purposes for calling a functionenum EPurpose{ eInitialize, // Used if GameStateFunction is being called for the first time eNormal, // Used as the Normal GameStateFunction call eShutdown, // Used if GameStateFunction is being called for the last time eReturning // Used if GameStateFunction was running, then we left it, and now coming back to it};// Holds States Function and pointer to Next Statestruct SGameState{ void (*Function)(void* vpFunction, EPurpose ePurpose); void* vpOptionalData; SGameState* spNext;};// Manages the Game Statesclass CStateManager{ public: CStateManager(); // Constructor ~CStateManager(); // Destructor // Set the State Function to call void SetState(void (*Function)(void* vpFunction, EPurpose ePurpose), void* vpOptionalDataToPass = 0); // Set the Next State Function to call, but Shutdown the current one first // NOTE: Do not use to Set the first State Function (since there will not yet be a Current State to Shutdown) void ShutdownCurrentStateAndSetNextState(void (*Function)(void* vpFunction, EPurpose ePurpose), void* vpOptionalDataToPass = 0); // Call the State Function // Returns false when no State Function to call bool Process(); // Returns the Current State Function the State Manager is processing // Returns 0 if no Current State Function is being processes void* ReturnCurrentStateFunction() const; // Shutsdown all State Functions. Once done, Process() will return false when called void ShutdownAllStates(void* vpOptionalDataToPass = 0); private: // Returns the State if it is in the list // Returns 0 if State not in list SGameState* FunctionIsInList(void (*Function)(void* vpFunction, EPurpose ePurpose)) const; // Removes a State from the List // Returns true if State was in List and Removed, False if not bool RemoveStateFromList(SGameState* spStateToRemove); // Moves a State to the front of the List bool MoveStateToFrontOfList(SGameState* spStateToMove); SGameState* mspCurrentStateToProcess; // Current State Function to call SGameState* mspNextStateToProcess; // Next State Function to call SGameState* mspHead; // Points to Head of State List EPurpose mePurpose; // Holds what purpose the Current State should have EPurpose meNextStatePurpose; // Holds what purpose the Next State should have};#endif
I was trying to post the .cpp as well, but it is too long and gamedev just times out when I try and submit it.
Basically your "Game States" are really just functions that are called via function pointers. So if you have a GameMenu() function just call cStateManager.SetState(GameMenu()) and that function will be called on the next game loop. This allows you to jump from one GameState to another at anytime from anywhere, instead of having to push and pop game states on and off of a stack. Also, when changing states you have 2 options: 1 - to shutdown the current state before calling the next state (so the next game loop would actually call the current state with a Purpose of eShutdown, then on the next game loop the next GameState is called), or 2 - simply set a new GameState to run next, in which the current GameState is left as is. This allows you to return to this GameState at anytime later simply by setting it as the current state, and the function will pick up from where it left off automatically. This type of approach works better than your basic switch() statement or bunch of if/else cases if you have a lot of game states, because instead of the code having to check a bunch of if cases every game loop, it simply makes one function call. If you want to use my class you can download my game in progress's source code from here and just take the CStateManager.h and CStateManager.cpp files. You can also check out my game to see how I use it. Sorry about that little rant of my State Manager. If you have a better way than how I do this, or if you like my way let me know. Thanks.