Game State Control

Started by
32 comments, last by Bob Janova 17 years, 7 months ago
In Game Programming Gems 5, James Boer published an article titled "Large-Scale Stack-Based State Machines", which described a simple system for handling game states. States can be accessed at any time via name, as each game state is associated with a string. The state manager maintains a map of string-gamestate pairs (the state registry) in addition to the state stack (which actually contains state names rather than the states themselves). It supports different stack commands: Push a state, Pop an arbitratry number of states, Pop all states and Replace any state in the stack. The "default" state (the bottom of the stack) can never be popped, though it can be replaced. Commands are not executed immediately, but are placed in a command queue that is processed when the game state manager's update method is called. The queue can be flushed anytime a command you send a command.

The game state interface declares 5 methods:

init - called when a state is pushed on the stack and passed the name of the state is being pushed on top of.

term - called when a state is popped from the stack and passed the name of the state that is the new top.

override - called when a state loses its position as the stack top due to a push operation.

resume - called when a state regains the top position due to a pop operation.

update - called by the game state manager's update method

When you call the state manager's update method, it first processes any stack commands in the queue, then iterates the state stack and calls the update method of each state.

This is a very flexible system. State instances don't need to know about other state instances. Anytime you want to push a state onto the stack you do so by name. This can be configured in a text file, scripted, or whatever. You can access any state in the stack, or any registered state in the map, at anytime. You can use the override method to pause rendering, pause update logic, or both. It becomes trivial to implement cascading menu screens, menus with background rendering, or whatever you can think of. One block of effort to implement the system (or adapt Boer's), and then you have a reusable, highly flexible game state management system that is much cleaner to implement that the old switch thing. I think implementing, at most, five methods for each state is peanuts compared to implementing the switch system, particularly for complex interactions.

Boer's implementation was in C++. For kicks, I have ported it to Java, D, and even C. I also implemented a C++ version that uses Lua to implement the game states. The C++/Lua version allows states to be registered via script, rather than hardcoding state registration. The Java version does the same via dynamic instantiation so that states could be defined in a script, an XML file, a properties file, a text file, a database table, or any other source by providing state name-class name pairs (actually, the C++/Lua version could do all of that as well with state name-script path pairs).

If you implement this system along with code to create an app window, change display modes, intialize input, and other basic app plumbing, it becomes trivial to put together test applications, demos and prototypes by implementing a single game state. It doesn't have to be part of a game engine, but such a framework is very, very useful.
Advertisement
Quote:Original post by nsto119
I don't see how this is a negative at all, especially as opposed to having one gigantic Render function for all possible game states.

I'm not sure where you got that from, and it seems to be behind most of your other objections to the procedural programming approach. Where did I advocate having one gigantic Render function?

In terms of commercial games using this approach, I'm afraid that I can't post the code (NDA, copyright, whatnot). Suffice to say that about 1/2-2/3 of the codebases I've seen use the OO-centric method, and 1/3-1/2 use the procedural method. And it really makes very little difference, except that the programmers who used the procedural method obviously lost very little sleep over it compared to many of the posters here.
Quote:Original post by Sneftel
Quote:Original post by nsto119
I don't see how this is a negative at all, especially as opposed to having one gigantic Render function for all possible game states.

I'm not sure where you got that from, and it seems to be behind most of your other objections to the procedural programming approach. Where did I advocate having one gigantic Render function?


Well, I suppose you didn't. I meant though, having one single function with code to render all gamestates.

And just so we're clear, I'm not the "OO or bust" type of person that this site crawls with. I prefer to actually get things done rather than write everything with a perfeclty academic OO model. I program procedurally when I think it makes things easier for me, I just really feel that this problem is made simpler when OO-ified.
I think one of the reasons why I strongly dislike the idea of spending so much time on a clever and robust and feature-laden GameState architecture is a misconception that it somehow forms the "bedrock" of a game's architecture, and the ramifications of that (I'll get back to that last bit at the end). That without a main loop that you could post on the cover of Software Engineering Weekly there's no way to keep the rest of the code in line. It's an intuitive viewpoint; after all, the GameState stuff IS what ends up calling everything else. To some extent I agree with that intuition, inasmuch as a hairy thousands-of-lines Render() function like you describe runs far afoul of No Broken Windows. And yet it is IMHO 180 degrees wrong, because the GameState does so little, and matters so little. It changes between every few seconds and every few hours, you could count the states on your fingers. Combine the complications that often arise between switching between states and the small number of states and you have the very picture of an overcomplicated and underworked state system. Put differently, I see programmers coming up with a procedural conception of how state and state transitions work, then shoehorning that into an OO design which tries to do the same thing. I don't see a need for that.

As I write this, I'm working on the fifth iteration (the third from-scratch iteration) of my job/action dispatch system, which handles AI state, for a game I'm working on. It's heavily OO, with careful separation of responsibilities and an emphasis on no-need-to-look-back subclassing. Each iteration has been clearer and more robust. I've spent such a huge amount of time on this because it MATTERS so much. There are hundreds of states and hundreds of agents, and each agent changes state every few frames in response to a wide variety of internal and external stimuli, with a need to keep outside coordinators informed to keep tasks from slipping between the cracks. I think game programmers need to be able to identify the right tool for the job, and having 5-10 classes for the relatively simple function of remembering whether the game's paused or not sets a programmer down the onerous path of assuming that all tasks which are so simple nevertheless require the red-carpet OO approach.
State:
updateLogic(), updateFrame(), render(), onAdded(), onRemoved(), onKey, onChar
with util methods updateLogicPrevious(), updateFramePrevious(), renderPrevious(), disableMe() and getStateManager()

StateManager:
addState(string name, State*), enableState(string name), disableState(string name)
updateLogic(), updateFrame(), render()

Reminds alot of the gems6 state design Aldacron pointed out but is different from on some key points:
*states are disabled and enabled and not popped. You enable game instead of pushing it.
*this lets you choose the state order/priority, the console is always on the top for example

I got 9 states(splash, game, press key(change binding), meny, in-game meny, pause, console input and history/messages & cheat menu). The states help me keep a clean code and not getting my head wrapped up in the different combinations I can have. With this system all the state needs to worry about is if it should render other states and/or direct input to other states.

Other systems might be good, but this works for me(and I feel good about it ;) )

hth
Quote:Original post by Sneftel
Put differently, I see programmers coming up with a procedural conception of how state and state transitions work, then shoehorning that into an OO design which tries to do the same thing.

Yes. A state change is basically a GOTO, and a "push new state on stack" emulates a procedure call.
I think the use of states is a consequence of the fact that, if you want to decouple timing/framerate control from the rest of the game using a single thread of execution, you need to partition game logic into discrete, frame/timestep-sized chunks; you can't use the normal programming language constructs to go from one chunk to the next.
I *think* it might be possible to avoid that using either coroutines or threads, but coroutines aren't present in most languages, and threads would have to be explicitly synchronized.
I especially like the way some old mobile phones did their menu structure. The idea was that each menu could link to every other menu if desired and backstepping should work from any level. The solution was to use a simple array as a state stack, give each state (or submenu) a number and dispatch the events with a switch. This way each state had only a single function with event id and parameters in a single c function (an oo compiler was out of question on the severly limited system) and the return parameter controlled the operation of the menu stack, which was implemented as an array of integers. The function was called with events like input, render, etc. and the return value could be 'done', 'pop one level', 'pop all levels' or the id of a submenu to be opened. Communication was done by setting global variables or often directly controlling the hardware. The result was a romable code that required very little ram (a few dozen bytes), almost no stack (at most 3 levels) and was fully customizable with the main functions (render menu, process inputs) being in a function toolset. The users even had a menu editor so they could put together their custom menu and customize the shortcuts from the idle screen (this required a writeable menu id list, implemented as a fixed length array). This can be done with oop, but there is really no need to make it overly complex. The windows gui system (gdi) uses the same system, but stores a tree instead of a stack because multiple windows can be open at the same level and they can have multiple children. But in the end it's still written in procedural c.
Quote:Original post by Sneftel
I think one of the reasons why I strongly dislike the idea of spending so much time on a clever and robust and feature-laden GameState architecture is a misconception that it somehow forms the "bedrock" of a game's architecture, and the ramifications of that (I'll get back to that last bit at the end). That without a main loop that you could post on the cover of Software Engineering Weekly there's no way to keep the rest of the code in line. It's an intuitive viewpoint; after all, the GameState stuff IS what ends up calling everything else. To some extent I agree with that intuition, inasmuch as a hairy thousands-of-lines Render() function like you describe runs far afoul of No Broken Windows. And yet it is IMHO 180 degrees wrong, because the GameState does so little, and matters so little. It changes between every few seconds and every few hours, you could count the states on your fingers. Combine the complications that often arise between switching between states and the small number of states and you have the very picture of an overcomplicated and underworked state system. Put differently, I see programmers coming up with a procedural conception of how state and state transitions work, then shoehorning that into an OO design which tries to do the same thing. I don't see a need for that.


I'm firmly in the 'OO is a tool, not a lifestyle' camp. I very much dislike overengineering simple problems. But in this case, I don't see it as overengineering. It's not about being clever, or creating the most uber, awesomely architected main loop possible. It's about code maintenance and reusability. For someone who never needs complex game state interactions, such a system really would be overkill. But handling game states with switch statements can quickly turn into a maintenance nightmare when you factor in complex interactions.

The time required to implement a state management system is miniscule, particularly when using another implementation as a guide. If it were a matter of days rather than a couple of hours, I'd be inclined to skip it. But since the cost of implementation is so low, and the benefits to reusability and maintenance so high, it's a no-brainer. It's a one-off task. Once the framework is in place, implementing states is not much more work than adding cases to a switch statement. And because of the simplicity of the problem the system doesn't need to be anywhere near as complex as the AI state management you describe. It's a simple solution for a simple problem.
I prefer to model my user interface states just like a normal GUI. A state is just a fullscreen window, and they all inherit from the same thing that a Button inherits from. It makes input and other event handling much simpler. How do the procedural guys implement their widgets, and do you need a separate switch statement for all your events (onRender, onChar, onClick...)?

Game states have a separate state system. If I'm maintaining the current step in a sequence of events, then an enum with switch statement is probably best. Something that controls the entire behaviour of a game (like deathmatch or capture the flag) may deserve it's own class. Pausing is just a separate flag. Just because you're in the menu doesn't mean that the game is paused.
Quote:Original post by hh10k
I prefer to model my user interface states just like a normal GUI. A state is just a fullscreen window, and they all inherit from the same thing that a Button inherits from. It makes input and other event handling much simpler. How do the procedural guys implement their widgets, and do you need a separate switch statement for all your events (onRender, onChar, onClick...)?


The best example is the windows gdi. It's fully procedural and only needs one callback per window. (wigdets are windows too) Most widgets have predefined window functions, user made ones have to be defined by the user. The function in the window callback has a switch that selects the event. When someone in a user defined window wants to use the default method, he can call it. The whole thing works just like single inheritance with a single virtual method based event handling. Imho this is the most simple way to do it in oop, except that the compiler generates the single entry vtables instead of maintaining a function pointer. Every window event is caused by an event queue, simply storing window events like draw, input and even events generated by a previously run event hander. For example a mouse move event handler can generate resize events that are fed back to the same window. The required structures are: window state structure (oop: window object instance), message queue (oop: also called the message queue), message structures (stored in the queue, oop: event objects).

This topic is closed to new replies.

Advertisement