How to avoid game state hell (huge switchs)?

Started by
17 comments, last by Dario Oliveri 11 years, 10 months ago
The last 2 projects I worked on ended up like this:
(pseudo c#)
[source lang="csharp"]public class InGame_Situation1
{
public enum state_S1
{
fadein, waitFade, initialDlg, doThat, animateNPC2, waitNPC2FinishAnimation...
};

public state_S1 internalState = fadein;

void Update(){

switch( internalState ){

case fadein:{

fadescreen.fade();

internalstate = waitFade;

}break;

case waitFade:{

if( !fadescreen.isfading ) internalstate = initialDlg;

}break;

...
}

}

};[/source]
Its incredible easy (adding new stuff is easily donne by adding enums and case blocks), but incredible ridiculous, every time something changes, ctrl+f entire solution to the rescue..
So, what should I do? what comes to mind is state machines, event driven systems and message driven systems...But I have never implemented one, the experience I have with stuff like this are the win32 message system, and the SDL event driven system, never saw and I have no clue on what would a system that handle game states would look like.

When I think on how it would be, I start get confuse with stuff that need to be communicated across lots of objects (like stuff that happens in parallel)..

Lets say I want to fade out the screen, in that moment (the screen is fading), all my GUI should become inactive(but still displayed), the problem is, theres a lot of GUI/HUD objects that handle gui for different parts of the game( i.e. pause menu, dialogues with npc, inventary...), so what Id do? each of those would have a fadescreen listener registered? Or the listner itself would have links to all gui objects that need to be deactivated? Or Im way out of reality?

Any source, tips, snippets, links, whatever would be great..

Id try my own to see what happens, but If things goes bad I dont believe I have the time to redo everything..since those systems are the core..
Advertisement
Generally the code smell of complex case statements is resolved through inheretence and the strategy pattern.

Depending on the code smell there may be other patterns (such as the Visitor pattern) that may apply depending on your details.

In your example you might implement:


class UIUpdateBehavior
{
virtual void OnUpdate()
{
BaseUpdateBehavior();
}

void BaseUpdateBehavior
{
// Normal behavior here.
}
}


class IdleBehavior : UIUpdateBehavior
{
// Does nothing special
}


class FadeInBehavior : UIUpdateBehavior
{
virtual void OnUpdate()
{
// do stuff differently
}
}


With similar classes for your states (waitFade, initialDlg, doThat, animateNPC2, waitNPC2FinishAnimation, etc.)


In your Update() loop you can reduce it to:

Update()
{
if( mCurrentUpdateBehavior != null )
{
mCurrentUpdateBehavior.OnUpdate();
}
}


You can then create new behaviors easily by deriving from the base class, or from deriving from an existing class. You can keep the behavior code contained to the individual behaviors. If for some reason external people need to work with your code, the added benefit is that they can create new behavior without modifying your original base code.
Except that the strategy pattern comes with a significant cost and that's the cost of virtual look up every time you call a Strategy's method. Optimized code is not necessarily the best looking and most manageable code.

So ask yourself and clarify for us, do you want performance or convenience? With that in mind notice that the examples are written in JAVA. Most Java apps don't care too much about performance on the level required by a game.

Except that the strategy pattern comes with a significant cost and that's the cost of virtual look up every time you call a Strategy's method. Optimized code is not necessarily the best looking and most manageable code.


I think it would be at best premature to worry about optimizing away the virtual call to update using a switch and at worst an incredible waste of development time both up-front and in maintenance.

Except that the strategy pattern comes with a significant cost and that's the cost of virtual look up every time you call a Strategy's method.


Depends on which paper you read, but looks like the cost currently tends toward about 7 nanoseconds for a virtual function. It takes longer to fetch memory to the cache.

If the few nanoseconds in that above update loop are critical then there is a very serious problem with his app.

I've used the pattern on major AAA games ranging from high-performance games requiring multi-core processors all the way down to 66MHz handhelds. The overhead of virtual function use is negligible, especially in comparison to the development time and the developer's sanity.
What i did in my engine is a similar system using Function Pointers instead. So each state registered an function pointer and whenever you switch state, you call the proper function pointer for the state you are in.

Except that the strategy pattern comes with a significant cost and that's the cost of virtual look up every time you call a Strategy's method. Optimized code is not necessarily the best looking and most manageable code.

So ask yourself and clarify for us, do you want performance or convenience? With that in mind notice that the examples are written in JAVA. Most Java apps don't care too much about performance on the level required by a game.

Premature optimization is a root of evil.

Don't know where I first read that sort of thing, but it's so very, very true.

Worry about optimization:

1) When it's relevant.
2) When you know how your code will work.
3) When you know where the slowdowns are (Code profiling).

This isn't an excuse to write lazy code, but writing your code around "optimization" for anything other than a small coding exercise is a great way to drive yourself nuts.

What i did in my engine is a similar system using Function Pointers instead. So each state registered an function pointer and whenever you switch state, you call the proper function pointer for the state you are in.
Whether you use inheritance's vtables -- which are nothing more than function pointers -- or you manually do it yourself with a function pointer table, the end result is the same. The real difference is letting the language do the work for you.

Premature optimization is a root of evil.

Don't know where I first read that sort of thing, but it's so very, very true.

Donald Knuth said that, in this paper (page 8 (or page 268 of the full thing), right hand column, end of the paragraph). He's the one who is quoted as saying that, and it is indeed true.
[size=2][ I was ninja'd 71 times before I stopped counting a long time ago ] [ f.k.a. MikeTacular ] [ My Blog ] [ SWFer: Gaplessly looped MP3s in your Flash games ]

...

With similar classes for your states (waitFade, initialDlg, doThat, animateNPC2, waitNPC2FinishAnimation, etc.)


In your Update() loop you can reduce it to:

Update()
{
if( mCurrentUpdateBehavior != null )
{
mCurrentUpdateBehavior.OnUpdate();
}
}


You can then create new behaviors easily by deriving from the base class, or from deriving from an existing class. You can keep the behavior code contained to the individual behaviors. If for some reason external people need to work with your code, the added benefit is that they can create new behavior without modifying your original base code.


Thanks a lot for the response!
Some questions regarding the method you described, Im not so sure about how Id use it..

How Id change to the next behavior? If one behavior is responsible for changing/update to the next behavior, then I lose reusability of behaviors (after the first fade complete, change to the first dialog, but the next fades will change to new behaviours..so : next question)

Each behaviour would be unique? i.e. InitialFadeBehavior, FadeBeforeAnim1Behavior, FadeToScene2Behavior, FadeBeforeAnim2Behavior...? (each would derive of a base FedeBehavior like you mentioned, I believe)

My current enums really have a LOT of values, some have 20 +, some case blocks have really 2 lines of code, is it fine to create classes and objects for stuff like this? Im used to see classes as more complex stuff, maybe I should review my concepts..Im afraid of the number of simple small classes that will sum up.

(Dont know if its relevant, the current games Im working on are really a bunch of diferent situations, its not a constant game play where the things that change are level design and amount of enemies, its like a bunch of scenes with some diferent interactive GUI each, depending on user choices, different paths are taken, etc..)

This topic is closed to new replies.

Advertisement