Sign in to follow this  
Toadhead

Game state management (C++)

Recommended Posts

Toadhead    244

Hi guys,

 

My gamestate management uses classes (gamestates) with a init(), run() and close() function,

When Changestate(state) gets run, it closes the old state, inits the new one and runs it.

 

Here is my problem, whenever I use Changestate() in my current state, e.g. someone presses the "Game Menu" button

so Changestate(gamemenu) is called, it still has to finish the old state. For instance take this example:

 

//some gamestate
remove_old_gamestate()
 
while(running)
{
update();
draw();
}
 
update()
{
if(button.pressed())
{
ChangeState(menu);
}
}

 

The Changestate() makes a copy of the old gamestate (which will be removed using remove_old_gamestate() later, and inits the new one.

This is not very efficient as in this period the game needs about double the memory (old one is not closed down yet, new one is already initialized). However it is nessacary, because using "currentstate->close(); delete currentstate; currentstate = newstate" does not work, as soon as the ChangeState() function is over, the call stack goes back to where it was called: the old gamestate, and the draw function still has to be called, in a state already closed/deleted. I dont understand how I can overcome this problem without using gotos (which I prefer not to use).

Anyone has an idea?

 

Share this post


Link to post
Share on other sites
noizex    1437

Hard to tell because you provided only partial pseudo-code for your solution. The usual way (I think) it is done is - you don't instantly change the state (especially not as a responsibility of state class itself), but you ask to change the state on next update. Then finite state machine gets updated and it processes to next state, cleaning up old one. It does not require holding any copy or anything like that. Its just: finish old state, prepare new state, switch current state to new state.

Edited by noizex

Share this post


Link to post
Share on other sites
Toadhead    244

Yeah you are right, guess I'll have to make a couple of small changes to my code.

I want it to work as easy as possible, implementing a new state at this moment only requires me to have new code files for the state, but guess ill just need to implement

some kind of enumeration somewhere that has all possible states.

Share this post


Link to post
Share on other sites
SiCrane    11839
One option is to change the way you change states. Rather than change the state inside the state code signal the calling code that you want the state to change. Once method is to use the run and return successor pattern: state functions return a (smart) pointer to the next state and the calling function uses that pointer to set the state. Some sample code of how you might use that is in this older thread.

Share this post


Link to post
Share on other sites
Norman Barrows    7179

a game state management system can sometimes take modules that are at different levels and place them at the same level, which can lead to complications.

 

init_program, run_program, and shutdown_program are all top level modules. 

 

game startup menus are a module called by run_program. they in turn call run_game.  so the startup menus or "shell" is the second level of modules.

 

init_game, run_game , end_game is the next level of modules.

 

the in_game  menu is  a module called by run_game. its at the next level down. 

 

by making the in_game menu or game startup menu just another state along with init_game, run_game, end_game, you "force" them to be at the same level, which may require extra communication which is normally handled implicitly by the call hierarchy.

 

note that its very common to have a game loop that looks like this:

 

while (! gameover)

     process_input

     if (gameover) return

     update_all

     if (gameover) return

     render all
 

the sad fact is that there are a number of places you must break out of a game loop for a number of reasons. such as the player pressing the hotkey for the ingame menu, or the game ending due to any of a variety of reasons (mission goals achieved, mission goals failed, all bad guys dead, player dead, etc).

 

so there will be a number of places where something will occur and you'll want to break out of the game loop and call a sub-module such as an ingame menu, or return control to the calling module (the shell / wrapper / game startup menus).

 

about all you can do is set a flag when such things occur, and either finish the current module's processing (such as updating all) or return immediately. then after each call to a module where the flag can be set, you check to see if you should break out of the game loop.

 

one thing i  note in this particular case is that your loop is of the form:

 

while ! quitgame

     update

     render

 

and you get input inside update...

 

update_each:

if (is_player) get_human_input else get_AI_input

 

or some such thing.

 

with a loop of the form:

 

while (! quitgame)

        render_alll

        process_input

        update_all

 

you only need to check for gameover after getinput:

 

while (! quitgame)

        renderalll

        process_input

        if (! gameover) update_all

 

 

you can also change the order to eliminate the check completely:

 

while (! quitgame)

     update_all

     render_all

     process_input

 

but you would still need a check for gameover due to updates (such as player getting killed):

 

while (! quitgame)

     {

     update_all

     if (! gameover)

            {

            render_all

            process_input

            }

     }

 

 

i usually go with:

 

while (! gameover)

       {

       render_all

       process_input

       if (! gameover) update_all

       }

 

 

note that by having an explicit process_input module in your loop, the in-game menu simply becomes a sub module called by process_input, instead of a state to be dealt with.

 

 

now, here's an example of when its a good idea to use "game states":

 

the game has 4 different types of environments / types of locations / points of view to draw: outside, in_cave, in_cavern, and up_a_tree.

 

each uses its own unique method of rendering the required scene, essentially 4 separate types of graphics engines.

 

as the player moves about the world, their "location" can change from outside to in_cave, etc. location is the "state" variable, with the 4 pre-defined state values of: outside, incave, incavern, and upatree.

 

when render_all is called, it checks the current state (the location) and calls the appropriate rendering engine.

 

each of the 4 rendering engines is a module at the same level, just below the render_all module. the render_all module NEEDS to be able to operate in one of 4 states. so its a good candidate for a "game state" approach.     the key is that all the states are at the same level, so they are truly ONE thing (the renderer) operating in different states, as opposed to different things at different levels (the game loop and wrapper or in-game menus) being force to operate at the same level via state transitions.

 

what you're doing is replacing the flow control implicit in call hierarchy with explicit state transition flow control. but your problem is that in a game, you're not really dealing with a single state machine with multiple states like init, shutdown, startup_menu, ingame_menu, and rungame.

 

you're dealing with a hierarchy of state machines.  

 

your highest level state machine has the states init_program, run_program, end program.

 

your next highest level state machine is run_program, with the states init_game, run_game and end_game.

 

your next highest level state machine is run_game with states render_all, process_input, and update_all.

 

your next highest level state machine is the one controlling the in-game menu, which is called from the process_input state as needed. think of the entire in-game menu as a "sub-state" of process_input that the game switches to as needed (IE when the player pulls up the in-game menus).

 

but as you can see, just from the previous sentence, trying to think of call hierarchies in terms of states at the same level can be confusing. that's because call hierarchies are tree type structures of modules, and state machines are more like peer to peer networks of modules. 

 

peer to peer networks don't seem to me to lends themselves especially well to representing tree structures.

 

note that in a peerless network all the nodes (modules) are at the same "level". no client server hierarchy.

 

i think this may be a good test for when to use and not to use game states. are the modules that execute the states all inherently at the same level? if so, they may be a good candidate, if not, they may not be such a good idea.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this