2
\$\begingroup\$

I'm in the process of redesigning some of my state management code (there are several features I've grown to dislike in practice..) which was based on this tutorial and others. The issue I'm having right now has to do with state transitions.

Where and when should state transitions occur? Should they be executed immediately, or should they be delayed to the end of the current frame, after the current state has finished its work for that frame?

Let's say my states can implement handleEvents, update, render, pause and resume methods, among others that are not relevant for this question.

My thoughts so far are the following:

  • If the transition is done at the end of the frame, then when I pause the game I might see an update during that same frame I clicked to pause. The Pause State will only appear after the current state has executed the rest of the frame (events, update and render). Perhaps that extra update should not have been done.

  • If the transition is done immediately I have to be careful to not to delete the current state while it is still executing its method, let's assume the handleEvents method. But even so, if I were to pause a game in the handleEvents function, then the game would immediately pause and the game state update function would not be executed that frame.

The second options is more appealing to me, because as a player I would probably notice that extra bit of movement after pressing to pause the game. What if the player died during that frame? The delayed state change would be unwarranted. But then, the game state itself should probably disable updates if a game pause was requested.

On the other hand it might make more sense to delay state transitions to after the current state has fully executed its functions for the current frame. Maybe the Pause state should be a sub-state of the Game state and not a state of its own.

Is it simply a matter of preference or is one of the approaches clearly superior for a different reason?

\$\endgroup\$
2
\$\begingroup\$

I generally try to have a single location where the state can change: from the update() method of the state. This allows to see quickly where one state can go from there. This means that even if you have an event (in handleEvents) that would make it change state, delay that until the update().

And I generally use a hybrid method: delayed switch.

Once you know you have to change the state, instead of asking the state machine to change state immediately, you place a request to change the state to another state. This allows you to finish running State1::update(), and cleanly go to State2.

Something that I have not seen mentioned in the tutorial you've linked to: onEnter and onExit methods. Each state has some code that needs to be executed once when you enter it, and once when you exit it. For instance: the graphics/data that need to be loaded/unloaded for that state, etc. should be done in these methods. The update() method is there for what's done every frame. I call it the 'steady-state' (yeah, that's a lot of "state").

Here is what I usually do; first the classes:

enter image description here

and here's how they interact:

enter image description here

Your main loop is calling the update method of your state machine, which is calling the onUpdate method of the current state. If that state realizes that it has to go to another state, it requests a change of state to it's owning state machine. The state machine does not change the state now, but waits until the current state's update method is done to change the state.

But it does not just change the states. Before doing so, it calls the onExit method on the current state, then changes the state to the new requested one, then call the onEnter method on the new (now current) state. This way, the next frame will be in a 'steady-state': no initialization to do, no set-up to do, and your 'render' method will correctly display stuff that relates to the new state, even if no update has been done in this new state yet.

\$\endgroup\$
  • \$\begingroup\$ That's a good solution. I've been doing the delay after all functions (after ender), but it's just as I said, sometimes that extra render doesn't look good. Your solution gets the best of both worlds. I still wonder if it comes down to preference but it's a big help for me to decide which approach to go with. By the way, the tutorial I mentioned gave the exit / enter methods a different name: pause and resume - which makes it sound like they are called only when the state is entered the second time onwards, so perhaps the first time an initialize method handles the graphic data. \$\endgroup\$ – aslg Sep 14 '17 at 6:27

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.