Creating a Game-State System

Creating a Game-State System

An Introduction

Take a look around. Every game that is currently on the market — and probably 75% of all amateur games made — feature some kind of menu system. Oddly though, one of the most common questions that I come across from people looking to make their own games, is how exactly do you get that effect? And not just from a conceptual standpoint, how do you implement a menu system that can take care of loading systems, splash screens, menu options, and above all just rendering the game?

Well, the hope is that this tutorial will clarify just that. Most of the examples will be in pseduo-code format, but if you have any questions on implementation feel free to ask me or any of our officer team. Rest assured though, the examples that I will provide should be enough to start coding up your own game state system.

Primer

The basic premise behind any game, is that there’s a continuous loop constantly running throughout the program — and it’s basically up to you to decide what happens within it. This process is commonly referred to as the game loop, and it will normally handle all of the rendering, sound, music, physics, and whatever else is happening in your game.  Simply put, the game-state system is designed to allow control in what gets rendered, what inputs do and do not get processed, what music / sounds get played, and whatever else you might want to control throughout the life of your program.

Think of the game-state system as a giant switch-case block within your game loop. For example:

Example 1
void Game::gameLoop()
{
	switch(gameState->gamestate)
	{
		case gameState->LOADINGSCREEN:
			break;

		case gameState->ENGINESCREEN:
			break;

		case gameState->INTROSCREEN:
			break;

		case gameState->MENUSCREEN:
			break;

		case gameState->SINGLEPLAYER:
			break;

		case gameState->MULTIPLAYER:
			break;

		case gameState->KIOSK:
			break;

		case gameState->CREDITS:
			break;
	}
}

The code above shows a skeleton function for a game-loop. The case’s obviously have no true implementation at the moment, but that is irrelevant. Somewhere outside of this statement in other game / engine code, another function could trigger the changing of a game-state. When that happens, something like this could occur:

Example 2
void Game::gameLoop()
{
	switch(gameState->gamestate)
	{
		case gameState->LOADINGSCREEN:

			/*
			* The game runs a load functions for game components
			* When the function finishes, it triggers our gameState
			* object to go to the next game state which is ENGINESCREEN
			* as seen below
			*/

			break;

		case gameState->ENGINESCREEN:
			break;
	}
}

At this point, instead of rendering our loading screens, the game could now be rendering a splash screen, playing new music, and doing whatever else that was inside the next case statement. And there you go, you have your first state change withing your game.

In C++, the switch-case statement can only accept integer types for the switch case statement — lame. Lucky for us however, we treat anything we want as an enumeration, which is readable to us, and internally represented as an int value.

Example 3

class GameState
{

public:
	static GameState* getInstance();

	enum State { LOADINGSCREEN, ENGINESCREEN, INTROSCREEN,
	 MENUSCREEN, SINGLEPLAYER, MULTIPLAYER, KIOSK,
	 CREDITS, INSTRUCTIONS};

	int gamestate;

	~GameState();

protected:
	GameState();

	GameState(const GameState&);
	GameState& operator= (const GameState&);

private:
	static GameState* instance;
};

The code above was taken directly from our PHASE project, so for now ignore everything in the code except for the enum State statement and int gamestate.

Whew, done

The great thing about the state system is that it can be applied to many problems in game programming. For example, a great way to manage weapons in a first person shooter, would be to make a state machine like our previous examples, but instead apply it to switching through an inventory of your arsenal using the mouse wheel or number keys. You could even have nested state systems to handle more complicated tasks — such as loading levels in a particular game-state (I.E. Singleplayer mode):

Example 4

void Game::gameLoop()
{
	switch(gameState->gamestate)
	{
		case gameState->SINGLEPLAYER:
			switch(Level->level)
			{
				case mouse->LEVELONESELECT:
					levelOne->render();
					break;

				case mouse->LEVELTWOSELECT:
					levelTwo->render();
					break;

				case mouse->LEVELTHREESELECT:
					levelThree->render();
					break;
			}
			break;
	}
}

And there you have it folks, a working game-state system :)