Game State Control

Started by
32 comments, last by Bob Janova 17 years, 7 months ago
Here is my State Manager Class I made. It's based off of the state manager used in Programming RPGs With DirectX. It uses function pointers for the game states. In the book they used a stack for the state manager, which I found as a stupid way to do it as I would have to try and keep track of which states are where in the stack so that I would know which state would show next once I popped one off. Personally I think the stack is a dumb model to follow for a state manager, but what do I know, I'm still a beginner. I've only made a tetris clone and am working on a 2D space invaders meets starfox type of game. But anyways, here is the code for my state manager class; it is written in C++. Feel free to use or change the code freely.
/*****************************************************************************//* 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.
-Dan- Can't never could do anything | DansKingdom.com | Dynamic Particle System Framework for XNA
Advertisement
Quote:Original post by deadlydog
In the book they used a stack for the state manager, which I found as a stupid way to do it as I would have to try and keep track of which states are where in the stack so that I would know which state would show next once I popped one off.


I'm confused - what would you have to keep track of in a stack? You pop off the topmost state, and whatever is under it becomes the top. That's exactly why I use a stack in my game. Keeping track of the current state is one thing, but I need to know what state(s) came before it to return to.
Quote:Original post by Anonymous Poster
Quote:Original post by deadlydog
In the book they used a stack for the state manager, which I found as a stupid way to do it as I would have to try and keep track of which states are where in the stack so that I would know which state would show next once I popped one off.


I'm confused - what would you have to keep track of in a stack? You pop off the topmost state, and whatever is under it becomes the top. That's exactly why I use a stack in my game. Keeping track of the current state is one thing, but I need to know what state(s) came before it to return to.

I ran into many problems like this when using a stack for my game state manager. For example, let say the game starts by entering the MainMenuState, then the player chooses to play and the game goes into the PlayState. Then the player pauses the game and is presented with the standard pause menu (things like continue, return to main menu, quit game, etc), I'll call this the PauseState. So now the stack has 3 states on it, PauseState on top, PlayState in the middle, and MainMenuState on the bottom. Now if the player chooses to quit the current game and return to the main menu, you can't just pop off one state as that will take them back into the PlayState. You would have to pop off 2 states to return to the MainMenuState. But depending on your game, it could be more than 2 states that you have to pop in order to return to the main menu; it could be 3 or 5 states; who knows. This is what I meant by having to keep track of what all is in the stack. This is a very simple example, but you can see that if you have a lot of game states and different ways to get to them, knowing how many states to pop can be tough. This is what inspired me to make my game state manager. Just specify what state to call and it's called.

The stack I guess would work fine if you only have a few high level game states, such as MainMenu, Play, Pause. My game state manager is more useful for lower level game states, such as sub-menus, or when you have many states. It basically has the advantages of using your basic switch statement to control game states, without the overhead of the code potentially having to look through all game states to find the one that should be called. But you do make a good point about knowing what game state called the current one. I'll add that functionality to my state manager once I get time.
-Dan- Can't never could do anything | DansKingdom.com | Dynamic Particle System Framework for XNA
Deadlydog, I'd say it was your system which is only suitable for simple games (not that that's a bad thing, necessarily). If the game states are complex and require unloading so as not to leak resources, you need to know which states are present in order to clean up properly. (For example, imagine the player enters the pause menu in the middle of an FMV sequence, thus the game stack is In Play->FMV. Now, if the player chooses to quit the game and return to the main menu, both those states probably need to be cleaned up properly – but obviously you can't clean up before entering the pause menu as that would make it impossible to unpause!)

'Keeping track' of what's on the stack is a case of reading myStack.length or similar. (In your 'clean up everything and go to the main menu' example, that's just while(state = stack.pop()) state.cleanUp(); stack.push(MainMenuState).

This topic is closed to new replies.

Advertisement