Sign in to follow this  
Icebone1000

How to avoid game state hell (huge switchs)?

Recommended Posts

Icebone1000    1958
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..

Share this post


Link to post
Share on other sites
sss_abaker    132
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.

Share this post


Link to post
Share on other sites
BornToCode    1185
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.

Share this post


Link to post
Share on other sites
Narf the Mouse    322
[quote name='sss_abaker' timestamp='1340135769' post='4950701']
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.
[/quote]
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.

Share this post


Link to post
Share on other sites
frob    44903
[quote name='BornToCode' timestamp='1340146170' post='4950757']
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.
[/quote]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.

Share this post


Link to post
Share on other sites
Cornstalks    7030
[quote name='Narf the Mouse' timestamp='1340152279' post='4950779']
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.
[/quote]
Donald Knuth said that, [url="http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.103.6084&rep=rep1&type=pdf"]in this paper[/url] (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.

Share this post


Link to post
Share on other sites
Icebone1000    1958
[quote name='frob' timestamp='1340133229' post='4950679']
...

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


In your Update() loop you can reduce it to:
[CODE]
Update()
{
if( mCurrentUpdateBehavior != null )
{
mCurrentUpdateBehavior.OnUpdate();
}
}
[/CODE]

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.
[/quote]

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..)

Share this post


Link to post
Share on other sites
L. Spiro    25619
You may find [url="http://lspiroengine.com/?p=351"]this[/url] useful.
Again, virtual functions drive the scenes, but you can ignore the warnings regarding the performance penalty—this results in 2 virtual calls per frame, which would not even slow down a PlayStation 3. This is a state machine.

A mix between this and what was mentioned by frob is ideal.
This is a macro-level arrangement (a CState for the entire main menu, another one for the entire options menu, etc.) and then within each state you will use frob’s method to create [i]substates[/i]. This is where you handling fading in the main menu, flashing the buttons after they are pressed, fading out, etc.

You are over-engineering a lot of things. A “fade-screen listener” seems to have no purpose, and certainly should not be registering a bunch of objects. If a state needs some fade, it can make a CFader instance and render that on top of itself over the whole screen while fading in or out. These should be instances owned within each state, not connected to anything else. This allows them to be layered—your pause menu fades out the game screen a little, and then a sub-menu adds even more fade over the pause menu and the game screen.


L. Spiro

Share this post


Link to post
Share on other sites
Calin    419
[quote name='frob' timestamp='1340133229' post='4950679']
Generally the code smell of complex case statements is resolved through inheretence and the [url="https://www.google.com/search?q=Strategy+pattern"]strategy pattern[/url].

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:

[CODE]
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
}
}
[/CODE]

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


In your Update() loop you can reduce it to:
[CODE]
Update()
{
if( mCurrentUpdateBehavior != null )
{
mCurrentUpdateBehavior.OnUpdate();
}
}
[/CODE]

[/quote]

could you expand the code a little more, how do you jump from one state to another i.e [code]. if( !fadescreen.isfading ) internalstate = initialDlg; [/code]

Share this post


Link to post
Share on other sites
smr    2468
[quote name='Calin' timestamp='1340222509' post='4951101']

could you expand the code a little more, how do you jump from one state to another i.e
. if( !fadescreen.isfading ) internalstate = initialDlg;

[/quote]

You can have a mNextState member that would be set to the current state upon completion of the update. Or at the beginning of the next update. Either way, this will prevent the CurrentState from being changed during the actual update of the entity/game object/whatever you're calling it.

Share this post


Link to post
Share on other sites
NEXUSKill    475
A simple yet flexible solution for this is a Finite State Machine, you are likely to use it for many other purposes as well such as AI agent behavior simulations, so its always good to have a generic implementation at hand.

It can get as complex and powerful as you want it to, but the basic implementation is very straightforward and can get you up and running in no time.

Share this post


Link to post
Share on other sites
Narf the Mouse    322
[quote name='NEXUSKill' timestamp='1340295438' post='4951403']
A simple yet flexible solution for this is a Finite State Machine, you are likely to use it for many other purposes as well such as AI agent behavior simulations, so its always good to have a generic implementation at hand.

It can get as complex and powerful as you want it to, but the basic implementation is very straightforward and can get you up and running in no time.
[/quote]
That still leaves you locked to a state machine.

Share this post


Link to post
Share on other sites
frob    44903
[quote name='Narf the Mouse' timestamp='1340296138' post='4951408']
That still leaves you locked to a state machine.
[/quote]
The OP described a state machine.

State machines are perhaps the most often use control system for complex systems, or any system that needs a simple flow.

I've probably written a thousand state machines over my career so far.

In my current mass-production of game objects I'm creating about three state trees every week and the code that goes inside them.


I completely agree about having generic state machine code available in any serious game. It is necessary functionality. Edited by frob

Share this post


Link to post
Share on other sites
sss_abaker    132
I suppose I came in half cocked. I don't optimize as I code myself and I use polymorphism pretty liberally. I simply code with those thoughts in mind. But I was of the mind that polymorphism's cost was still a determining factor when deciding whether or not to use it and I never did any experiments to confirm. Thanks for setting me straight guys.

Share this post


Link to post
Share on other sites
Cagnazzo    140
Yeah, the need for experimentation is pretty often unstated, but very important. People are notoriously bad at seeing what actually is costing them time so they try to optimize everything. It's variable on the code, of course, but I have a software engineering book here whose author mentions several times that 5% of his code caused about 90-95% of the slowdown. Optimizing everything as it's written in that case means 95% of the work is wasted.

You might call that habit suboptimal [img]http://public.gamedev.net//public/style_emoticons/default/tongue.png[/img]

(Not that you shouldn't ignore obvious inefficiencies as you write, of course, but weigh "this'll be easy to change later" and "this is easy to read" over "it'll run faster if I do this," especially before you actually profile it)

Share this post


Link to post
Share on other sites
Dario Oliveri    290
you just have to use a state pattern
Every state just setup next state when needed. This require onl 1 virtual function call /frame (update current state) and 1 extra virtual function call when switchin states(set state). Every states need to know only about the AbstractState in the interface and need to know only about few states can be setted from it in the implementation. No huge classes etc. That's one of the most basic design patterns very usefull in games you need to know. Many books uses that pattern

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this