Jump to content
  • Advertisement
Sign in to follow this  
Phineas

Managing Game States: The Stack Method

This topic is 4856 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

Like most budding game programmers, I started by creating clones of the classics. No, the world did not need another version of Pong or Asteroids; but, as classically-trained musicians may begin with Mozart, so game programmers may begin with Tetris. In writing Pong, Asteroids and Tetris I managed states the easy way: using a switch-case block. This is nice for simple projects, but for more complicated projects the code becomes messy. Enough of my preamble about what many of you already know. On to the point: In researching alternative methods, I came across this tutorial about managing states using a stack (which most of you already know of as well). I adapted this method, changed a few things to suit my needs, and I came up with reusable code for managing game states. gamestack.zip comes with the reusable code, as well as a sample (text) game as a proof-of-concept1 to show how it all comes together. download gamestack.zip (c++) Changes from the tutorial: 1. Removed SDL code. 2. Removed the need for GameStates to be singleton classes. I know it takes a lot to download source code and look at it, but if you could take look and give me some pointers, I would be very grateful. Even if you can't, I'd like to discuss any conceptual problems with managing states this way, and any alternatives you may have (though I have used the gamedev.net search function a bit). (1) I’ve always wanted an excuse to say “proof-of-concept.” Edit: Updated code per suggestions below [Edited by - Phineas on December 1, 2005 4:41:16 PM]

Share this post


Link to post
Share on other sites
Advertisement
A very good initiative, which earns you an increased rating from me.

I do have some questions, though:

  • Why use a vector to implement manually a stack, when the SC++L already provides std::stack?

  • Wouldn't it make sense to have GameStack inherit from GameState?

  • Shouldn't the documentation for functions be in the header, rather than in the implementation file?

  • I disagree with your handling of popState in the situation where the stack is empty. My reflex would have been to throw an exception (or an assert, depending on who is allowed to use the object).

  • Similarly, you should issue at least warnings or errors when a stack that was disabled is used.

  • Who is responsible for destroying objects added to the stack? Note that quit destroys all objects, while popState does not destroy the removed object (and as a consequence, you have a memory leak in your example code).

  • You already implemented a method to push/pop states. Why, then, reimplement both functionalities in changeState, when you could simply make two successive calls to popState and pushState?



Also, I have two short suggestions to make:

  • Don't force the programmer to implement all of the virtual functions in GameState. As you noticed, it results in empty functions, which is useless to have or write. Have the base functions do nothing instead.

  • You should also add onEnable and onDisable functions to GameState, and have these functions be called whenever the game state gains focus (is pushed onto the stack, or the state above it is popped) or loses focus (is removed from the stack, or a state is pushed on top of it).

Share this post


Link to post
Share on other sites
Thanks for your reply. The code was taken from the tutorial mentioned above and modified, so perhaps I was not as thorough as I should have been. My main objective was to remove singletons, which I have been indoctrinated to avoid. You've presented a lot of ways in which GameStack may be improved, and a lot of your suggestions I am taking.

Quote:
Wouldn't it make sense to have GameStack inherit from GameState?

Its logical in the sense that most of GameStack's functions (init, exit, input, process, output) are also functions of GameState. But, to inherit GameStack from GameState would imply that GameStack is-a GameState, when the purpose of GameStack is to control GameStates.

Quote:
I disagree with your handling of popState in the situation where the stack is empty. My reflex would have been to throw an exception (or an assert, depending on who is allowed to use the object).

Good point. If there is no state on the stack, GameStack isn't doing anything anyway. Although, I could just call GameStack::quit when this happens.

Quote:
You already implemented a method to push/pop states. Why, then, reimplement both functionalities in changeState, when you could simply make two successive calls to popState and pushState?

Do you mean GameState, instead of calling changeState, should call popState then pushState, or that changeState should call popState then pushState?

Quote:
Don't force the programmer to implement all of the virtual functions in GameState. As you noticed, it results in empty functions, which is useless to have or write. Have the base functions do nothing instead.

So, I should keep the base functions declared as virtual, just remove their "pure virtual"-ness ( foo() = 0; to foo() {} )?

Quote:
You should also add onEnable and onDisable functions to GameState, and have these functions be called whenever the game state gains focus (is pushed onto the stack, or the state above it is popped) or loses focus (is removed from the stack, or a state is pushed on top of it).

The tutorial originally had these functions, and I removed them. What would a game state need these for?

Quote:
Who is responsible for destroying objects added to the stack? Note that quit destroys all objects, while popState does not destroy the removed object (and as a consequence, you have a memory leak in your example code).

Shouldn't the documentation for functions be in the header, rather than in the implementation file?

Why use a vector to implement manually a stack, when the SC++L already provides std::stack?

All of these my novice hands overlooked; thanks for pointing them out (especially the memory leak! GameStack is supposed to handle destroying GameStates).

Share this post


Link to post
Share on other sites
Guest Anonymous Poster
Quote:
Original post by Phineas

Quote:
You already implemented a method to push/pop states. Why, then, reimplement both functionalities in changeState, when you could simply make two successive calls to popState and pushState?

Do you mean GameState, instead of calling changeState, should call popState then pushState, or that changeState should call popState then pushState?


I think he meant that you are duplicating code in changeState, popState and pushState. You should modify changeState to read:


void GameStack::changeState(GameState *state)
{
popState();
pushState(state);
}


I would also make something similar with the quit() function, as it also duplicates the code from popStack and, in this case also it would have prevented the memory leak in changeState and popState (if you used the delete in the popState function, of course).

Overall, though, nice job.

Share this post


Link to post
Share on other sites
Nice, I have done something like this as well, so I thought I'd give some comments as well. As to Enable/Disable methods, I think they actually have a lot of uses. For example, you may not want to keep all resources around at all times, so you could free them up when you lose focus. Or you could use it to hook up to input events when gaining focus and disabling it again when losing focus (not necessary if using your input-mechanism, I know, but if using an event-based system it might be useful).

Also, in my opinion, having GameStack derive from GameState might actually make sense if you see GameStack not simply as a container for GameStates but rather as representing the current GameState. Actually, it sounds as if the problem with both approaches is that the class is in the end doing two things at once: It holds GameStates and it represents the current GameState.
So, in my opinion, it should really be something like Stack->Current->Update() or whatever instead of Stack->Update(), to reflect this. If you feel this is too cumbersome, I'd still derive it from GameState, as it is no more wrong than not deriving. But then, I am not that good at designing classes, so I might be completely wrong about this. :)

Share this post


Link to post
Share on other sites
Quote:
Original post by Phineas
Quote:
Don't force the programmer to implement all of the virtual functions in GameState. As you noticed, it results in empty functions, which is useless to have or write. Have the base functions do nothing instead.

So, I should keep the base functions declared as virtual, just remove their "pure virtual"-ness ( foo() = 0; to foo() {} )?


Pretty much, although be careful to make a distinction between the functions that must be implemented versus those that may be implemented.

Quote:
Quote:
You should also add onEnable and onDisable functions to GameState, and have these functions be called whenever the game state gains focus (is pushed onto the stack, or the state above it is popped) or loses focus (is removed from the stack, or a state is pushed on top of it).

The tutorial originally had these functions, and I removed them. What would a game state need these for?


Doing things when you enter or leave a state. Wuntvor mentioned resources and input focus. If you are using this class in multiple places to do different things for example controlling the global game state or implementing a FSM in an enemy character having the extra flexibility can be very useful.

Share this post


Link to post
Share on other sites
Quote:
Original post by Wuntvor
So, in my opinion, it should really be something like Stack->Current->Update() or whatever instead of Stack->Update(), to reflect this. If you feel this is too cumbersome, I'd still derive it from GameState, as it is no more wrong than not deriving. But then, I am not that good at designing classes, so I might be completely wrong about this. :)

Guess I'll quote myself here.. I just wrote a simple implementation of this, and it appears a lot cleaner to me than my previous implementations (when I did have Draw() and Update()-methods directly in my Stack). Also, the more I think about it, the more I think that having those inside the GameStack class is actually not logical at all.

Why would a GameStack have input(), process() and output() methods? Do I want to output() the GameStack to the screen? No, what I actually do want to do is retrieve the current GameState and output() that to the screen. Sure, it may seem more convenient to write Stack->output(), but that should not guide us in making decisions like this, in my opinion.

One more thing I have been thinking about: Are Init() and Exit() methods actually important? It doesn't really sound that useful to me. The GameState may care when it becomes active and when it stops being so, but why would it care about being inserted or removed into a GameStack? Actually, I'd argue that GameState shouldn't know at all about GameStacks but should rather be independent from it.
If Init/Exit is included so that resources can be managed, I'd argue that these should be loaded at construction of the object (or in the Enter event) and freed at destruction (or in the Leave event). Otherwise, it may be initialized several times (if it is inserted again after having been popped) which isn't very intuitive at all.

Okay, I'll stop ranting now!

Share this post


Link to post
Share on other sites
Quote:
Original post by Wuntvor
...

Why would a GameStack have input(), process() and output() methods? Do I want to output() the GameStack to the screen? No, what I actually do want to do is retrieve the current GameState and output() that to the screen. Sure, it may seem more convenient to write Stack->output(), but that should not guide us in making decisions like this, in my opinion.

One more thing I have been thinking about: Are Init() and Exit() methods actually important? It doesn't really sound that useful to me. The GameState may care when it becomes active and when it stops being so, but why would it care about being inserted or removed into a GameStack? Actually, I'd argue that GameState shouldn't know at all about GameStacks but should rather be independent from it.

...

Wuntvor, I've implemented your suggestion about removing input, process, and output from GameStack; doing so increased the flexibility of GameState-derived classes. I've also removed init() and exit(), as you make a good point that those are actually a constructor and destructor in disguise.

Share this post


Link to post
Share on other sites
I am still at the same stage as the OP here (writing simple 2d games) and using a stack-based game state manager. I too am using a vector, for the reason that I might still need to access one of the states lower on the 'stack' as opposed to just the current one, thus am not using an STL stack.

Share this post


Link to post
Share on other sites
I'm currently designing the code layout for an RPG of mine and want to know what kind of advantages do stacked game state systems have over switch-case systems? Will the latter method eat too much CPU?

See, I tried to keep things as simple as possible in my RPG. I have a Game class, which carries out two important functions: initialized the game and its global variables, and then it goes to awaiting the game state choice. It stays in this mode in a while loop, until the player decides to quit.

Currently I have four game states: Menu (which can double as 'Pause'), Battle, Shop, and Plot. There is no dungeon crawling; it's a tactical RPG. And the Plot is empty for now, it will just trigger scripted events so that's not important for me now. The game's in debug/testing mode so the user can choose any of these options at will, which takes the input to a switch/case statement.

As you can see it's not as elegant as the game stack approach, but I'm only choosing among 4 different game states here. Depending on what state you're in, the program simply applies functions from various other classes (like a Shop class, Status class, etc). Most of these involve many while loops, which keep the game in its current state, and if the loop is broken out of, the player is free to go do something else. Is this good way to handle events? Currently I have a shop that works, and a simple battle simulator (which receives no input from the player), and have had no problems.

So is it a good idea to attempt to break down even a complex game such as this into a few states?

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

GameDev.net is your game development community. Create an account for your GameDev Portfolio and participate in the largest developer community in the games industry.

Sign me up!