Game Programming Patterns - State Pattern

Why are the state functions in the Context? Land(), Jump() etc. Where are they called?
Can’t you just SetState(IState)?

image

It’s not shown in the code you showed.

Of course you can.

Sort of. If you do it that way, that means that SetState and currentState would both be publicly accessible anywhere. So if something is wrong with your states, the problem can be anywhere in your program. Here, there’s a central place where you can keep all your states and you have public methods to give your program access to change them in a SOLID way that hides the internal mechanisms.

1 Like

Amazing! Great explanation :slight_smile:

I was assuming this but doesn’t it break Open Close because you have to always open up the interface and add the state as well as adding the new class for the state?

Yeah, it does. As long as you adding new function calls for each state, you are not getting polymorphic behavior. I’m not even sure whether or not you’re getting a state machine. A state machine, almost by definition seems like it has to be polymorphic – you run the same method calls, but the implementation of those calls can be swapped out as state. It doesn’t seem like you are really changing states if you also have to switch to new method calls used by that state.

If we were to add Swim() functionality for example, not only would we have to add a Swim class that implements the LocomotionState interface, but we’d have to modify the interface to add a Swim() method, re-implement the interface in all classes that currently implement it, and filter through those classes for any changes that need to be made to their Swim() method (likely none). That sounds like a small nightmare, depending on how many States you end up adding.

A more polymorphic approach might be just to have functions like Idle, Move, and Transition. Idle and Move could differ in functionality depending on your state (a Ground-state Idle() could be considerably different from a flight-state Idle or a swim-state Idle, for example). And then Transition would basically work like you are suggesting for SetState.

I don’t see a point in having a contract that says every LocomotionState needs a specific method call, but every LocomotionState needs every method call. We should be using the same method calls to ensure that we get new results from every state, to get the most out of the state machine.

That being said, I have heard of times when state pattern has been used to “nullify” method calls like this. For example, I knew someone who did this for multiplayer stuff, putting the player’s UI in one of two states for when it was their turn or not. When it was their turn, their state allowed them to do all sorts of things. But when it was not their turn, a lot of UI controls and methods were either inactive, responded differently, or didn’t do anything. But they could still do a lot of stuff like move around the map, open information windows, chat with other players, etc.

This sort of thing seems to work so long as you don’t plan to expand the interface.

So I guess we have two kinds of state patterns. One with polymorphism that is expandable, solid, and useful, and one without polymorphism, which is not expandable, not really solid, but could still could be useful, maybe, in a limited scope.

Sorry for the long post just to say, “Yeah, I think you’re right!”. I’m just thinking out loud here, trying to work through these ideas.

I guess this is true of any interface now that I think about it.

Genius man. You have stated everything I was thinking.

I have seen the state pattern implemented before that was waaaaay more modular in the way you are talking about and more importantly, extendable.

This version seems like a really elaborate FINITE STATE MACHINE with a finite number of states that you can’t really extend at a later date. Like a “more clean” version of the Switch statement FSM.

The only thing I can think of (I was playing around in REPL) is gating certain transitions which you can’t do with an interface as all classes must implement the interface.

In this example - the console in the state context and the games are states.

In the image - console.PlayHalo(); works because it’s a Method that exists inside the context for new Xbox(). However if I change it to console.PlayUncharted(), which only exists in new Playstation(), an exception is thrown.

If I change the console to Playstation, it works :slight_smile:

So maybe it’s to gate transitions.

This topic was automatically closed 20 days after the last reply. New replies are no longer allowed.