Jump to content

  • Log In with Google      Sign In   
  • Create Account


How to avoid game state hell (huge switchs)?


Old topic!
Guest, the last post of this topic is over 60 days old and at this point you may not reply in this topic. If you wish to continue this conversation start a new topic.

  • You cannot reply to this topic
18 replies to this topic

#1 Icebone1000   Members   -  Reputation: 979

Like
0Likes
Like

Posted 19 June 2012 - 12:38 PM

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

Sponsor:

#2 frob   Moderators   -  Reputation: 18849

Like
5Likes
Like

Posted 19 June 2012 - 01:13 PM

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.

Edited by frob, 19 June 2012 - 01:17 PM.

Check out my personal indie blog at bryanwagstaff.com.

#3 sss_abaker   Members   -  Reputation: 132

Like
-2Likes
Like

Posted 19 June 2012 - 01:56 PM

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.

#4 smr   Members   -  Reputation: 1554

Like
6Likes
Like

Posted 19 June 2012 - 02:59 PM

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.

Edited by smr, 19 June 2012 - 03:00 PM.


#5 frob   Moderators   -  Reputation: 18849

Like
12Likes
Like

Posted 19 June 2012 - 03:12 PM

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.
Check out my personal indie blog at bryanwagstaff.com.

#6 BornToCode   Members   -  Reputation: 902

Like
0Likes
Like

Posted 19 June 2012 - 04:49 PM

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.

#7 Narf the Mouse   Members   -  Reputation: 318

Like
0Likes
Like

Posted 19 June 2012 - 06:31 PM

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.

#8 frob   Moderators   -  Reputation: 18849

Like
3Likes
Like

Posted 19 June 2012 - 06:40 PM

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.
Check out my personal indie blog at bryanwagstaff.com.

#9 Cornstalks   Crossbones+   -  Reputation: 6966

Like
0Likes
Like

Posted 20 June 2012 - 12:19 AM

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

#10 Icebone1000   Members   -  Reputation: 979

Like
0Likes
Like

Posted 20 June 2012 - 08:05 AM

...

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

#11 L. Spiro   Crossbones+   -  Reputation: 12231

Like
0Likes
Like

Posted 20 June 2012 - 08:53 AM

You may find this 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 substates. 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
It is amazing how often people try to be unique, and yet they are always trying to make others be like them. - L. Spiro 2011
I spent most of my life learning the courage it takes to go out and get what I want. Now that I have it, I am not sure exactly what it is that I want. - L. Spiro 2013
I went to my local Subway once to find some guy yelling at the staff. When someone finally came to take my order and asked, “May I help you?”, I replied, “Yeah, I’ll have one asshole to go.”
L. Spiro Engine: http://lspiroengine.com
L. Spiro Engine Forums: http://lspiroengine.com/forums

#12 Calin   Members   -  Reputation: 238

Like
0Likes
Like

Posted 20 June 2012 - 02:01 PM

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();
  }
}


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


#13 smr   Members   -  Reputation: 1554

Like
0Likes
Like

Posted 20 June 2012 - 04:06 PM

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


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.

#14 NEXUSKill   Members   -  Reputation: 446

Like
0Likes
Like

Posted 21 June 2012 - 10:17 AM

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.
Game making is godlike

LinkedIn profile: http://ar.linkedin.com/pub/andres-ricardo-chamarra/2a/28a/272



#15 Narf the Mouse   Members   -  Reputation: 318

Like
-1Likes
Like

Posted 21 June 2012 - 10:28 AM

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.

That still leaves you locked to a state machine.

#16 frob   Moderators   -  Reputation: 18849

Like
0Likes
Like

Posted 21 June 2012 - 10:42 AM

That still leaves you locked to a state machine.

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, 21 June 2012 - 10:43 AM.

Check out my personal indie blog at bryanwagstaff.com.

#17 sss_abaker   Members   -  Reputation: 132

Like
1Likes
Like

Posted 21 June 2012 - 03:33 PM

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.

#18 Cagnazzo   Members   -  Reputation: 140

Like
0Likes
Like

Posted 21 June 2012 - 07:34 PM

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 Posted Image

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

#19 DemonRad   Members   -  Reputation: 290

Like
0Likes
Like

Posted 22 June 2012 - 01:22 AM

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

Peace and love, now I understand really what it means! Guardian Angels exist! Thanks!





Old topic!
Guest, the last post of this topic is over 60 days old and at this point you may not reply in this topic. If you wish to continue this conversation start a new topic.



PARTNERS