I was thinking about how to implement game states into my game. The main things I want for it are:
Semi-transparent top states-being able to see through a pause menu to the game behind
Something OO-I find this easier to use and understand the theory behind, as well as keeping orgranised and adding more to.
I was planning on using a linked list, and treat it as a stack. This means I could access the state below for the semi-transparency.
Plan: Have the state stack be a linked list of pointers to IGameStates. The top state handles its own update and input commands, and then has a member isTransparent to decide whether the state underneath should be drawn.
Then I could do:
states.push_back(new MainMenuState());
states.push_back(new OptionsMenuState());
states.pop_front();
To represent the player loading, then going to options, and then main menu.
Is this a good idea, or...? Should I look at something else?
Thanks.
Answer
I worked on the same engine as coderanger. I have a differing viewpoint. :)
First, we did not have a stack of FSMs - we had a stack of states. A stack of states makes a single FSM. I don't know what a stack of FSMs would look like. Probably too complicated to do anything practical with.
My biggest problem with our Global State Machine was that it was a stack of states, and not a set of states. This means, e.g., .../MainMenu/Loading was different than .../Loading/MainMenu, depending on if you got the main menu up before or after the loading screen (the game is asynchronous and loading is mostly server-driven).
As two examples of things this made ugly:
- It led to e.g. the LoadingGameplay state, so you had Base/Loading, and Base/Gameplay/LoadingGameplay for loading within the Gameplay state, which had to repeat much of the code in the normal loading state (but not all, and add some more).
- We had several functions like "if in character creator go to gameplay; if in gameplay go to character select; if in character select go back to login", because we wanted to show the same interface windows in different states but make the Back/Forward buttons still work.
Despite the name, it was not very "global". Most internal game systems did not use it to track their internal states, because they didn't want their states mucking about with other systems. Others, e.g. the UI system, could use it but only to copy state into their own local state systems. (I would especially caution against the system for UI states. UI state is not a stack, it's really a DAG, and trying to force any other structure on it is only going to make UIs that are frustrating to use.)
What it was good for was isolating tasks for integrating code from infrastructure programmers who didn't know how the game flow was actually structured, so you could tell the guy writing the patcher "put your code in Client_Patch_Update", and the guy writing the graphics loading "put your code in Client_MapTransfer_OnEnter", and we could swap certain logic flows around without much trouble.
On a side project, I have had better luck with a state set rather than a stack, not being afraid to make multiple machines for unrelated systems, and refusing to let myself fall into the trap of having a "global state", which is really just a complicated way to synchronize things through global variables - Sure, you're going to end up doing it near some deadline, but don't design with that as your goal. Fundamentally, state in a game is not a stack, and states in a game are not all related.
The GSM also, as function pointers and non-local behavior tend to do, made debugging things more difficult, though debugging those kind of large state transitions wasn't very fun before we had it either. State-sets instead of state-stacks does not really help this, but you should be aware of it. Virtual functions rather than function pointers may alleviate that somewhat.
No comments:
Post a Comment