[C++] Game states, game states, game states!!!

Started by
25 comments, last by Kylotan 15 years, 2 months ago
Not read the thread in too much detail, but:

Say you have an abstract base class, state:

class State{  public:    virtual void Run() = 0;};


and from this you have GameState, MenuState, and whatever other states you have (pause?)

class GameState : public State{  public:    virtual void Run();};class MenuState : public State{  public:    virtual void Run();};



In your main application class/global var/whatever, you have, for example

State* pState;


then in the main loop

while(!quit){  pState->Run();}


Now to change state at any point you would assign a pointer to an instance of one of the derived classes to pState, and the main loop would call the corresponding run function.

e.g.
void onMenuCall(){  pState = GetMenuState();}void onResumeGame(){  pState = GetGameState();}


where GetMenuState and GetGameState returns a pointer to a MenuState or GameState object initialised somewhere else by you (maybe created at startup).

This also allows you to create a deeper tree of inheritence if, say, you have more than one state available within a game state, or something.
________________________________Blog...
Advertisement
I do the same as Ibasa has stated: A Stack of (pointers to) GameState instances which all derive from a base GameState interface. This gives each state nice encapsulation, as they are entirely self-sufficient -- All they need to do is run their loop and recieve input messages from the application class. It also fits nicely with resource management -- when each gamestate is made it loads its resources, and when it is popped off the stack, it destroys its resources. Optionally, you can also "suspend" its resources when it is not at the top of the stack (or more precisely, when it won't be drawn -- you may wish to allow states lower in the stack to run in the background), and then restore them when the state is run again.

Gamestates change because each state knows to which states it can transition, and under what circumstances it should do so -- The map state is transitioned to the dialog state when the player talks to someone for instance.

If you have a resource manager that prevents duplicate resources from being loaded this system works wonderfully -- As long as you load resources from the state coming into focus before releasing resources from the state losing focus, then you will never load resources extraneously.

I'm actually working on publishing an article here on GameDev.net about my resource manager, and after that I'd like to publish on on the state system I've described above.

throw table_exception("(? ???)? ? ???");

Quote:Original post by Ravyne
I'm actually working on publishing an article here on GameDev.net about my resource manager, and after that I'd like to publish on on the state system I've described above.


I'd sure read that! :D
I feel as if I am coming really close to a solution now. I just have some questions.

First of all, the site you directed me to Shadowwoelf was the one I thought it was. I looked deeper into it and tried testing it out. All I did was place the given class definitions before main() on main.cpp exactly as they appeared on the website and it did not compile. I think the reason for this was that the class CGameState uses a pointer to and object of the engine class as parameters for several of its member functions while the CGameEngine class definition uses pointers to a CGameState as parameters, as well as containing a vector of CGameStates.

Unless I am sorely mistaken, can't you not use a class before it is declared? This seems to me to create an unsolvable paradox. CGameEngine uses CGameStates but cannot be properly declared unless CGameState is declared and CGameState utilizes CGameEngine but cannot be properly declared unless CGameEngine is declared. What is the solution to this problem, or am I doing things wrong?

edit: I just looked it up and found out what forward declarations are. I feel dumb.

A feel as though once this problem is solved, I should be able to jump right in and start coding some states right away (maybe read up on how to handle vectors first).

Shadowwoelf: The example you posted, of Game->PushState(new WORLD_STATE,ScreenWidth,ScreenHeight); and the others, if I am understanding this right, is this PushState or ChangeState function essentially defined to take a pointer to an object of the state class, and is being called by creating a 'new' object of the WORLD_STATE class (derived from the state class) at a location in the vector? And the vector can handle freeing this memory as necessary?

Thanks again for the major help.
I used to have something similar to the stack-based version
Enabled : Game |      |               | Console-echoDisabled:      | Menu | Console-input |

At the start of the game you push all wanted states on the stack, and then pretty much disable all of them. When you hit the menu key, you enable it.
Requests such as render, update or handleKey would pick the rightmost enabled state. That state could choose to send the request to it's left, handle it or ignore it. So the console-echo could respond to page-up and page down keys to scroll the messsages up and down, but send all other keys to the game. The console-input would trap all requests except the render.
I used this system in lolball.

The problem I faced with this system, and all the stack versions is that they are hard to work with. The control-flow is hard to follow and it's cumbersome to pass information to and from states. I wanted something where this was easy. My main goal was to get a pressed-key from the user(for key-binding) without having to add special cases or resort to globals.
It occurred to me that all stack-based techniques are all essentially recreating the wheel. We already have a stack. So I refactored the game-loop into a class, and every new state push/pop is a function-call since, when you think about it, all pushes should have a pop. The end result is pretty much like this:
if( playGameButtonPressed ){  GameLoop game("start.lvl");  loop(&game);}else if( bindJumpKeyPressed ){  KeyPressLoop kp("hit the jump key");  loop(&kp);  if(kp.hasPressedKey()) jumpKey = kp.getKeyPressed();}


hth
It is occurring to me that, if we are only running a single state at a time, what is the point of a vector? Can we not just have a GameState* member of the GameApplication or GameEngine class that points to whatever state is to be ran? Is the vector a solution to the issue of simply holding the next state, until the current ending state can call it's final render, call uninitialize() or exit() or cleanup() (whatever we are calling it), and then have itself removed from the stack? Or is this not for handling multiple states?

Suppose when I pause the game state, I want the game to continue (in essence you do not have a true pause, you just have your menu open while the game is playing). The menu state could be added to the stack and the game state could call pause();, and the game state could handle and update and render in a manner simply inhibiting your interaction with the game state while the menu state handles the events until it is exited, and the game state resumes as normal. Is this the intended usage of the stack? If not, then from where is its necessity?
I'm going to guess that the vector is used as a convenient place to save your states while they're not active, but I can't speak for the person who posted about it.

I'd imagine that each State would be able to call the previous one if it so desired, similar to how a win32 app's WndProc can call the DefWndProc for the events it's not using. This way, if you're in your Menu state, it can call the Game state first like you wanted, then it would draw its own stuff.

(I've actually never used a state system before, so this is all coming out of my you-know-where!)
I use both an IState interface and a StateManager:

class IState{public:  virtual void Update();  virtual void OnEnter() {};  virtual void OnExit() {};};class CStateManager{public:  IState* GetCurrentState();  void ChangeState(state_id);  // state_id can be a string, enum, ... whatever  void Update();};


Calling for update is just this:

CStateManager manager;// Game loop....manager.Update();....

Change between states is handled by the manager, who holds the information for the current active state. And also you can easily turn this to behave as a stack. Instead of providing a ChangeState, just try with PushState(state) and PopState, and provided the desired underlying implementation.

When calling for a state change, the manager marks itself with the next state to moveon. Then, on the next Update it will call the OnExit of the current state, change the state, and call the OnEnter method of the new state. After that, Update of the active state will be called as normal. Adding new states will not need more code in the game loop. And the enter/exit mehtods provide a built-in mechanism for treating state changes specifically for each state. For instance, you can handle there resources.

And also, don't forget that if your states are really big, and need for more tune, you can use a set of states (with another manager) inside a given state. For instance, having a "minigame" inside the main game.
Quote:Original post by Ruzhyo2000
I feel as if I am coming really close to a solution now. I just have some questions.

First of all, the site you directed me to Shadowwoelf was the one I thought it was.


Yea sorry that was the site I used (And I even made a few game States posts myself here)

Quote:I looked deeper into it and tried testing it out. All I did was place the given class definitions before main() on main.cpp exactly as they appeared on the website and it did not compile. I think the reason for this was that the class CGameState uses a pointer to and object of the engine class as parameters for several of its member functions while the CGameEngine class definition uses pointers to a CGameState as parameters, as well as containing a vector of CGameStates.


I use opengl so I didn't bother trying to compile it so I can't offer you any explanation as to why it didn't

Quote:
Unless I am sorely mistaken, can't you not use a class before it is declared? This seems to me to create an unsolvable paradox. CGameEngine uses CGameStates but cannot be properly declared unless CGameState is declared and CGameState utilizes CGameEngine but cannot be properly declared unless CGameEngine is declared. What is the solution to this problem, or am I doing things wrong?

edit: I just looked it up and found out what forward declarations are. I feel dumb.


I made the same mistake myself when I first started learning.

Quote:
A feel as though once this problem is solved, I should be able to jump right in and start coding some states right away (maybe read up on how to handle vectors first).


Vectors are easy to use really, and there are plenty of websites that will explain them.

Quote:
Shadowwoelf: The example you posted, of Game->PushState(new WORLD_STATE,ScreenWidth,ScreenHeight); and the others, if I am understanding this right, is this PushState or ChangeState function essentially defined to take a pointer to an object of the state class, and is being called by creating a 'new' object of the WORLD_STATE class (derived from the state class) at a location in the vector? And the vector can handle freeing this memory as necessary?


This is an interesting question so I will try to word this correctly, but you should research this yourself.

std::vector<BaseGameState*> States; is a vector of pointers to BaseGameState.
So anything derived from BaseGameState should be allowed into the vector

When calling an object by the keyword new e.g. new WorldState you are putting the object onto the heap which you can only access through pointers.

Then you pass the pointer or copy it to the engine e.g. Game->PushState(new WORLD_STATE,ScreenWidth,ScreenHeight); which will then do whatever it needs done and pushes the pointer on top of the vector.

Hmm I hope my logic is correct... someone correct me if i'm wrong?

Quote: It is occurring to me that, if we are only running a single state at a time, what is the point of a vector? Can we not just have a GameState* member of the GameApplication or GameEngine class that points to whatever state is to be ran? Is the vector a solution to the issue of simply holding the next state, until the current ending state can call it's final render, call uninitialize() or exit() or cleanup() (whatever we are calling it), and then have itself removed from the stack? Or is this not for handling multiple states?

Suppose when I pause the game state, I want the game to continue (in essence you do not have a true pause, you just have your menu open while the game is playing). The menu state could be added to the stack and the game state could call pause();, and the game state could handle and update and render in a manner simply inhibiting your interaction with the game state while the menu state handles the events until it is exited, and the game state resumes as normal. Is this the intended usage of the stack? If not, then from where is its necessity?


If you create your game states correctly I suppose your top state could call on the bottom state to update or draw.

for example your in world state then you push menu state on top. During the loop menu state runs the function Update which then calls World.update effectively allowing the world to continue as is. Then Menu.render is called which then if you so choose you can call World.render as well to draw the world behind the menu

Arrays can overflow.

Example: int numbers[5];
numbers[100]=54;

that will give you a compiler error or allow you to run but experience bugs.
Quote:Original post by Ruzhyo2000
It is occurring to me that, if we are only running a single state at a time, what is the point of a vector? Can we not just have a GameState* member of the GameApplication or GameEngine class that points to whatever state is to be ran? Is the vector a solution to the issue of simply holding the next state, until the current ending state can call it's final render, call uninitialize() or exit() or cleanup() (whatever we are calling it), and then have itself removed from the stack? Or is this not for handling multiple states?

Suppose when I pause the game state, I want the game to continue (in essence you do not have a true pause, you just have your menu open while the game is playing). The menu state could be added to the stack and the game state could call pause();, and the game state could handle and update and render in a manner simply inhibiting your interaction with the game state while the menu state handles the events until it is exited, and the game state resumes as normal. Is this the intended usage of the stack? If not, then from where is its necessity?


You can of course skip using a data structure like a stack or a vector, and just have a bunch of gamestates.
The thing is that then you would have to hard code all the transitions between the gamestates all around the code.

As an example:

Say you press escape while in the menu state. If you don't have a stack or vector it could look something like this:
void calledWhenPressingEscapeInMenu(){  ...  currentState = GameStateRunning; // change current state}

Now, the same example using a stack:
void calledWhenPressingEscapeInMenu(){  ...  stack.pop(); // remove the gamestate at the top of the stack  if(stack.isEmpty()) // Quit the game if the stack becomes empty  {    gameOver = true;    return;  }  currentState = stack.top(); // the next state becomes the current one}

These two examples are very different. The first one says "Always go to running state when escape is pressed in menu".

The other says "Go back to the state that was active before menu state became the active one".

Lets say that the first state pushed onto the stack when the game starts is the menu state. Now if you press escape the game quits. Neat huh?

At one time the stack could look like this:
[ GameOverState | RunningState | ScoreState | MenuState ]

MenuState is the current state, and by pressing escape you go back to the score state.

In another situation it could look like this
[ GameOverState | RunningState | MenuState ]

Pressing escape here goes back to running state.

Ok you get the idea, basically the stack will keep track of the order of the states.

There is other advantages also, like keeping the states in a stack/list will make the code cleaner and it is easier to iterate/process through the states using a loop.

Imagine the cleanup when the game ends.
Using a stack:
while(!stack.empty()){  GameState *next = stack.pop();  delete next;}

This code will always work. You never need to change it no matter how many states is left in the stack and what they are called.

Not using a stack:
delete GameOverState;delete RunningState;delete ScoreState;delete MenuState;

The code above has to be maintained if you add or remove states to the game.

This topic is closed to new replies.

Advertisement