Game State

posted in N.O.W.
Published December 03, 2011
Advertisement
Well the good news is that I was given a reprieve on the 3 month overtime death march for two weeks. Unfortunately for my kick ass progress it looks like we will be starting it up harder and longer within the week. I was able to get some decent work done to my art assets. I have been looking into picking up the Genius G-Pen F610 drawing tablet to speed up art production, once I get a few dollars ahead. If anyone has any input on this little guy let me know.

Anyway, a few weeks ago I saw a blog on Gamasutra about using design patterns in your game. I personally think that is an awesome topic with loads of possibilities. The gentleman writing the blog briefly touch on a few. Now due to the nature of design patterns I personally think that it is hard to say whether someone else' implementation is right or wrong. As long as your implementation does not cause poor performance and solves your problem without causing other problems I call it a win.

[heading]What three things do almost ALL interactive software have in common?[/heading]

So before we get started discussing what is a design pattern I would like you to consider something. What 3 things do practically every piece of interactive software have in common? We'll come back to this later.

A design pattern is a solution to a problem that occurs over and over in software. I believe the idea was originally taken from the world of architecture. The awesome thing about design patterns is that they are very loose by nature.

[subheading]What the hell does that mean!?!?[/subheading]

Good question! it means that aside from some generalized concepts there is no cut and dried, "This is the only correct way!" The initial reaction is probably along the lines of, "how is that helpful in any way if the solution is not the same for everyone?" And there in lies the beauty and the power of design patterns. In software we run into problems that are pretty much the same things over and over. The real problem is that they are not the exact same problem over and over but have many of the same aspects. The solution to that problem needs to be specific to that problem in order for it to be successful.

What some really smart people did was put together groups of patterns of problems and solutions and called them design patterns. These problems are somewhat generalized as are the solutions. The design patterns also define the pros and cons of using them! Holy cow! That means there is a catalog in the mystic aether that solves all of our problems!

[subheading]No not really. But sort of.[/subheading]

There is a catalog of patterns and it is getting larger everyday and some of them begin to get more specific to a problem. Design patterns are not a silver bullet. They do not solve every problem and using as many as you can in one project will probably be more of a pain and less useful than it is really worth. To borrow a phrase.. We need to use intelligent design. One of the basic ideas in the patterns are abstraction and encapsulation. By adding some abstraction we can handle more problems with the same methods. By encapsulating things that don't change we can implement them across the board and change them across the board too.

I am doing a huge disservice by over generalizing but you will need to do a bit of research on your own to really get the nitty gritty of it all.

All of that being said I would like to talk about the State Pattern today. The state pattern describe the state of an object. So it's not just a clever name. Ironic isn't it. It consists of an abstract class using only virtual functions to describe the behavior of the class. Then a concrete class implements those virtual methods according to it's intended function. Two short sentences but there really is a lot of stuff said between them. Check out the above link to see a diagram.

[subheading]My problem[/subheading]

Yes I know... I know I have more than one and not all of them are psychological. Seriously though. When I first started making games I tried keeping track of all of my states using enumerations and switch/case blocks to handle them. This works great! For very small programs with very few states. Now take you average small game, like a tetris clone for example. Off the top of my head I can think of about 14 different states for the game as follows:

  • New Game
  • Game Active
  • Game Over
  • Title Screen
  • Load Images
  • Load Sounds
  • Load Cut Scenes
  • Intro
  • Level Completed
  • Level Loading
  • Level Clearing
  • Display High Scores
  • Instructions
  • Enter Initials for High Score

So this is for a very basic game. Each of these game states needs to be handled differently. You are going to want to handle input, rendering and game updating differently for each state. Think about this... A switch/case block to handle rendering 14 different states. A switch/case block to handle updating 14 different states. A switch/case block to handle input for 14 different states. That just sounds messy thinking about it. That is okay. I am not going to post any code from some ancient project show casing this. But that was how I used to do it.

Remember when I asked what three things almost all interactive software have in common? They all do these three things:

  1. Handle input according to the current state
  2. Update the software model according to input and state
  3. Render information to the screen according to the input and current state

And you know what. Every piece of software does this for every pass through the main loop. I can also bet that you saw the one reoccurring theme in those three things. The word state (I know. I made them bold font). The point is this is one of the most perfect candidates for the state pattern. We can use abstraction to make a template that will handle the functions and concrete classes of that template to encapsulate the functions for each separate state.

So what does this look like?

[subheading]The Game State[/subheading]


#pragma once

class GameState{

public:



virtual void HandleInput(void) = 0;
virtual void Update(void) = 0;
virtual void Render(void) = 0;
virtual void onEnter(void) = 0;
virtual void onExit(void) = 0;

GameState(void){};
virtual ~GameState(void){};

};


So there are a few things here that I didn't mention to begin with. OnEnter and OnExit. More than likely you will find that as you change states within your game there are specific tasks that need to be performed when you enter or leave any one state. Maybe loading media assets upon entering a state and releasing them just as you exit the state? Depends on your state and it's function, but they will come in handy.

So the header for a concrete game state would look something like:


#pragma once
#include "GameState.h"
#include "Game.h"

class stateINTRO :
public GameState
{
public:
stateINTRO(Game *g);
~stateINTRO(void);

void HandleInput(void);
void Update(void);
void Render(void);

void onEnter(void);
void onExit(void);

private:
Game *game;
bool completed;

};


Pretty simple now you can handle input, update your game model and render independently for each game state. Just to give you an idea of how this simplifies the main game loop here is the main.cpp from bash.


#include "SDL.h"
#include "Game.h"

int main( int argc, char* args[] )
{
//Try to create a new game
Game *bash = new Game();

//Check to make sure the game systems initialized correctly
if(bash->Initialized){

//While the game is not requesting to quit run the game
while(!bash->ExitRequested){

bash->HandleInput();
bash->Update();
bash->Render();

}

}

//Clean-up the memory used by the game
delete bash;

return 0;
}


So yes there is a lot more crap stuffed into the game object. BUT the game object is the game engine. It only provides services like managers for input, audio and graphics. It also provides functions that span across many different states. This in itself makes the game engine a bit more reusable and flexible. Meaning the engine is not the game.

So the interface to the game states in the game engine it could look something like:


void Game::HandleInput(void){

//Always handle the user trying to quit the game
if(this->kb->isKeyDown("QUIT")){
this->ExitRequested = true;
}

//Let the current state handle the other input
this->currentState->HandleInput();
}

void Game::Update(void){
//Update the keyboard so the current state doesn't have to
this->kb->Update();
//Update the gamepad
this->pad->update();
//Allow the current state to chose what systems to update
this->currentState->Update();
}

void Game::Render(void){

//clear the drawing surface
SDL_FillRect( this->screen, NULL, GColor::BLACK);

//Let the current state draw what it needs to
this->currentState->Render();

//Update Screen
SDL_Flip( this->screen );

}

void Game::SetState(GameState *s){
this->currentState->OnExit();
this->currentState = s;
this->currentState->OnEnter();
}



[subheading]Pro's and Con's[/subheading]

That about sums of the majority of the game state, or at least my current implementation. But as with all design patterns there are drawbacks associated with them.

What are they?

Class Explosion
The major problem with this pattern is that EVERY SINGLE STATE requires a new class. Ouch. That can get pretty messy. You are basically trading a messy class for a mess of classes. With proper naming conventions this will help mitigate the organization problem. But it also makes it hard to see the design as a whole without creating a diagram of the state machine. BTW I recommend doing this even if it is just on paper.

Brittle Interface
Yep it's true. Without proper foresight in your design this pattern can muck things up quick. Why? Because if you need to add another interface in the pure virtual class you need to add it to EVERY concrete class. This is also why I recommend putting a design on paper or in UML prior to tearing into it. That being said I think as far as the game state goes, it doesn't get much more basic than what I presented.

So the pro's! What are the good things about this and why is it even worth making a metric ton of classes?

Harder to Break
It is harder, though not impossible, to break any other state by making changes to any single state using this pattern. You have encapsulated your functionality and cannot muck about with any other state.

Distributable
Really big plus here. This pattern forces the programmer to design to an interface. Meaning since you only have 3 functions you need to interface to the game as a whole you can have multiple programmers working on separate states all at the same time and not have to worry about them breaking another state.

Organization
Although it is hard to see the whole picture it is doable. It is very very hard to look at one single class that handles every single state of input, logic and rendering. Piecing those bits into one manageable whole is harder than being able to look at one file for one state and knowing what it does.


[subheading]No Silver Bullet[/subheading]

This is no silver bullet. This is not the thing you want to use all over your program unless you are prepared to deal with the negative aspects of this little devil. But it does solve some specific problems and it does it well enough in my opinion.

I highly recommend two books for further reading.

Head First Design Patterns
This book is truly awesome! It is a pleasure to read injecting humor and thoughtfulness into every chapter. This is not your typical dry stereo instructions programming book. Go buy it today.

Design Patterns: Elements of Reusable Objects
This is the original gang of four book that everyone mentions. It really is an excellent read and presents the topics in a well thought out manner. I would recommend reading this book after you have read Head First Design Patterns.
0 likes 6 comments

Comments

yckx
I have something close to this, but your implementation is a little less bare-bones than mine. I think I may have to revise my GameState class.
December 04, 2011 01:30 AM
Wrathnut
:) Lol I thought I had made this as bare bones as I could!
December 04, 2011 05:53 AM
yckx
I was missing the [b]OnEnter()[/b] and [b]OnExit()[/b] methods.
December 05, 2011 04:29 PM
zdlr
"[color=#1C2837][size=2]You are basically trading a messy class for a mess of classes."[/size][/color]
[color=#1C2837][size=2]
[/size][/color]
[color=#1C2837][size=2]True, but each one of those classes should have very directed reason for existing - as well as being small. Far rather more classes than a big ball of mud.[/size][/color]
[color=#1C2837][size=2]
[/size][/color]
[color=#1C2837][size=2]The Brittle Interface problem can be solved by using the Interface Segregation Principle: splitting an otherwise large interface into multiple parts, grouped logically.[/size][/color]
[color=#1C2837][size=2]
[/size][/color]
[color=#1C2837][size=2]Furthermore, you could make a CompositeGameState and have that be the default applied to the Game. Then, the other concerns that you have inside the game (detecting the quit button was pressed, updating the keyboard and pad, plus clearing / flipping the screen) can also be factored out - possibly into a DefaultGameState or something more descriptive.[/size][/color]
December 06, 2011 03:37 PM
Wrathnut
As far as the interface goes; If your interface is that large or too disjointed across multiple states, you should really consider using a different pattern or define what is a state more precisely. The idea is to keep it as simple as possible and no simpler.

I had not thought about a composite game state. I kind of like that idea, it has some real potential to it!
December 07, 2011 12:06 PM
zdlr
Indeed, think about the 'Pause' state, for example. For rendering, it needs to do whatever the normal in-game state does, but with a big 'PAUSED' text on the middle of the screen It still needs to poll input, but it probably doesn't need to update any game logic (moving actors, etc.) and it should only respond to the UNPAUSE command - FIRE, etc., should be disabled. So, it's a specialization of the normal game state, but by using a CompositeGameState, we can keep the concerns separate while [b]still[/b] promoting reuse.

What you end up with, in essence, is a game state graph.
December 07, 2011 04:11 PM
You must log in to join the conversation.
Don't have a GameDev.net account? Sign up!
Profile
Author
Advertisement

Latest Entries

Advertisement