A more complex state system, running multiple states at once

Started by
9 comments, last by pacobarter 11 years, 7 months ago
I know there are a few topics already out there talking about game states and state management with them, but this one comes with a twist. I want to know what your thoughts and suggestions to improve a system I've been trying out with a new game I'm making.

I've seen SiCrane talking about how to go manager-free with game states before, and I understand how it works. But how well do you think going manager-free will bode for game states that don't necessarily follow a LIFO order of inserting/removing? Or combined with the allowing of several game states to be running and updating at once? This is the twist I was talking about.

The game I'm working on uses an adapted "screen system" which from the XNA's officiel game states sample. Although the project is called GameStateManagement they call the states "screens" in the code. In it, you are able to switch in and out an arbitrary number of screens, and let a manager class update and draw them as usual. But it allows for many screens to be visible and updating at once, so for instance you can have one screen just drawing a background, while a screen pushed on top of it will draw and read input from a menu, and then that can be removed and replaced with another menu when a choice is selected, while keeping the same background. Just to add icing on the cake, they also had transitions, which are optionally implemented to have screens animating themselves in and out of view.

After wrapping my head around how exactly works, I decided to tinker with its guts underneath. For me this is more flexible than a system using a structure that strictly behaves like a stack, but I found some trade-offs which are mostly related to avoiding logical/design errors. Those trade-offs are, because states don't follow a LIFO rule, it's easier to lose track of the control flow of the game unless you carefully implement rules behind how and when screens should be updated, and you have to keep a closer watch at what screens you swap in and out to avoid unexpected behavior. After clearly laying out my own rules to improve/customize the sample code to my liking and displaying the output of the screens' statuses and transitions, I'm almost completely satisfied with how it works.

I know it may seems complex, but it has been working fine on my game so far. More importantly it has really gotten me to understand how a more complex game like a fully-featured RPG might be able to function with several states running at once. Any suggestions improve on this system, and maybe even having it become free of using a state manager class? It seems to be trickier to go manager-free here because you can have multiple screens on at once. Would having a manager class be justified in this case?

New game in progress: Project SeedWorld

My development blog: Electronic Meteor

Advertisement
I assume you're referring to the run and return successor technique such as described in this thread? You're not limited to LIFO with that technique. It can be used to do things like you've described.

If you have a menu state that you want another state rendered underneath, pass the menu state constructor a pointer to the background state, and have the menu state call the background state's render function before it does its own rendering. If you transition to a new menu state with the same background state, just pass the background state's pointer to that new menu state when you construct it.

If you want to transition between two states, create a transition state class (or wipe/crossfade/dissolve/etc class) and pass it both the original and desired state. The transition state will have access to both beginning and end states' render functions and can do whatever voodoo it likes on those when rendering the transition. When the transition effect is done, it returns the desired state to complete the transition.
It really depends on the situation.

The simplest is a single-state manager, where you just have a switch statement for the current game mode and call the appropriate update function... or in OO style, a pointer to a base class of game state, and call its update function.

Next is a stack, but they're usually more complicated than they are effective.

You can do a linked list of active "screens", and either update them in the order they were created, or by a priority system of some sort.

You can also take that a step further to a parent/child type system, where each node of the linked list can have a child list, as a way of prioritizing updates and making it easy to kill off associated things all at once and in the correct order.

My personal favorite is the single-state manager, but with explicit children as needed. For example, an RPG could have the main state be the title screen, and then switch to the map where you wander around and talk to people. Then the map could have a pointer to a text box, which can be created to do the talking, and a pointer to a menu which can be created if you push start or whatever. Then the menu class could have a pointer to another menu class, which it could create as a child menu of itself (e.g. going into the item menu). The text box could have a pointer to a yes/no selection box for doing branching dialog.

Then the update order is all controlled very explicitly, with the map calling each of its potentially-existing children in a set order, and informing them who should actually respond to input (text box shouldn't respond if menu is open), and they each call their own children as desired.

But that style works best for relatively simple game designs, where there aren't that many potential combinations of active states at once. If it starts getting complicated, switch to one of the more general-purpose linked list styles.

I assume you're referring to the run and return successor technique such as described in this thread? You're not limited to LIFO with that technique. It can be used to do things like you've described.

If you have a menu state that you want another state rendered underneath, pass the menu state constructor a pointer to the background state, and have the menu state call the background state's render function before it does its own rendering. If you transition to a new menu state with the same background state, just pass the background state's pointer to that new menu state when you construct it.

If you want to transition between two states, create a transition state class (or wipe/crossfade/dissolve/etc class) and pass it both the original and desired state. The transition state will have access to both beginning and end states' render functions and can do whatever voodoo it likes on those when rendering the transition. When the transition effect is done, it returns the desired state to complete the transition.


Thanks SiCrane, I'll keep this in consideration as I refine my system. I was doubting whether or not the menu can keep the background state's lifespan going when the menu goes away, but the way you explained it makes sense. If I understand correctly, the menu keeps a pointer (or passing by value as I'm using C#) to a state that needs to update before itself, say it's called lowerState. And in the menu's Draw function, it should call lowerState.Draw before its own drawing code. So I guess this can continue down the chain of states until a null "lowerState" value is found.

My idea behind going beyond single-state managers is that going multi-state it makes it a lot easier for me to divide the game's logic in discrete parts when several different things are happening at once. That way I can keep all the HUD updating and drawing logic in a state that rests on top of the main gameplay state where all the action is happening, and I could open other menus such as an inventory menu that might share some data with the gameplay state so when you do some actions on that menu, the effects take place in the gameplay.

As for transitions, I currently have a Transition object that has a "transition delta" variable that exists in all states, and the actual length of time that it takes for delta to go from 0 to 1 (or 1 to 0 if it's leaving). Each screen can have a custom time length for a transition, or a zero length if it just needs to "pop" into view. I'm not sure why the transition should be a separate state itself, but I'm imagining it's sort of like in video editing software where you put a transition clip to overlap the duration of two movie clips?

New game in progress: Project SeedWorld

My development blog: Electronic Meteor


I'm not sure why the transition should be a separate state itself, but I'm imagining it's sort of like in video editing software where you put a transition clip to overlap the duration of two movie clips?

Exactly.

The transition state neatly wraps up all the logic and data involved in the transition, and can composite the other two states in a nice transition animation when rendering.

Tristam MacDonald. Ex-BigTech Software Engineer. Future farmer. [https://trist.am]

Ideally I'll want each game state to have its own way of transitioning, so the transition state doesn't know of the kinds of animations used in each game state. This seems very feasible, as I already have my Transition object of each state just updating a "time delta" variable and the states can do whatever they want with it.


I think this is starting to make sense to me. Let's see if I have this down pat:

- The game's main class needs to keep a "current state" variable to update the top-most state, then all the ones following that one.
- Each game state would have a "lower state" variable in which to store other states that you may want to update before it does, creating the linked list.
- Any time you want to add more than one game state at a time, start with the lowermost state and "chain" each state as you go to the top.

So far I see two possible precautions:

You have to make sure to completely initialize the new states and load their resources before the end of the old state's Update. When the Draw part of the game's loop comes around it will try to draw the new states. If their resources are not properly loaded it will draw an empty screen for one frame, or more likely, crash the program because it's trying to render null resources.

Is it an issue that the function stack grows the more states you add, since updates are called recursively through the state list? I'm guessing that in the usual case scenario, it's not, because you'll only need a handful of states at one time. I currently just have at most 4 or 5 states updating at once and I can't see myself updating more than 10.

New game in progress: Project SeedWorld

My development blog: Electronic Meteor


Ideally I'll want each game state to have its own way of transitioning, so the transition state doesn't know of the kinds of animations used in each game state. This seems very feasible, as I already have my Transition object of each state just updating a "time delta" variable and the states can do whatever they want with it.

I look at it from the other direction. I want my states to be as lean as possible, so I don't want them knowing how to do a transition unless they are specifically a transition state. Further, why clutter states that don't want or need a transition with transition data? It seems pointless for a pause state to have a transition to the non-paused state. Further, if I isolate the transitions into their own individual components, it's easy to change what transition I use to go from one state to another. For example, if there's a game option to lower graphics quality. Obviously it's possible to do it the way you want to, it's just that I would take a different approach.

The game's main class needs to keep a "current state" variable to update the top-most state, then all the ones following that one.
[/quote]
It doesn't have to be a class. You could have a state local variable in your main() function if you wanted. Ideally there's a two way separation: whatever holds a state handle doesn't know or care what kind of state it holds and states neither know nor care what holds a handle to themselves.

Each game state would have a "lower state" variable in which to store other states that you may want to update before it does, creating the linked list.[/quote]
A state may or may not have one or more handles to other states. I wouldn't generalize it to say that every state would have a "lower state" variable. What two states may do with referenced states may be completely different. For example, a pause game state might only invoke the render() member function of a held state. A drop down console state may invoke update() and render() member functions, while a loading screen state might not have a referenced state or if it does, it may not do anything with its held state other than transfer control to it when it's finished loading. And again, transition states might have handles to two different states it's transitioning between. You might decide that the best way to implement two player split-screen is to have your split screen state have two separate child states for top and bottom. So the structure is potentially less like a linked list and more like a tree/DAG.

Any time you want to add more than one game state at a time, start with the lowermost state and "chain" each state as you go to the top.[/quote]
I wouldn't generalize like that. If the main game state wants to go to a menu state, it might pass a handle to itself to the menu state and let the menu state render on top of the main game state. On the other hand if a loading screen state wants to go to a split screen state that contains two states for the top and bottom, it might just create the split screen state and let the split screen state constructor handle the creation of the top and bottom states.

You have to make sure to completely initialize the new states and load their resources before the end of the old state's Update. When the Draw part of the game's loop comes around it will try to draw the new states. If their resources are not properly loaded it will draw an empty screen for one frame, or more likely, crash the program because it's trying to render null resources.[/quote]
This is hardly a concern specific to this kind of state management system. Or game states. For any kind of object, if your constructor doesn't fully construct an object, then you need to be careful how you use it.

Is it an issue that the function stack grows the more states you add, since updates are called recursively through the state list?[/quote]
Even if you somehow nested a hundred or a thousand states, game state functions are generally only called a few times per frame. Generally one update() call and one render() call and maybe a handful of keyboard/mouse/other events. Complex nested render() and update() calls could cause slow down, but the problem generally isn't overhead from the state management system, but that you're you trying to do something extremely complicated that would cause issues no matter what kind of organization you used. For example, doing a live cross fade between two complexly rendered states.
It doesn't have to be a class. You could have a state local variable in your main() function if you wanted.


I am using C# with XNA, so having a Game class is usually required for the framework.

A state may or may not have one or more handles to other states. I wouldn't generalize it to say that every state would have a "lower state" variable. What two states may do with referenced states may be completely different. For example, a pause game state might only invoke the render() member function of a held state. A drop down console state may invoke update() and render() member functions, while a loading screen state might not have a referenced state or if it does, it may not do anything with its held state other than transfer control to it when it's finished loading. And again, transition states might have handles to two different states it's transitioning between. You might decide that the best way to implement two player split-screen is to have your split screen state have two separate child states for top and bottom. So the structure is potentially less like a linked list and more like a tree/DAG.

...

If the main game state wants to go to a menu state, it might pass a handle to itself to the menu state and let the menu state render on top of the main game state. On the other hand if a loading screen state wants to go to a split screen state that contains two states for the top and bottom, it might just create the split screen state and let the split screen state constructor handle the creation of the top and bottom states.[/quote]

Limiting the functions that are invoked when a certain state is present is exactly what I've been doing now. I have certain states that are marked with a bool for being "exclusive". My Pause screen is an example of an exclusive state. When an exclusive state is loaded, all other states stop reading input and stop updating. The loading state is kind of weird but it's a hold-off from the sample XNA code. They treat it as a static object, that is, you would a call LoadingState.Load() function if you want to create the loading screen. I guess it makes sense since you're only expected to have one load screen at a time. The Load function takes an parameter group of states (with C#'s params) you want to the loading screen to transfer control to when it's done.

I can see what you mean when you say the group of states are more like a tree than a list. My initial understanding was that the program's main function begins by updating the top state it points to and each state further down only has a one-to-one-relationship. So instead you are saying each state should have the ability to hold several child states, not just one.

This is hardly a concern specific to this kind of state management system. Or game states. For any kind of object, if your constructor doesn't fully construct an object, then you need to be careful how you use it.[/quote]

You're right about that, I was only saying that I have to re-implement the way my states are being initialized.

Even if you somehow nested a hundred or a thousand states, game state functions are generally only called a few times per frame. Generally one update() call and one render() call and maybe a handful of keyboard/mouse/other events. Complex nested render() and update() calls could cause slow down, but the problem generally isn't overhead from the state management system, but that you're you trying to do something extremely complicated that would cause issues no matter what kind of organization you used. For example, doing a live cross fade between two complexly rendered states.[/quote]

Okay, so it's still just the complexity of the logic in the states rather than the number of nested calls to make those stats run their logic that would make a bigger performance difference.

New game in progress: Project SeedWorld

My development blog: Electronic Meteor


Limiting the functions that are invoked when a certain state is present is exactly what I've been doing now. I have certain states that are marked with a bool for being "exclusive". My Pause screen is an example of an exclusive state. When an exclusive state is loaded, all other states stop reading input and stop updating.

From my point of view that's more complicated than necessary. A pause state that doesn't want anything else to update or process input simply doesn't call the update() method or pass any keyboard/mouse events to any states that it holds. Again, ideally whatever holds the root state handle doesn't know or care how many total states exist. All it does is use the interface for that one state and that one state can pass on or not pass on any of the state function calls it likes.


I can see what you mean when you say the group of states are more like a tree than a list. My initial understanding was that the program's main function begins by updating the top state it points to and each state further down only has a one-to-one-relationship. So instead you are saying each state should have the ability to hold several child states, not just one.
[/quote]
In my code, a state is a full fledged class, with as many variables as necessary, but at the same time, the base state interface is just that: an interface with no implementation. Just by being a class a state can hold an arbitrary number of handles to other states. This can be one other state, no other states, or even an arbitrary number of states via a collection (though I currently can't imagine what kind of use that would be). Whatever structure or relationship a state has with any of the states it hold references to are dictated by that state, and not imposed by any entity from the outside.
Now I'm getting it, I think. With child states being completely optional, and with user-defined placement of child state Update and Draw calls, the order of execution for all of the states is completely up to how you design each state class. That's how it's possible to have a list/tree of states that can either update in forward or reverse order, or like a DAG.

I think using diagrams would explain some cases easier. Won't bother spending time making them in Photoshop, so just gonna use plain text for them. So say I have three states A, B and C, and out of all the ways to design the classes, here two possible ways.
In both examples C has no child, is a child of B, which is a child of A, and the main game loop is going to use A as its root state.

So the first example setup

StateA.Update {
// do stateA stuff here
child.Update
}

StateB.Update {
// do stateB stuff here
child.Update
}

StateC.Update { ... }


Will traverse through the states like this:
StateA, finish update -> StateB, finish update -> StateC, finish update

List-style order of execution.

But if we switch things up and change StateA like so it enters StateB first:

StateA.Update
{
child.Update
// do stateA stuff here
}


The order of updates will now be this:
StateA -> StateB, finish update -> StateC, finish update -> StateA, finish update

And now it looks more like a DAG than a list.

In both cases the game still goes through State A as the root, but now calls StateB.Update first before everything else inside StateA.Update. Then after State C finishes updating, we also finished State B at this point so the game goes back to finishing with StateA.

This means that it IS possible to have the root state to update before everything else as long as you update its child(ren) after all the A's own logic, and all its children going to the end are setup the same way. Then the order of code execution is not simply dictated by how you arrange the child-parent relationships but how you arrange the code itself in all the states.

New game in progress: Project SeedWorld

My development blog: Electronic Meteor

This topic is closed to new replies.

Advertisement