Advertisement

Two state machines might affect how a function is handled. Techniques to handle this situation?

Started by August 21, 2022 01:14 AM
3 comments, last by aljav 2 years, 2 months ago

Hey guys, first a caveat - I am a beginner when it comes to programming, all of my experience is with Unreal blueprints, and I may use some technical terms incorrectly. I'll try to give concrete examples to avoid confusion.

I am not having a progress blocking issue, I just see a situation where I have a feeling there is a battle-test technique to handle things more efficiently than I know how to. Perhaps some experienced programmers can offer advice?

The situation involves too much code to show - I've tried to condense it with a simple graphic below.

There are two State Machines at play. GameState is watching things happening in the game, recording data about it, etc.
PlayerState is used to determine how input will be handled, and what UI elements will be shown.

An example is that while in the PlayerState:Movement and you press W, the character will move forward (first person style game). While in PlayerState:UI and press W, the current UI menu will move selection up one element.

PlayerState watches GameState because when GameState changes, in most cases PlayerState should also change. E.G., when we go into GameState:PreGame, that is like a countdown where for 5 seconds the player cannot move, therefore they go to PlayerState:InputLocked.

Now you can imagine, if PlayerState is UI and GameState is InGame, ESC might toggle a Pause Menu off. But if it's same PlayerState but GameState is Menu, then ESC should not toggle off the main menu - it would have different functionality.

So, there is this situation where GameState + PlayerState can create additional states that aren't defined. Sort of like there is a multiplication process happening.

The simple, obvious solution is just to do a Switch statement using the GameState as an enumerator on whatever input events need to consider that. But, that has zero modularity and it is going to be difficult to maintain. I find code with lots of conditional checks like that to be very hard to reason about, so I try to avoid it.

So, have you seen a problem like this before? Is there a tried and true method to deal with it? Might you set things up entirely differently? Perhaps I am misusing these state machines?

Any advice is appreciated, thank you.

(about the image: Arrows denote that source causes target to change. E.G. when GameState:Menu is entered, then PlayerState:UI is request to be entered as well.)

After typing this out, I think it's become clear what the actual problem is. I am overlapping responsibilities.

The Game State is not responsible for player input, but I am making it so. I think the solution is stupid-simple: just make a couple extra PlayerStates for things like “MainMenu”.

Advertisement

Navigating an UI is not player state, just UI state. Judging from the items in your diagram, your “player states” (completely incorrect name) seem to be details of various “game states” (also a completely incorrect name): for example “movement” and “inventory” are different modes of the “in game” UI.

They aren't necessarily a state machine like the top-level scenes: they might just be objects that translate inputs to commands (e.g. arrows → menu navigation commands in “UI” or arrows → movement commands in game).

In typical terminology, “game state” is a complete description of the game, what you would need to record to save and reload the game, not of insignificant application details like what menu is open; and “player state” is the part of game state that describes the player character, for example "against a wall and unable to move east", not “in the inventory UI and unable to receive movement commands”.

Omae Wa Mou Shindeiru

@LorenzoGatti

I had thought about decoupling the UI from the Players State, but to me that seemed like it would add more hoops for me to jump through without any benefit.

What UI elements will be shown is always going to be directly related to what state the player is in, and the state that the player is in will always be driven by input events or when the game state changes. Therefore, what might a benefit be of handling the UI state somewhere separate from the Player State?

The way I handle PlayerStates is that I wrap each state in an object. When a state is changed, first thing I do is “sanitize” the UI, meaning that I revert everything to default. Then I call the states OnEnter function, and the state sends out interface calls to tell the UI manager and possessed character what they need to do.

This topic is closed to new replies.

Advertisement