Page 1 of 1

State Management Using a state manager Rate Topic: ***** 4 Votes

#1 sparkart  Icon User is offline

  • D.I.C Addict
  • member icon

Reputation: 113
  • View blog
  • Posts: 691
  • Joined: 16-February 09

Post icon  Posted 06 August 2009 - 03:44 PM

Note:
Before continuing with this tutorial please ensure that the following concepts are familiar to you: pointers, vectors, data stacking, classes



State management is a great way to keep things organized when certain things happen based on certain occasions. Let's take a game as an example. Games run in a continuous loop and perform things based on its current state.

Here's a sample of what happens in a game:
InitializeGame();

while (quit != true)
{
	RunGame();
}

EndGame();



Now let's add states to the picture:
InitializeGame();
state = stateMainMenu;

while (state != stateQuit)
{
	switch (state)
	{
		case stateMainMenu:
			RunMainMenu();
		break;

		case stateGameplay:
			RunGameplay();
		break;

		case stateOptions:
			RunOptions();
		break;

		case statePauseScreen:
			RunPauseScreen();
		break;
	}
}


Using a state manager to handle your application's state gives you more flexibility. For this tutorial I will demonstrate how to implement it in C++ using classes.

The first thing we'll want to do is construct an abstract state class that every other state will derive off of. This is how it will look:
class GameState
{
public:
	virtual void Init()=0;
	virtual void Cleanup()=0;

	//Pushing and popping states causes pausing/resuming.
	virtual void Pause()=0;
	virtual void Resume()=0;

	virtual void GetEvents()=0;
	virtual void Update()=0;
	virtual void Display()=0;
};



Don't forget your inclusion guards. I omitted them for this tutorial to keep things short.

Our state class has seven methods: Init(), Cleanup(), Pause(), Resume(), GetEvents(), Update(), and Display()
They are all pure virtual methods, meaning they must be implemented in the derived class.

Before moving on I will explain each method a bit:

Init()
- This method is executed at the beginning of each state.

Cleanup()
- This is the complete opposite of the Init() method. It happens when a state is left (popped off the state stack).

Pause() & Resume()
- These states add additional functionality. It allows you to execute code when you wish to pause/resume a state.

GetEvent()
- This method receives input and performs actions as a response. This allows different things to be done based on the current state. For instance, pressing the Escape key while at the "Main Menu" screen would be different from pressing it in the "Pause Screen".

Update()
- This happens each frame.

Display()
- Like the Update() method, this also happens each frame. Having a separate displaying method for each state instead of a single general one allows us to draw different things based on the current state.
---------------------------------------------------------------------------------------------------------------------------------------

The next step to do is to define the state manager which will be responsible for handling all the "state magic":
// Use the STL vector to hold our states.
#include <vector>

#include "GameState.h"

class GameStateManager
{
public:
	void ChangeState(GameState* state);
	void PushState(GameState* state);
	void PopState();
	void Clear();

private:
	std::vector<GameState*> m_states;
};


You'll notice that we have three methods responsible for manipulating the current state: ChangeState(), PushState(), & PopState().

ChangeState() will remove the current state from the stack and add a new state to the end of the stack. Using a stack for state management allows for greater flexibility.

PushState() will pause the current state and add a new state to the end of the stack.

PopState() will remove the last state on the stack and set the current state to the previous state on the stack.


And for our state stack we'll be using a vector of GameState pointers.

The implementation for our state manager is as follows:
#include "GameStateManager.h"

void GameStateManager::Clear()
{
	while (!m_states.empty())
	{
		m_states.back()->Cleanup();
		m_states.pop_back();
	}
}


void GameStateManager::ChangeState(GameState *state)
{
	// Cleanup the current state.
	if (!m_states.empty())
	{
		m_states.back()->Cleanup();
		m_states.pop_back();
	}

	// Store and init the new state.
	m_states.push_back(state);
	m_states.back()->Init();
}


/*
 * Pause the current state and go to a new state.
 */
void GameStateManager::PushState(GameState *state)
{
	if (!m_states.empty())
		m_states.back()->Pause();

	m_states.push_back(state);
	m_states.back()->Init();
}


/*
 * Leave current state and go to previous state.
 */
void GameStateManager::PopState()
{
	if (!m_states.empty())
	{
		m_states.back()->Cleanup();
		m_states.pop_back();
	}

	if (!m_states.empty())
		m_states.back()->Resume();
}




There are many ways to implement a game state design, but this is a way that I write them for projects that I do as it provides for flexibility while maintaining simplicity. Hopefully this tutorial was informative for you.

Is This A Good Question/Topic? 4
  • +

Replies To: State Management

#2 Flickayy  Icon User is offline

  • New D.I.C Head

Reputation: 0
  • View blog
  • Posts: 7
  • Joined: 12-August 12

Posted 11 October 2012 - 04:11 PM

Thanks for this tutorial! It really helps clear a few things up about state management. ^_^
Was This Post Helpful? 0
  • +
  • -

Page 1 of 1