Game states

Started by
22 comments, last by EdR 15 years, 11 months ago
If you where to have an update and a render function then you would need two switch statements. Another way would to just have a simple game loop function which is called each frame, in there you would have one switch statement:

void GameLoop(){     switch(currentGameState)     {          case GAMESTATE_MAINMENU:          {               UpdateMainMenu();               RenderMainMenu();          }break;          case GAMESTATE_GAME:          {               UpdateGame();               RenderGame();          }break;          case GAMESTATE_CREDITS:          {               UpdateCredits();               RenderCredits();          }break;          default:break;     }}
Advertisement
Quote:Original post by Shadowwoelf
Which would end up being a lot of functions...

Nah. You'll have number_of_function_slots * number_of_states functions. You can't really get around this number, because this is exactly the number of pieces of functionality you've asked your state machine to access. You'll end up implementing this many functions regardless of whether you choose an all-singing-all-dancing OO extravaganza or a collection of switch statements. (Of course, you can double up these functions between states if two states do the same sort of rendering, for instance.)
Any comments on Lazy Foo's way of doing a state machine?

http://lazyfoo.net/articles/article06/index.php
It's about par for the course for OO-based state machines. At a glance, it involves about twice as much code as doing it the simple way, for (in the case of a game state FSM) no added functionality. It's also a great example of how shoehorning everything into a single state machine decreases readability.
Quote:Original post by Sneftel
typedef std::stack<GameState> SimpleCleanFacilityForAStateStackInAConceptuallyCleanManner;


How's that? Use std::vector instead if you don't like container adapters (I sure don't).
LazyFoo's method is, as Sneftel says, about par for the course, but that is more indicative of the problems of "par for the course" than object-oriented development. I've used similar systems as his before and found them inflexible. You might as well be using a delegate or function pointer to a procedure to do the same thing! However, I've begun using the call stack as the state stack, with surprisingly pleasant results. (This is a natural outgrowth of using fully object-oriented languages such as Java and C#; I don't think I would have thought to gone this route in a multiparadigmatic language such as C++.)

In most decent modern languages you can repurpose the call stack to serve better as such, gaining a number of useful benefits such as the aforementioned modal boxes. An external stack reduces these benefits and you have to rely on hackish sub-states within each game state for message passing between states. Conceptually, you can track program flow more easily--states simply pause. when they go out of state. It's perhaps less of a traditional finite state machine, but accomplishes the same task efficiently and cleanly.

Quote:State machines with many similar, easily and regretlessly parameterizable states are ideal for an OO approach. the "game state" machine does not fit that mold.
I disagree; I think it does fit that mold just fine.

I use something similar to the following in the current game I'm working on (code examples offhand, but C#-ish):

public class StateController{    protected Boolean isRunning = true;        public StateController() { }    public abstract void Update();    public abstract void Draw();    public virtual void Go()    {        while (this.isRunning)        {            this.Update();            this.Draw();        }    }}


This lets me do something like this bit below, within SomeStateController and using a DialogBoxController inherited from StateController:

public override void Update(){    // do some stuff    DialogBoxController dBox = new DialogBoxController("Do you want to answer Yes or No?", DialogBoxController.Buttons.YesNo);    dBox.Go();    Debug.Print(dBox.Result); // will echo something like "Namespace.DialogBoxController.ButtonResult.Yes")    // do more stuff}


It provides a clean, consistent interface from state to state, and eliminates sub-state "catch it on the next update" hackery. I mean no disrespect, Sneftel, but I think you haven't thought this through. There are benefits to this approach, and I think you're trivializing it due to some dislike of the process. Is there something wrong with this approach that a procedural approach does better?
http://edropple.com
Quote:Original post by EdR
In most decent modern languages you can repurpose the call stack to serve better as such, gaining a number of useful benefits such as the aforementioned modal boxes. An external stack reduces these benefits and you have to rely on hackish sub-states within each game state for message passing.

The phrase "most decent modern languages" reminds me a bit of the "No true Scotsman" fallacy, particularly given C++'s prevalence in the area. In any case, using the callstack for this involves a great deal of boilerplate code if you want to separate rendering and game code, particularly if the decent modern language in question doesn't give you absolutely perfect reflection facilities. Additionally, it's only really a clever idea when you expect to be stacking your states more than one or two deep, which is extremely rare in games. Finally, the question of whether states are in callstacks or external variables doesn't really bear on the OO/non-OO question.

Quote:
Quote:State machines with many similar, easily and regretlessly parameterizable states are ideal for an OO approach. the "game state" machine does not fit that mold.
I disagree; I think it does fit that mold just fine.
Not sure how to respond to this, since you haven't given any support for the various attributes.

Quote:I mean no disrespect, Sneftel, but I think you haven't thought this through. There are benefits to this approach, and I think you're trivializing it.

Having dealt with many, many game code organizations, and many OO and non-OO game state representations, I can tell you categorically that I've thought this through much more thoroughly than I would care to have. [smile]
Quote:The phrase "most decent modern languages" reminds me a bit of the "No true Scotsman" fallacy, particularly given C++'s prevalence in the area.
I was including C++ in this, as I don't see why C++ would have any trouble with it.

Quote:In any case, using the callstack for this involves a great deal of boilerplate code if you want to separate rendering and game code, particularly if the decent modern language in question doesn't give you absolutely perfect reflection facilities.
Boilerplate code only has to be written once, though, with inheritance (well, if you're reasonably clever with your object structure). :) To address your other point, I can see how reflection facilities would help, but I don't use them for my own--I do most of the rendering work in the StateController base class's Draw function and only do data manipulation (sprite positioning, etc.) in the derived class's.

Quote:Additionally, it's only really a clever idea when you expect to be stacking your states more than one or two deep, which is extremely rare in games.
Depends on the genre, I'd think. I'm working on a turn-based strategy game that requires repeated code such as UI handlers, so this works pretty well for me. For KillFest ShootEmUp 3000, it'd be almost certainly less valuable.

Quote:Finally, the question of whether states are in callstacks or external variables doesn't really bear on the OO/non-OO question.
No, but the relatively tight packaging of methods and data can, at least in this case, only help. Encapsulation Is Good(tm). :)

Quote:Not sure how to respond to this, since you haven't given any support for the various attributes.
Similar: the external interface is going to be the same for more or less any state, at an abstract level, yes? Update/Logic/whatever, and Draw/Render/whatever. Although, with a setup like this, your Go/BeginState function can call whatever's necessary, and other states don't need to know about it anyway.

Easily and regretlessly parameterizable: I don't see how this would not apply, could you give an example?

Quote:Having dealt with many, many game code organizations, and many OO and non-OO game state representations, I can tell you categorically that I've thought this through much more thoroughly than I would care to have.
Sorry, I was editing my post when you replied, so I'll ask the question again: Is there something wrong with this approach that a procedural approach does better?
http://edropple.com
I tend not to make a habit of this, but I'm going to have to (at least, partially) disagree with Sneftel on this one.

I think state objects are a good thing, for a number of reasons:
  1. They keep the logic/resources/etc bundled up and easy to modify without the worry of affecting other states.
  2. Beyond a few states, perhaps 3-5, they make code more readable in my opinion.
  3. They allow you to destroy states which are used infrequently.
  4. State objects allow you to easily support transitional actions (things to do when a state is made active/inactive) such as OnPush()/OnPop() which suspend/restore the state respectively.
  5. Destruction and suspension of states allow associated resources to be freed or marked for garbage collection easily.

If the game is very simple, then a simple switch statement may do -- thats the way we used to do it, after all.

When the game becomes more complex, however, large switch statements, or nested switch statements (often across a function call) can become difficult to follow and they are always more prone to changes in one state affecting another. Further, if your life would be made simpler being able to have transitional actions, then state objects are the way to go -- you wouldn't want to add several "transition states" for each game state, would you?

For me, I've found the stack-based approach to be most useful overall, but the requirements for my games have typically been the go-in/back-out variety which maps precisely to a push/pop setup. For more free-form transitions, a state-machine approach might make sense. I've also found that the hybrid approach, where some states on the stack have small, internal state machines driving them, to be useful -- the battle state in a jRPG for instance.

One issue I've encountered is that, in my stack-based system, I've run across situations in which a state needs to pop itself. In my last system, a state object is immediately destroyed when it is popped, so the state object was essentially calling delete(this) -- which *can* be done, but only safely under very specific constraints, some of which are difficult to guarantee. In the end, this is best avoided, and can be by deferring destruction until after the state is no longer being executed. This can be implemented in a number of ways -- a modified stack, message-passing, return codes...

throw table_exception("(? ???)? ? ???");

Quote:Original post by EdR
Quote:The phrase "most decent modern languages" reminds me a bit of the "No true Scotsman" fallacy, particularly given C++'s prevalence in the area.
I was including C++ in this, as I don't see why C++ would have any trouble with it.

Quote:In any case, using the callstack for this involves a great deal of boilerplate code if you want to separate rendering and game code, particularly if the decent modern language in question doesn't give you absolutely perfect reflection facilities.
Boilerplate code only has to be written once, though, with inheritance (well, if you're reasonably clever with your objectstructure). :) To address your other point, I can see how reflection facilities would help, but I don't use them for my own--I do most of the rendering work in the StateController base class's Draw function and only do data manipulation (sprite positioning, etc.) in the derived class's.

I think we might be talking at cross-purposes. When you mentioned "using the callstack as a state stack", I took that to mean using normal code flow facilities to implement long-running game states. In event-driven or constant-update scenarios, that requires that either the state stack be running in its own execution thread, or that the event loop operate above the state stack in the call stack. The first one I've seen done, but it's only really pleasant to do if you have cooperative multithreading. The second one I've rarely actually seen, so I doubt if you're talking about that one, but let me know if you are.

Quote:
Quote:Additionally, it's only really a clever idea when you expect to be stacking your states more than one or two deep, which is extremely rare in games.
Depends on the genre, I'd think. I'm working on a turn-based strategy game that requires repeated code such as UI handlers, so this works pretty well for me. For KillFest ShootEmUp 3000, it'd be almost certainly less valuable.
What's an example of deeply stacked states in the TBS game?

Quote:Similar: the external interface is going to be the same for more or less any state, at an abstract level, yes? Update/Logic/whatever, and Draw/Render/whatever. Although, with a setup like this, your Go/BeginState function can call whatever's necessary, and other states don't need to know about it anyway.
Ah, but you missed the most important one: "many". The mere machinery of an OO state stack, with all the trimmings, easily runs to at least a couple hundred lines of code beyond what you'd be doing without such a thing. The organizational benefits outweight the costs eventually, but only when you've got dozens and dozens of states. Properly normalized game state machines don't tend to have more than half a dozen.

Quote:Easily and regretlessly parameterizable: I don't see how this would not apply, could you give an example?
It applies here only in a trivial fashion, in that there's nothing to parameterize. Consider a state machine with states A, B, C1, C2, C3, ... C253, and D. Here it makes complete sense to represent this as four state classes, one of which is parameterized on an individual integer.

Quote:Sorry, I was editing my post when you replied, so I'll ask the question again: Is there something wrong with this approach that a procedural approach does better?
No. The decomposition from a procedural approach to an OO approach is simple and mechanical, so there can't be. The benefits of a procedural approach are simplicity, and (more fundamentally) the opportunity to gain a better understanding of the relationship between a piece of code's complexity, its importance, and its pervasion.
Quote:Original post by Sneftel
Quote:Original post by EdR
Quote:The phrase "most decent modern languages" reminds me a bit of the "No true Scotsman" fallacy, particularly given C++'s prevalence in the area.
I was including C++ in this, as I don't see why C++ would have any trouble with it.

Quote:In any case, using the callstack for this involves a great deal of boilerplate code if you want to separate rendering and game code, particularly if the decent modern language in question doesn't give you absolutely perfect reflection facilities.
Boilerplate code only has to be written once, though, with inheritance (well, if you're reasonably clever with your objectstructure). :) To address your other point, I can see how reflection facilities would help, but I don't use them for my own--I do most of the rendering work in the StateController base class's Draw function and only do data manipulation (sprite positioning, etc.) in the derived class's.

I think we might be talking at cross-purposes. When you mentioned "using the callstack as a state stack", I took that to mean using normal code flow facilities to implement long-running game states. In event-driven or constant-update scenarios, that requires that either the state stack be running in its own execution thread, or that the event loop operate above the state stack in the call stack. The first one I've seen done, but it's only really pleasant to do if you have cooperative multithreading. The second one I've rarely actually seen, so I doubt if you're talking about that one, but let me know if you are.
I am referring to the former. I have an input thread running separately, generating input events into a list; the base functions of the StateController class parses them, invokes UI handler delegates, and passes those not handled by the UI (and thus being handled by the main game itself) into the Update() function of the derived state class. For a menu state, these extra input values are tossed out; a click that isn't on a UI widget is unimportant, for example, as are keyboard events not caught by a widget like a text box.

While threading isn't easy, this sort of black-box threading is really quite trivial to implement; I would be very worried about a programmer who couldn't grasp this sort of simple design. I'm not sure in C++, but I can't see it being that difficult; in C# or Java, it is perhaps five lines to call the thread, then just wrapping input events in the format the rest of your program understands and working from there.

Quote:
Quote:
Quote:Additionally, it's only really a clever idea when you expect to be stacking your states more than one or two deep, which is extremely rare in games.
Depends on the genre, I'd think. I'm working on a turn-based strategy game that requires repeated code such as UI handlers, so this works pretty well for me. For KillFest ShootEmUp 3000, it'd be almost certainly less valuable.
What's an example of deeply stacked states in the TBS game?
Submenus. I modeled my GUI paradigm on the "Form" class of System.Windows.Forms; each separate "form" is a separate state. Occasionally I go about 4-5 deep, plus modal dialog boxes in some areas (main game map, "headquarters" screen, advisor panel, advanced functions for that advisor with which I don't want to clutter up the information screen, and so on). Instead of having to find a suitable place to pause the main game state, I just call out to the modal menus (which save the current screen surface as a background for any drawing effects over it), and continue operation from wherever they were invoked once the user's done there.

Quote:
Quote:Similar: the external interface is going to be the same for more or less any state, at an abstract level, yes? Update/Logic/whatever, and Draw/Render/whatever. Although, with a setup like this, your Go/BeginState function can call whatever's necessary, and other states don't need to know about it anyway.
Ah, but you missed the most important one: "many". The mere machinery of an OO state stack, with all the trimmings, easily runs to at least a couple hundred lines of code beyond what you'd be doing without such a thing. The organizational benefits outweight the costs eventually, but only when you've got dozens and dozens of states. Properly normalized game state machines don't tend to have more than half a dozen.
Denormalization is sometimes valuable, though. I don't want to stuff all my menus in a single state under this approach; it's far cleaner and far simpler to just make a new state for each one.

Quote:
Quote:Easily and regretlessly parameterizable: I don't see how this would not apply, could you give an example?
It applies here only in a trivial fashion, in that there's nothing to parameterize. Consider a state machine with states A, B, C1, C2, C3, ... C253, and D. Here it makes complete sense to represent this as four state classes, one of which is parameterized on an individual integer.
I agree. I do this.

(new ParameterStateController(1)).Go();(new ParameterStateController(2)).Go();...(new ParameterStateController(253)).Go();


Nothing says you can't have a constructor take an argument, does it? :)

Quote:
Quote:Sorry, I was editing my post when you replied, so I'll ask the question again: Is there something wrong with this approach that a procedural approach does better?
No. The decomposition from a procedural approach to an OO approach is simple and mechanical, so there can't be. The benefits of a procedural approach are simplicity, and (more fundamentally) the opportunity to gain a better understanding of the relationship between a piece of code's complexity, its importance, and its pervasion.
I don't really see the "simplicity" argument here, except in the case of programmers who don't really know object-oriented development very well. (Not referring to you, just in general.) I would also say that the object-oriented approach allows for more natural linking between states from a conceptual standpoint as well.
http://edropple.com

This topic is closed to new replies.

Advertisement