Overview
The subject of how one should organize a game has been appearing a lot lately. Mainly these questions are focused on how to organize all the different parts of the game, such as the menu, the gameplay area, the bonus rounds, the story screens, etc.
The most common proposal I see are switch cases for each state, with an ID assigned to each state since nothing else would really work for a switch case. It would look something like this:
void UpdateGame() { switch (curGameState) { case MAINMENU : { MainMenu::Update(); } case OPTIONSMENU : { OptionsMenu::Update(); } case GAMEPLAY : { GamePlay::Update(); } … } }
This is obviously one of the worst possible ways to accomplish this goal, and defeats the purpose of using C++ or any other object-oriented language.
There are many ways to go about this, and this article is intended to provide one which is a much healthier alternative.
Goals
Our goal is a clean way to switch between parts of the game to which we refer as “states” from here forth. Examples of states would be the main menu, options menu, title screen, story screen, credits screen, gameplay screen, bonus rounds, etc. Any part of the game that can cleanly stand on its own without needing to know about any other section of the game. In some implementations, changing from the main menu to the options menu won’t cause a fade-out, but simply one set of buttons to move off the screen and another to move on. In this case the entire menu system would be its own state and each individual menu would still be managed modularly, just not via the state machine.
Some have asked me why we need to break the game into states at all. The benefits are fairly clear. Breaking the game into these types of modular components helps with development in that it allows each team member to work inside his or her little black box, reducing the chances of stepping on someone else’s code.
One way in which it does that is to enforce the concept of modularity, which is not only an intuitive and comfortable way to think about each part of the game (as proved by how many people attempt to break their games into states via switch cases—it is just how we humans want to think about the game), but it also gives developers a clear idea where to put the data needed for the functionality they are creating, which will be as regular members of the classes they are making (CMainMenu, for example) rather than as globals tossed randomly about.
It also allows the core engine to enforce (or rather simply expect) some policies that allow better memory management. If a state has correctly released all memory that it allocates, the memory manager can assume that the RAM before and after the state has run is exactly the same. Any extra allocations can be reported as leaks, which provides a very helpful early-warning system and isolates the leak to specifically one person’s code, and even more specifically to one part of the game.
Additionally, if the game does release all of its RAM properly for each state, fragmentation is avoided. Breaking the game into its individual states simply provides a lot of opportunities for organization and debugging, and all with no drawbacks—if you think breaking the game up into states this way is silly, just use one state for your entire game.
How?
When new people ask the best way to manage their game states, they aren’t usually concerned with debugging features and being isolated from teammates. They just want an intuitive way to manage the game states, but just don’t know enough C# or C++ (or what-have-you) to know how to go about it.
My method takes of advantage of polymorphism. Since this is mainly for beginners I won’t be using such big words. Polymorphism, in the most beginner-friendly description that applies here (pedantic Nazis be warned), is basically the act of overriding virtual functions on a base class.
Obviously, to do that, we would need to begin with a base class that has a few virtual functions on it that we can override.
// For cross-platforming. typedef LSVOID void; typedef LSUINT32 unsigned int; typedef LSBOOL int; // For alignment. Could be "bool" for most people. #define LSENEW new
class CState { // Also, the state manager needs to have access to our private parts. friend class CStateMachine; public : // == Various constructors. CState(); virtual ~CState(); // == Functions. virtual LSVOID Init(); virtual LSVOID Destroy(); virtual LSBOOL Tick(); virtual LSBOOL Draw(); protected : // == Members. /** * Local time which keeps track of time since the start of this state. */ CTime m_tLocalTime; /** * This state ID. */ LSUINT32 m_ui32StateId; };
I felt posting code would be the most helpful since it is extremely obvious what each of the 4 functions do. Init() is called when the state is created and Destroy() when it is destroyed. Allocate and delete resources here. Between these calls, Tick() and Draw() are called repeatedly as the state is active.
I took the liberty of posting a state ID and a local time counter, because again everything’s purpose is fairly intuitive. The local time is just the time that the state has been running.
What is not intuitive is why we are using Init() and Destroy() instead of constructors and destructors. I did declare constructors and destructors for completeness, but they are empty. Why? Because we need access to a bunch of managers and things to help us load resources.
Beginners are tempted to make those kinds of things into singletons, raw globals, or etc. This is extremely bad practice in general and limits your game to one instance. It might be hard to imagine why having more than one instance of a graphics class would ever be desirable since you only have one game running, right? Just wait until you start making tools with Qt and you realize that each OpenGL View has its own OpenGL context, and the resources you made when one context was activate are no longer applicable in the new context. It can also help with debugging and it is just conceptually more sound.
There are many topics available online for why singletons are evil, and if you are curious you can look into it on your own.
All that being said, I would rather pass what the state class needs than to have it trying to go looking all over the place and picking things without thought. But of course I don’t want to pass tons of pointers. There should be a cleaner way to get that information to the states.
It just so happens there is not only a need for various managers to be passed down, but also game-specific data that is understood only by the game itself (not the engine), and we can kill 2 birds with 1 stone.
// == Functions. virtual LSVOID Init( CGame * _pgGame ); virtual LSVOID Destroy( CGame * _pgGame ); virtual LSBOOL Tick( CGame * _pgGame ); virtual LSBOOL Draw( CGame * _pgGame );
The CGame class has the job of masterminding the operation of the game. It is only natural that that would be the place to put pointers to the keyboard input, touch input, sound manager, etc.
CGame is a class provided by the engine. Inheriting from CGame class when you make your own game is a way to add new functions and features to the CGame class, all of which will be passed down to all of your states for each of the 4 times they would ever need it. This means you can keep a list of high scores on your own “CMyGame” class and all parts of the game could access it if they have reason.
class CMyGame : public CGame
class CHighScoreScreenState : public CState
LSVOID CHighScoreScreenState::Init( CGame * _pgGame ) { _pgGame->GetSoundMan()->ClearAllSounds(); // Last state should have done this but this is just an example. CMyGame * pmgMyGame = static_cast(_pgGame); // And now Anything you added to your CMyGame class can be accessed by your state. }
Generating States
The engine provides a base CState class and CGame class, from which both can be inherited. Since the states you make have meaning only to you and not the engine, you will need to also make a way to associate state ID values with the custom state classes you have created.
I use the factory pattern. This involves a switch case with enumerated values, but that is nothing new to anyone who has considered the initial switch-case example I presented.
/** * Class CStateFactory * \brief Generate unique states. * * Description: Generate unique states. */ class CMyStateFactory : public CStateFactory { // All is public. This class has no secrets. public : // == Functions. /** * Create a state by ID. * * \param _ui32Id The unique ID of the state to create. * \return Returns the created state or NULL if none could be created. */ virtual CState * GetState( LSUINT32 _ui32Id ) const; };
/** * Create a state by ID. * * \param _ui32Id The unique ID of the state to create. * \return Returns the created state or NULL if none could be created. */ CState * CMyStateFactory::GetState( LSUINT32 _ui32Id ) const { switch ( _ui32Id ) { case LST_S_SOUND : { return LSENEW CSoundTest(); } case LST_S_BENCHMARK : { return LSENEW CBenchMark(); } case LST_S_MODELTEST : { return LSENEW CModelTest(); } case LST_S_TERRAINTEST : { return LSENEW CTerrainTest(); } case LST_S_SPRITETEST : { return LSENEW CSpriteTest(); } } return CStateFactory::GetState( _ui32Id ); }
The engine provides a base class called CStateFactory, and CMyStateFactory inherits from it. The CGame class holds a CStateFactory * member. All we need to do is make it point to a CMyStateFactory object instead. This can be done anywhere during start-up once you have made an instance of CMyGame (or even CGame if you don’t want to add any features to the CGame class). In my case, I create the object on the stack inside Main().
INT APIENTRY wWinMain( HINSTANCE /*_hInstance*/, HINSTANCE /*_hPrevInstance*/, LPWSTR /*_lpwsCmdLine*/, INT /*_iCmdShow*/ ) { // Do engine initalization. … CMyGame mgGame; mgGame.SetStateFactory( LSENEW CMyStateFactory() ); …
The Runtime Logic
If you are only trying to make a basic game, it is enough to have only one active state at a time. We have a factory that can turn an ID value from an enumeration into a CState *, and those pointers are to classes that are designed specifically for your game. You could handle the logic for initializing, destroying, and ticking the current state from the CGame class, but it is more organized to just make a CStateMachine class that performs this job, and add it as a member of CGame. But the CStateMachine member does not have access to the state factory. You could organize it so that it does, but I preferred to keep my state machine unaware and uncaring of just how the states come into existence. All it knows is that it is being told to make a state its current state and it handles the rest.
/** * Set the next state. * * \param _ui32Id ID of the state to set. * \return Returns false if a memory error occurred while trying to create the new state. */ LSBOOL CGame::SetState( LSUINT32 _ui32Id ) { CState * psNewState = m_psfStateFactory->GetState( _ui32Id ); return m_smStateMachine.SetState( psNewState, this, _ui32Id ); }
/** * Set a new state. Removes the current state to make room for the new one. * * \param _psState The new state to make active. * \param _pgGame The game class. * \param _ui32State The state ID. * \return Returns true if the state was set. */ LSBOOL CStateMachine::SetState( CState * _psState, CGame * _pgGame, LSINT32 _ui32State ) { if ( !RemoveCurState( _pgGame ) ) { return false; } m_psCurState = _psState; if ( !_psState ) { return true; } // NULL states end here. m_psCurState->m_ui32StateId = _ui32State; m_psCurState->Init( _pgGame ); return true; }
/** * Destroys the current state. * * \param _pgGame Game object to pass to the states before destroying them. * \return Returns true in this simplified version. The real function could return false. */ LSBOOL CStateMachine::RemoveCurState( CGame * _pgGame ) { if ( !m_psCurState ) { return true; } m_psCurState->Destroy( _pgGame ); LSEDELETE m_psCurState; m_psCurState = NULL; return true; }
From this point forward it is just a matter of calling Tick() and Draw() on whatever the current state is. For my engine I have good reason for that to be delegated to the CStateMachine class instead of having the CGame class get the current state and do it itself, but if I stripped away the fat to post an example here, it would be nothing more than the CGame class calling “m_smStateMachine.Tick( this )” and then having the state machine simply pass that pointer on to the current state via “m_psCurState->Tick( _pgGame )”, which would appear redundant.
For a simple game you are free to do it either way, but the idea is simple: Keep calling Tick() and Draw() on the current state for as long as the game is running. Thanks to the magic of virtual functions, what actually happens and what appears on the screen will be totally different for each state of the game, and states are modular enough to safely jump from any state to any other state simply by telling the _pgGame pointer to set the next state with the given ID. The catch is that the CGame class must store this information until the beginning of the next frame rather than switch right away. That would cause the current state to be deleted while it is still inside its Tick() function.
It is trivial to pass information from one state to another, so I leave that as an exercise for the reader. But rest assured, states can communicate with each other quite easily.
Conclusion
Breaking the game into states is a very good way to organize your project intuitively and offers potentials in debugging as well. But it can become an unmaintanable mess if done improperly and result in more headache than happiness. Exploiting the features of an object-oriented language can yield a very compact and comprehensive solution to the alternative of endless switch cases, and helps clearly define where the engine ends and the game begins.
L. Spiro
Thanks, this has been really helpful!
L Spiro, you should provide a small example of passing info from state to state, as your idea of trivial may not be the same as your readers. Your experience in the industry trivializes those tasks that may seem monumental to those without it.
Okay, I will add some more details soon when I have time. It will be in a new topic so that this one can remain short and to-the-point. I expect I can get it written before the weekend.
L. Spiro
Hey! You should make a short example of passing data from one state to another. What might seem trivial to someone with your experience may not be so for someone who doesn’t have said experience
Sure, I will have something up in a new topic before or on the weekend.
L. Spiro
Preface:(am a programming noob), why the LSVOID instead of void?
As you may know, for cross-platform compatibility, types such as int, short, etc., are often typedef’ed to ensure consistent sizes across compilers.
void obviously is not prone to this problem, so you basically have your choice as to whether or not you want to do it. The engine at my office does not.
I prefer to typedef it for the sake of consistency. If 90% of the types I use are going to begin with LS, be capitol, and not be blue in the editor, the remaining 10% seems out of place and tacky.
So basically it is up to each person.
L. Spiro
thank you very much for the explanation!
Great article,
I just have a question –
How is the CGame class linked to the Engine?
-Does it contain an Engine instance and then drives the engine in its loop?
OR
- Does the Engine own the CGame class and calls the right functions during the engine’s loop?
Basically am asking how this design would be used with an engine?
Thanks
The engine is simply the collection of all the other parts—including an instance of CGame—coming together. The point where those meet could be your CEngine class.
Its duty is to initialize and destroy all of the parts of the engine that it brings together. This will typically be a static class, not instanced, because no matter how many games you want to run, you still only have one memory manager (if you plan to overload operator new then you have no choice but to make a static memory manager instance).
Here is how my wWinMain() looks in full:
Creating the CGame instance is up to you. No other parts of the engine actually call any of the CEngine functions, which means those calls are also up to you, and the 4 calls you see here are the only calls in the class.
Once each system is initialized, they run independently from that point forward.
L. Spiro
Wow, more information that I expected. Thanks.
Spiro, Where do you handle state transitions? is it handled by different states or by the state Manager?
Thanks
Anything fancy here would just be over-engineering. States themselves call _pgGame->SetNextState( LSE_OPTIONSMENU /* For example. */ ) directly at any time they choose to move on to the next state.
States can’t call _pgGame->SetState() directly because that would cause the state to be destroyed while it is still executing code. SetNextState() saves the data until the start of the next frame and then changes to the new state then, when it is safe to delete the current state.
L. Spiro
“Just wait until you start making tools with Qt and you realize that each OpenGL View has its own OpenGL context….”
How would this engine setup be used to create tools?
The CGame class is what drives the game/app, would it then make the 4 other windows/views used in a typical tool/editor?
How would it manage redirecting window messages to its views?
Am asking because it would be great to use the same approach for games and tools.
In short, this setup won’t be used for tools. You will still need a CGame class probably, but it won’t be running in its own loop. And there probably won’t be any CState classes; they don’t make sense if the CGame is not running its loop.
For event-driven UI’s you will have no choice but to make a few structural changes, but as long as the rest of your engine is decoupled as much as possible from the overall architecture this won’t impact you so much. For example, if you have a CSceneManager, nothing about how that is used will change. Only how it is updated—one way is via the game loop and the other is by responding to UI events.
Input messages wouldn’t be sent through the typical CGame input handlers. There is not enough context for that to work. Using Qt for example, your main window will inherit from QGLWidget and in that class just add your CSceneManager object. Input will be retrieved from messages sent to your widget and you will translate them into camera movements etc. on your own.
It sounds as though a lot changes between tools and games. While there are a lot of differences, they are fairly minor. The of bulk the work is done for you by CSceneManager and other classes, and all of that is the same.
L. Spiro
Very nice article L. Spiro, it would prove a great benefit to those learning about game development. May I offer a suggestion to you that would improve the quality a bit more. It’s a bit tough for many newbies to sometimes follow tutorials that have nothing but code shown at each section.
1)
When you use “LSBOOL” or “LSE_CALL” I know what all of this means as I’ve done cross platform programming, but you make the assumption the user does or knows the difference. Better to define these things the first time they appear in your code snippet. I couldn’t tell you how many times I’ve written tutorials, and had people ask me questions related to keywords and their context usage.
2)
I’ve been writing tutorials far longer than you have and I’ll share a nice tip for you to consider. If you’ve spent the time to copy in this code, take at least a few minutes and make an image showing the flow of the structure you wish to present.
It’s less intimidating to visually see some sort of structure defined before jumping into reading your code for a new game programmer. For instance, show how CGame and CState along with the CStateManager flow using arrows; It will make a huge difference in all your future articles discussing these types of concepts.
Conclusion)
If you take my advice I think it would greatly improve where your line of thought is before anyone even reads your code. Remember, data without context is noise.
Kudos on the article anyway.
P.S, I used to work as an Engine Anti-Cheat specialist, which branch location of UbiSoft were you located?
A. Turner
Hello and thank you.
#1: People have asked me too, so I guess it is shame on me for being lazy. I will probably start just replacing them with their raw values.
#2: Actually I would like some advice on any software you would recommend for making the kinds of diagrams suitable for these tutorials. I use Photoshop but that probably isn’t the best tool for this, even if it eventually gets the job done. This is what deters me from adding a lot of images.
L. Spiro
P.S.: The work I did for Ubisoft was outsourced from their France branch, where I lived for a short time. On the side, you may also be interested to know that I am the author of MHS (Memory Hacking Software). I guess that means people were using my software to give you a headache. Sorry about that.
Hey Spiro -
Gosh, I can’t believe it’s been 3 years until I’ve finally replied back to this, time really has kept me busy . Hope everything is working out well for you in Japan and no need to apologize, it’s not a big deal.
I plan to visit Japan next year and if you’re up to it would like to grab lunch in Tokyo as I’ve never been there before. Best Wishes and feel free to e-mail me at my provided account.
Thanks,
Fonz
Firstly, excellent tutorial, thank you.
I do have a question, though. When you subclass a state and CGame, you end up breaking LSP in order to use the features of the new CGame:
class CMyGame : public CGame
class CHighScoreScreenState : public CState
LSVOID LSE_CALL CHighScoreScreenState::Init( CGame * _pgGame ) {
_pgGame->GetSoundMan()->ClearAllSounds(); // Last state should have done this but this is just an example.
CMyGame * pmgMyGame = static_cast(_pgGame);
// And now Anything you added to your CMyGame class can be accessed by your state.
}
The cast here is a smell for me. Surely this high score should depend directly on CMyGame, rather than pretend to depend on the base class? Of course, this now means states need to be templated or some such, which is probably ugly.
Any ideas of how this could be avoided?
My only idea is to try (I couldn’t test this) to declare the functions inside of CHighScoreScreenState as:
virtual LSVOID LSE_CALL Init( CMyGame * _pgGame );
etc.
Off the top of my head I am not positive the virtual table will point to the correct function but you can try it.
Otherwise I don’t really have a graceful solution, since wouldn’t want to use templates for this as you said.
L. Spiro
Hey Spiro, I was just thinking that a single game state is composed by layers, sort of.. Say you pause your level state, or the player access the inventory(or pause while in the inventory), youre not going to destroy the level state to bring the inventory state (too heavy, or you even want to display the game behind the inventory/pausemenu popup, etc.), and you also going to need a timer for each “layer”, since your gui stuff need a timer for its own animation or whatever, while the game is frozen behind..
You think its a good idea to handle this layering system in the engine or the user should handle it himself on the inherited state his going to create?
A state should have only one responsibility (be that the game scene, the pause menu, controller disconnected screen, whatever).
Rather than the states themselves being expanded to handle more than that, the state manager should maintain a stack of states. This is how L. Spiro Engine works. You can push as many states on top of each other as you need and they all have flags such as “pauses lower states” etc.
Each state has its own timer, so implicitly some states can be paused while others are animated. The main game timer has 2 timers as well anyway (real time that always ticks and virtual time that only ticks when not paused) so it is always possible to have one part of the game paused while another is active even without multiple timers anyway.
L. Spiro
Hello,
After finishing a lot of DirectX 11 tutorials such as rastertek I decided to finally get my hands dirty at proper game engine design and I had already vaguely read about gamestates and it all seemed to make sense until I really tried it. I initially liked your idea of passing the game class downstream but doing so I learned about a new problem: circular references and/or include hell. Include guards don’t seem to stop this.
I keep getting errors such as undefined types if I don’t forward declare or can’t access such and such because it is an incomplete type.
If that wasn’t enough it seems like according to other sources upstream dependencies are an anti-pattern and shouldn’t be done. My world came crushing down, I really wonder how to do this now. I think my workaround will be to simply pass down pointers to all the subsystems or something like that.
I can at least assure you that your problem did not arise specifically because you tried to use states. I have no such problems.
If you have these kinds of circular dependencies then you have an overarching problem with your organization. This isn’t the cause of it, this is its revelation.
Whether you use states or not you need a better form of global organization. I can’t say anything specific because you haven’t given details, but it’s a problem I had long ago before I ever considered states etc., so it should be treated and fixed as a separate issue.
Implementing states should never cause up-stream dependencies. I may be able to offer more advice if you give more details on how this happens.
L. Spiro
Spiro, I think I got it to work. Sorry for bothering you.
I had some issues with incorrect derivation of a base class, having a private rather than protected base member and a simple stupid mistake where I forgot to actually initialise a variable in a constructor. It seems to work now and I’ll probably be back with more questions LOL.
Why does CStateMachine::SetState pass the state id? Doesn’t passing _psState already give the state id since state id is a member of State?
Thank you, Geoffrey
Oh, I see it’s a protected member. Why not add a getStateId method instead of passing it separately?
The state ID is not connected to the class. The same state class could have a different ID if the state factory wishes it so. Making any hard connection between the state ID and the state class outside of the state factory is to tightly couple the ID and the state, and possibly other things.
I have never used the state ID in practice, so it is really just optional.
L. Spiro
Not to spam your comment section, but I have another inquery.
What is the point of this scheme:
if ( !RemoveCurState( _pgGame ) ) { return false; }
As far as I can tell, RemoveCurState always returns true; so the code block of this conditional statement will never get executed. What is the utility of this over just calling RemoveCurState(_pgGame) directly?
inquiry*
Agh, I skipped over the important comment, “\return Returns true in this simplified version. The real function could return false.”
Anyways, thanks for this blogpost, it’s very helpful!