Best Practices: Game State

Godot Version

4.5.1

Question

As a sort of general question, what are some best practices for handling the current game state of a game?

For example, let’s consider a sort of rogue-like where you travel through rooms defeating enemies. Every time you clear a room, you get the choice to upgrade your character.

Arbitrarily, let’s say that this specific game has the following game states:

enum GameStates {
    WANDERING,    # Player isn't in an active battle, freely wandering the map
    IN_ROOM,      # Player is in an active battle
    ROOM_CLEARED, # The room has been cleared
    UPGRADE,      # The Player gets to choose an upgrade
    DEATH,        # The Player has just died
    LOSS_SCREEN,  # The Player is viewing the loss screen, with options to replay or exit to main menu
    PAUSED        # The Game is Paused
}

var current_state: GameStates

These may or may not be the perfect or even only states to include for such a game, but for the sake of this question, let’s just say that these are the states chosen for the game

What is the best way to manage changing from state-to-state when it concerns the game state as a whole? I know there are situations where using Nodes or Enums are good for state machines, but if it concerns the game as a whole, would something like an autoload to serve as a sort of “hub” work?

My best guess is that, in the ideal world, there would be one singular location that keeps track of the current state and what states are available, while every other node and script at most will be in charge of sending signals to change the game state and keeping track of the current state, but I don’t know if that’s the best practice.

In my own research, I’ve found a very wide range answers that conflict with each other or assume something that wouldn’t apply to most scenarios, so I come here to be able to properly ask this question in the hopes of narrowing the range of conflicting and/or unique answers so I can more easily choose which answer is right for whatever I’m currently working on.

I have the correct answer!
IT DEPENDS! :smiley:

It depends on the state complexity. You can have simple enums like that, or you can do a node based approach. But at the end, they all work as a state machine.

I use the simple enum state machine inside a state of a bigger ‘node based’ state machine.
Enum ones work like this:

var current_mode: Mode = Mode.IDLE:
	set(value):
		if current_mode != value:
			exit_mode(current_mode)
			current_mode = value
			enter_mode(current_mode)

So when you set the mode, it automatically calls the function exit_mode(old_state) and then enter_mode(new_satate).

For the more complex states (maybe you need this), I followed and implemented this tutorial. It really works well for my game. But of course, along the way, I did some small changes to adapt it to my game better.

And yes, there should be a one singular location that keeps track of the state. And other scripts can ask to change the state. I don’t think anyone would advise otherwise.

Enums work when things are simple. Avoid enum based state machine when the number of states increases, as the lines of code would grew exponentially, which also taxes performance. Whereas in node based fsm, here, only the intended state would be running. And you can incorporate enum based states within node based fsm. Here is the tutorial that I followed.

https://youtu.be/oqFbZoA2lnU?si=HssGMOHASzQQBrFK

I’m marking this one as the “solution” to this post because for what I need right now this actually seems like it’ll work pretty well, but any and all solutions posted here are perfectly serviceable as well