A ScriptableObject-based State Machine for Unity that provides a clean, decoupled, and Inspector-friendly way to manage game states.
SO State Machine uses Unity's ScriptableObject architecture to define and manage game states. By storing states as assets, it enables:
- Decoupled state logic – GameObjects react to state changes without tight coupling
- Inspector-friendly setup – Configure states and transitions directly in the Unity Editor
- Event-driven transitions – Enter/Exit callbacks via UnityEvents and a C# interface
- Scene-safe state management – Listener lists are automatically reset on scene loads
| Class | Type | Purpose |
|---|---|---|
GameStateSM |
ScriptableObject |
Defines a single game state |
GameStateSMSO |
ScriptableObject |
Variable container that tracks the active GameStateSM |
StateMachineSM |
MonoBehaviour |
Drives state transitions at runtime |
GameStateListenerSM |
MonoBehaviour |
Listens to state enter/exit events on a GameObject |
IStateListener |
Interface | Implement on any MonoBehaviour for code-driven state callbacks |
- Copy the contents of this repository into your Unity project under
Assets/(e.g.Assets/SOStateMachine/). - The package depends on the SO Variables framework (
VariableSO<T>). Make sure it is also present in your project.
In the Project window, right-click and select:
Create → SM → GameState
Create one asset per state (e.g. MainMenu, Gameplay, Paused). Optionally fill in the Game State Description field to document what the state represents.
Create → SM → Variables → GameState
This GameStateSMSO asset acts as a shared variable that the state machine writes to and listeners read from.
- Create (or select) a
GameObjectin the scene. - Add the
StateMachineSMcomponent. - Assign the
GameStateSMSOasset to Current Game State. - Assign the desired starting state to Start State.
- (Optional) Wire up the On Switch State UnityEvent to run logic on every transition.
Each GameStateSM asset needs a reference to its owning GameStateSMSO so it can trigger transitions.
In the Inspector for every GameStateSM asset, set the Statemachine field to the same GameStateSMSO variable.
Add the GameStateListenerSM component to any GameObject that should react to state changes.
- In the States Listeners list, add an entry for each state you want to respond to.
- Set the Game State field to the target
GameStateSMasset. - Wire up On Enter and On Exit UnityEvents.
- Enable Listen When Disabled if the listener should fire even while the GameObject is inactive.
Call either method on a GameStateSM asset (e.g. from a UnityEvent button or script):
// Switch to this state (ignored if already in this state)
gameState.Switch();
// Force-switch to this state (runs OnExit/OnEnter even if already active)
gameState.ForceSwitch();For code-driven state logic, implement the IStateListener interface on any MonoBehaviour that is a child (or self) of a GameStateListenerSM:
using SO.SMachine;
using UnityEngine;
public class MyGameplayHandler : MonoBehaviour, IStateListener
{
public void OnEnter(GameStateSM gameState)
{
Debug.Log($"Entered state: {gameState.name}");
// enable gameplay systems, spawn enemies, etc.
}
public void OnExit(GameStateSM gameState)
{
Debug.Log($"Exited state: {gameState.name}");
// clean up, pause timers, etc.
}
}IStateListener callbacks are invoked at the end of the frame after UnityEvent callbacks.
| Member | Description |
|---|---|
GameStateDescription |
Editor-only description of what this state does |
statemachine |
The GameStateSMSO this state belongs to |
Switch() |
Requests a transition to this state (no-op if already current) |
ForceSwitch() |
Forces a transition to this state regardless of the current state |
RegisterListener(gameStateListener) |
Registers an event-based listener |
UnregisterListener(gameStateListener) |
Unregisters an event-based listener |
| Field | Description |
|---|---|
CurrentGameState |
The GameStateSMSO variable that holds the active state |
startState |
The GameStateSM to activate when the component enables |
onSwitchState |
UnityEvent fired on every state transition |
| Field | Description |
|---|---|
Init |
UnityEvent fired once on first enable |
StatesListeners |
List of gameStateListener entries to handle |
| Field | Description |
|---|---|
GameState |
The state asset to listen to |
OnEnter |
UnityEvent invoked when the state becomes active |
OnExit |
UnityEvent invoked when the state becomes inactive |
listenWhenDisabled |
If true, stays registered while the GameObject is disabled |
GameStateBehaviour |
Editor-only description of this object's role in the state |
| Method | Description |
|---|---|
OnEnter(GameStateSM gameState) |
Called at end-of-frame when the state is entered |
OnExit(GameStateSM gameState) |
Called immediately when the state is exited |
This project is provided as-is. See the repository owner for licensing details.