A right way to do a GameManager with FSM in Unity 6

Hello, I’m trying to implement a FSM pattern that would allow me to manage game state (and other states in the future). I find my current implementation wrong, because i have to store in a bool if a game is paused or not to allow me to use the same input for pausing and unpausing the game. Is this the correct way to approach this situation or not?

The current code is working as expected for now.

Code for GameManager object:

using UnityEngine;

public class GameManager : MonoBehaviour
{
  private StateMachine stateMachine = new StateMachine();
  private IState pauseState = new PauseState();
  private IState playState = new PlayState();
  private bool isPaused = false;

  private void Awake()
  {
    stateMachine.ChangeState(playState);
  }

  private void Update()
  {
    stateMachine.Tick();
  }

  private void OnEnable()
  {
    InputReader.Instance.PauseEvent += HandlePause;
  }
  private void OnDisable()
  {
    InputReader.Instance.PauseEvent -= HandlePause;
  }

  void HandlePause()
  {
    if (isPaused) stateMachine.ChangeState(playState);
    else stateMachine.ChangeState(pauseState);
    isPaused = !isPaused;
  }
}

For FSM:

public class StateMachine
{
  private IState currentState;

  public void ChangeState(IState newState)
  {
    if (currentState != null) currentState.Exit();
    currentState = newState;
    currentState.Enter();
  }

  public void Tick()
  {
    if (currentState != null) currentState.Execute();
  }
}

For states:

using UnityEngine;

public class PauseState : IState
{
  public void Enter()
  {
    Debug.Log("Game Paused");
  }

  public void Execute()
  {
    Debug.Log("Is Paused");
  }

  public void Exit()
  {
    Debug.Log("Exiting Paused State");
  }
}
using UnityEngine;

public class PlayState : IState
{
  public void Enter()
  {
    Debug.Log("Game Played");
  }

  public void Execute()
  {
    Debug.Log("Is Playing");
  }

  public void Exit()
  {
    Debug.Log("Exiting Playing State");
  }
}

Could you not check what state you’re in instead of storing the bool?

That’s an awful lot of code just to stop time. If your design requires such complexity I suppose go for it.

Personally, I just set Time.timeScale = 0; when my games pause, then set it back to 1 on resume.

I have a pause screen with a static method to pause and unpause, which coincidentally can show / hide a pause overlay.

It really is that simple in Unity.

Would that basically mean making each state have a public var “name” and allowing to access it with a get method from StateMachine class?

I agree that it’s a simpler way, but since I’m going to make abstractions for state machine anyway I’ve decided to try making a game manager with FSM.

I might add other game states later.

Excellent, off you go then. Just for full disclosure, here’s my take on such things:

FSM finite state machines:

This is my position on finite state machines (FSMs) and coding with them:

All generic FSM solutions I have seen do not actually improve the problem space.

I suggest never using the term “state machine.” Instead, just think:

  • I have to keep track of some THING(s)
  • That THING might change due to reasons
  • Depending on that THING, my code might act differently

That’s it. That’s all it is. Really!! The classic example is a door:

  • track if it is open or closed
  • if it is open, you could close it
  • if it is closed, you could open it
  • if it is open you could walk through it
  • if it is closed you could bump into it

Wanna make it more complex? Try this:

  • put a latch on one side of the door.
  • handle all the above with the latch locked or open

I’m kind of more of a “get it working first” guy.

Ask yourself, “WHY would I use FSM solution XYZ when I just need a variable and a switch statement?”

Your mileage may vary.

“I strongly suggest to make it as simple as possible. No classes, no interfaces, no needless OOP.” - Zajoman on the Unity3D forums.

1 Like

Okay, you might be right that it’s too much for handling pauses.
But for future reference, is it a correct way to handle states in general? For player, for example.

I will only say that it is “a way” that I have unfortunately seen too often.

As my text above points out, making an abstract system doesn’t really help that much, as in none of the usual game development problems gets any easier by doing so.

Yes, it can help in certain cases, but mostly you will find it gets in the way early on, then becomes dangerously brittle later on: you don’t dare change any part of it because too much is hung on this huge armature of abstraction, making it hard to debug as well.

For player what? Death? Movement? Jumping? Carrying an item? Looting a chest? In combat?

All of those states have weird interactions with each other, overlaps, etc.

Do you want to tie them together generically?

Don’t get me wrong, I have what you might call “state machines” all over my games. For instance, this is one for the actual input handling on my Match3 game:

		public enum InputState
		{
			INVALID,
			OFF,
			DRAGGING,
			COLLAPSING,
			GAMEOVER,
		}

But again, it’s just an enum and a switch statement because it is ONLY related to the processing of input, nothing else. If you leave the game, that’s handled by what scene you’re in.

Think of Scenes in Unity as your primary way of handling major game state flow.

You can play the above match3 game and see all of its source code at:

1 Like

I have to agree with @Kurt-Dekker here. I’ve never seen the usage of a generic StateMachine abstraction in a codebase lead to anything good. Just managing an enum manually tends to always result in better quality code (both more readable and less brittle) in my experience.

The only exception I can think of is if the state machine is hooked up to a good visual node-based editor. That can be useful in some cases - like for configuring and debugging complex animation transition graphs, or AI behaviours, for example.

2 Likes

This thread also touches on the issue of the ambiguous “game manager”. Why does all the general management (whatever that entails) need to be centralised? There’s no reason over arching systems can’t be divvied up based on responsibility, and work in tandem.

I found it makes more sense to encapsulate an active gameplay runtime into an object, within which the various systems can exist concurrently. This also makes it very to cleanly ‘shut down’ the game cleanly, useful for when the player wants to swap between save games.

2 Likes

I second this opinion. I have very few centralized controlling systems. I’m an event-driven kind of person so I like to think in terms of signals that are posted that anything interested can then react to. For example, if the player presses the pause button it simply posts a pause event globally. Anything that cares about that event will react accordingly. One such thing that is listening for the pause event is the player input. When it receives that signal it switches to a different set of input mappings and in that list of mappings is the ability to press a button that posts an unpause event globally. Almost everything in my games reacts to such events and is programmed in such a way that it doesn’t care what the source of that event was. Likewise, everything that posts an event is designed in such a way that it doesn’t care what is listening and how they react to it if they are.

1 Like

So then, allow me to oppose yours, Kurts and Sluggy’s opinions. :slight_smile:

FSMs are pretty nice tools to create an “architecture” for the logic - if done right. Think of a State much like an Assembly definition, and each state is wholly self-contained.

I also don’t see in this and many other FSM examples the idea evolve beyond just having State classes. The FSMs I worked with since 2002 all follow this hierarchical pattern:

  • Statemachine (container for states)
    • States (container for transitions)
      • Transitions (container for conditions + actions)
        • Conditions
        • Actions

The big benefit lies in creating extremely lightweight, re-usable Conditions and Actions. This could be as simple as “Actor in Range” and “Instantiate Prefab”. But also include their own variable (int, bool) Conditions and Actions so that you can implement the state flow logic wholly within the FSM.

There’s a relatively high upfront cost that gets offset by the relative ease of introducing new, still typically very simple, easy to test and always self-contained Conditions and Actions. Times the number of team mates working with that system. Within two weeks you can have a basic FSM from the ground up. Add another two to six weeks and you have so many reusable Conditions and Actions that the game logic can be entirely expressed by combining what you already have. That’s when you make big gains.

The benefits are:

  • FSM usage is “safe”, you can have logic bugs but assuming programmers did their job well, no exceptions
  • FSMs in their entirety and their current state can easily be dumped for analysis, in a way that is superior to spreading logs everywhere
  • debugging with breakpoints always has a well defined entry point (ie a specific Condition is never true, or an Action doesn’t perform)
  • the code to express the game logic is well formed, it doesn’t just allow “any code” and thus the chances for introducing Junior designer madness (besides copypasta) are minimized
  • lends itself well for visual tooling, however, this is rarely as beneficial as most would think it to be (eg debugging is much harder, blueprint layout madness, a lot of time spent on UI programming)
  • FSM updates with a heartbeat at a fixed time - events are registered but not acted upon until the next heartbeat (the delay is usually acceptable, if not beneficial)

The downsides of enum-driven FSMs are:

  • can (and will) write any code anywhere, including code that violates the FSM integrity
  • can change state anytime, anywhere
  • no clear visualization of which state leads where (or why)
  • looks just like any other code

Example FSM code fragment:


Example visualization with PlantUML for debugging:

I wrote about this particular FSM here. For something highly state-driven like networking where you don’t want any sudden surprises and need to handle various edge cases, a FSM is perfect.

IMO an FSM sits between regular code and a scripting language while enforcing a rigid system when it comes to thinking about logic. You know which state you are in, you know which transitions lead you where, and you know what those conditions are and what they perform when that happens.

You can see from the graph above that the flow of code is a graph that you can easily follow and reason about. This is a huge benefit compared to the swaths of code we tend to write yet still don’t see the forest for the trees!

3 Likes

So, basically, an approach used by Unity in one of the community projects with SOs sending signals for different events?
That’s what i’ve found and am looking into it, thanks.

Omg, thanks!
I might agree with people above that FSM is an overkill in the case of game manager, but I want to learn and apply this pattern later. Your example and the one i’ve found on youtube by git-amend channel are exactly what i’ve been looking for.

Hey now! I never said I didn’t like FSMs. I just said I don’t like heavily centralized systems :wink: