Using a stack for the gamestate

Started by
33 comments, last by Seriema 18 years, 8 months ago
Quote:Original post by Holy Fuzz
A state in an FSM needs to know what state came before to do such tasks as returning to the previous state.

The code in that link that I posted earlier, everything that the fsm need is in the fsm class. Even the previous state, but (IIRC) in a real FSM "returning to the previous state" doesn't exist, only going to new states :)

Quote:Original post by Holy Fuzz
Quote:In this case I would probably have done a state in a popup-stack that overrides the game-state. ie:
update: if popupstack is empty update game-state, if popup is nonempty - update popup.
rendering: render game-state, then render popups

If I understand correctly, then you are proposing to implement a stack system for only those "base" states that really need a stack, which also turns it into a "bottom-up" system rather than a "top-down" system. Other than requiring a (probably insignificant) amount of work, I only see one problem with this method: By having a base state manually implement a stack system you are requiring every base state to know whether another state may or may not be pushed on top of it. In my opinion, this limits extensibility, though an argument could be made that it also keeps the state system from being abused with needless pushing of states ontop of other states.

no no no ;) That system would be used for popups only to override the game-loop. In this system a popup != gameloop. Popup stops the loop(to some extent) while the game loop is a loop, so the game loop lives in a happy little world not knowing what happens with the popup and for what it is conserned popups doesn't exist. sortof like popup(msg); could also be print(msg);.

Quote:Original post by Holy Fuzz
Quote:void GameState::update(float p_deltaTime) {
m_currentGame->update(p_deltaTime);
if( someCondition ) gameMashine->setState(InGameMenyState::instance());
}
or
void GameState::update(float p_deltaTime) {
m_currentGame->update(p_deltaTime);
if( someCondition ) gameStack->pushState(InGameMenyState::instance());
}

Again, if a pure FSM was used, you would need to hardcode in or otherwise tell the new state which state it should return to after it is done executing. In the first chunk of code, InGameMenyState::instance() would need to be passed a handle to the previous state.

See above, but if a true fsm would be used, I would have to send events to it all the time and have it call the updates.

Quote:Original post by Holy Fuzz
Quote:I really can't argue with this, but is it needed? A pause state might want to update the view, but not the game logic(like max payne pause) and there goes IMHO the beauty of stack.

Hm... maybe "stack" isn't the proper term... How about "runtime state inheritance"? What I'm describing is actually a lot like type inheritance and polymorphism, except with states instead of classes. For example, pushing state A onto a stack whose top state is state B really means that A is inheriting (and probably overriding some of) the functionality of B, which in turn may inherit and override the functionality of C, and so on... So yeah, it's like polymorphic states where inheritance happens a runtime.

So the update function in the pause state can move the game camera? but isn't this one hidden from the pause state since it belongs to the game state? How would it be coded into the states? Giving the state a getCamera() function seems like a stupid idea? A Meny or a popup state can't return a camera, right?

I think this isn't really a discussion about game-states but on if a meny state(Game Menu, Options Menu, Quit Menu etc) is a game state or a menu is a game state?
Maybee itsn't such a bad idea if you can make the whole menu as a single gamestate(and menu states as nodes in a tree). But I'm still unsure of wether it's a good idea to have a stack - stack overflow (and underflow) might occur unless managed correctly [rolleyes] ...
Advertisement
Quote:no no no ;) That system would be used for popups only to override the game-loop. In this system a popup != gameloop. Popup stops the loop(to some extent) while the game loop is a loop, so the game loop lives in a happy little world not knowing what happens with the popup and for what it is conserned popups doesn't exist. sortof like popup(msg); could also be print(msg);.


Ah... I think I get it now. So a popup like that would actually provide its *own* game loop? Much like calling message box functions in Win32 looks like a function call but actually pops up a box that the user can interact with, without breaking the flow of the code. You could still pass the popup an interface to the state so that it can call its code if necessary. I like the idea, and the only problem I see with it is implementing the system.

Have you indeed implemented such a system? If so, what issues did you have to deal with to implement it? My engine has some pretty strict definitions of where a frame begins and ends, and so providing a game loop inside of a frame that has already begun might cause some issues, as well as some timing issues and such. My game loop is a pretty hefty beast, and so providing a generic game-loop that any class can implement is crucial.

Quote:See above, but if a true fsm would be used, I would have to send events to it all the time and have it call the updates.


This makes sense now that I understand how you're handling the game loop.

Quote:So the update function in the pause state can move the game camera? but isn't this one hidden from the pause state since it belongs to the game state? How would it be coded into the states? Giving the state a getCamera() function seems like a stupid idea? A Meny or a popup state can't return a camera, right?


My states each have Input(), Update(), and Draw() methods which are exposed through an interface. A pause state (BTW, there are probably better ways to pause a game than using a state, but that's beside the point) would almost certainly call the game state's Draw() function, and would almost certainly NOT call the game state's Update() function (that's the whole point of pausing, right?). The pause state might or might not call the game state's Input() function, depending on whether the pause state wanted to let the user interact with the game state's UI. Whether the camera movement code is put inside Input() or Update() is really just a judgement call (do you consider it part of the UI or part of the gameplay?).

Quote:I think this isn't really a discussion about game-states but on if a meny state(Game Menu, Options Menu, Quit Menu etc) is a game state or a menu is a game state?
Maybee itsn't such a bad idea if you can make the whole menu as a single gamestate(and menu states as nodes in a tree). But I'm still unsure of wether it's a good idea to have a stack - stack overflow (and underflow) might occur unless managed correctly ...


Here's a rough example some of the possible stacks in one of my games:

MainMenu
MainMenu->SinglePlayer
MainMenu->SinglePlayer->GameSetup
MainMenu->Multiplayer
MainMenu->Multiplayer->GameSetup
MainMenu->DisplayOptions
Game
Game->GameMenu
Game->GameMenu->DisplayOptions

I've ommitted an options menu and some multiplayer stuff, but you get the point.

The GameSetup screen creates a new game, clears the state stack, and pushes the new game onto the stack. When the game exits, it clears the stack and pushes the main menu onto the stack. I don't consider individual gui elements to be their own states. The game state itself contains an in-game gui, which is not a seperate state, though when the user clicks on the "menu" button in the game it pushes the GameMenu state onto the stack, which effectively pauses the game and blocks the in-game UI from responding to user input.

There is of course no reason the functionality described above couldn't be done in your system. MainMenu and Game could be two states in the FSM, and SinglePlayer, GameSetup, Multiplayer, DisplayOptions, and GameMenu could be function calls that implement their own game loops.
Quote:So a popup like that would actually provide its *own* game loop?

Not quite. To the game-loop it would appear as such, but the reality could more look like this:
void popup(str) {  popupstack.push(new Popup(str));}void main() {  init();  while(run) {    if( popupstack.empty() ) {      update(deltaTime);    }    else {      popupstack.peek().update(deltaTime);    }    render();    if( !popupstack.empty() ) popupstack.peek().render();  }}

It't more or less a (poor) hack, but it does the job and quiet nice IMHO - so I guess it isn't a hack after all :).

Quote:Have you indeed implemented such a system?
nope, haven't had the time (or need) to do it. Since my current game (3 years of coding in october) is almost complete (and 100% badly designed) I plan to design my new game(Cpp this time instead of C) alot better ;)

Quote:(do you consider it part of the UI or part of the gameplay?)
The code should IMHO go into update() since it doesn't rely on input.
However in my current (planned) design a pause state probably would have pushed a PauseCameraControler onto the CameraControler stack.
(The Controler idea is basicly to have a stack with controlrs such as AI, Keyboard, Script etc. Some controlers should pop() themself(such as the script when it's done) and some should stay until pop()ed by someone else).

Quote:The GameSetup screen creates a new game, clears the state stack, and pushes the new game onto the stack.

Nice, I might try a sytem like this soon, however I got one (more) question for you: How do your exit-button function / game-loop header look like:
something like this:
while( !stack.isEmpty() ) gameLoop();

or something like this:
while(running) gameLoop();void onExit() {running=false;}
?
Quote:Nice, I might try a sytem like this soon, however I got one (more) question for you: How do your exit-button function / game-loop header look like:
something like this:

while( !stack.isEmpty() ) gameLoop();


or something like this:

while(running) gameLoop();
void onExit() {running=false;}

?


Actually, both:

while(running && !stack.isEmpty()) gameLoop();
void onExit(){running=false;}

is roughly what it looks like. I realize I could have onExit() clear the stack to the same effect, but by setting a flag instead I allow the application to examine and manipulate the stack without any unexpected errors (if the stack was cleared, and then something tried to pop a state off, this would result in an exception).
Quote:Original post by nilkn
Other than this, there aren't any other non-superficial differences between my system and yours.


I would disagree, these two designs are highly different. A stack based state machine makes the prequisite that all states must be designed as a tree-graph where iteration between states goes from node to node. I.e. you can't "jump" from one state to another. Simple example, you have set it up like this:

Main Menu
|- Options
|- Play Game

Now say you're in options, with a state stack you aren't allowed to start the game from there (not a problem in this case IMHO, but you get my point). You have to pop out of Options back to Main Menu, and from there push Play Game.

joanusdmentia shows a perfect example where this becomes a problem. For simple games I would even consider it good because it forces you to have states that are logically ordered hierarchly. I haven't used hplus0603 idea but it seems simple and good.

Toolmaker, your responce to joanusdmentia hacks in a fix to the problem. As I said, a state stack isn't done for "jumping". So looping until size==1 or current==MainMenu is more of a hack than good state stack management.

Niteage system is a bit like mine, where I did something I call substates. A substate calls the parent states update/render etc. so it doesn't pause its parent. This is useful for ingame menus that shouldn't pause the game, like weapon switching in a action game (think HL and Max Payne). (as Toolmaker noticed)

Quote:Original post by 5MinuteGaming
I believe xSKOTTIEx had the right idea. A separate manager is like putting more gas on the flames.

As I understood xSKOTTIEx he does use a manager, just not stack based. Having to repeat this:
current()->pause();
stack().push( newState );
current()->play();
everywhere you want to switch state is pretty bad, thus the manager.

Something I always keep getting back to is, how to switch states in a "nice way". Having a StateStack* in each state, so they can go pop/push whenever they want or using a event system or simple quering? hmm =)
[ ThumbView: Adds thumbnail support for DDS, PCX, TGA and 16 other imagetypes for Windows XP Explorer. ] [ Chocolate peanuts: Brazilian recipe for home made chocolate covered peanuts. Pure coding pleasure. ]

This topic is closed to new replies.

Advertisement