Skip to content

SST-Systems/StateMachine

Repository files navigation

StateMachine

release release date last commit license

English | Русский


Async states with awaited transitions.

A lightweight async state machine for Unity and pure C#. States are plain classes — by default created with a parameterless constructor. Entering a state awaits the current state's OnExit, then the next state's OnEnter. Parameterless and payload-carrying states are both supported, and a transition requested mid-transition is queued. Need dependencies inside your states? Plug in an IStateFactory backed by your DI container of choice.

Table Of Contents

Details

Installation

  1. .unitypackageReleases
  2. UPMWindow → Package Manager+Add package from git URL: https://github.com/SST-Systems/StateMachine.git Append #tag to pin a version.
  3. Manual — clone or download, copy to Assets/.

Unity 2021.3+

The core is dependency-free — no DI framework required.


Interfaces

IState — base state. Task OnExit() (default no-op).

IDefaultState : IState — a parameterless state. Task OnEnter().

IPayloadState<TPayload> : IState — a state that receives a payload on entry. Task OnEnter(TPayload payload).

All methods have default (no-op) implementations — implement only the ones you need.


StateMachine

Abstract base you derive your machine from.

Member Purpose
RegisterState<T>() Pre-instantiate and cache a state (through the state factory). Optional.
Enter<TState>() Transition to a parameterless IDefaultState.
Enter<TState, TPayload>(payload) Transition to an IPayloadState<TPayload>.
Enter(Type) / Enter<TPayload>(Type, payload) Same transitions, addressed by Type.
CurrentState The currently active state.
StateChanged Raised after each successful transition.

State creation

States are produced by an IStateFactory:

  • ActivatorStateFactory (default) — creates states via Activator.CreateInstance, so each state needs a parameterless constructor. Used when you construct the machine with new GameStateMachine().
  • Custom — implement IStateFactory to build states through your DI container (so they can receive injected dependencies) and pass it to the base constructor: public GameStateMachine(IStateFactory factory) : base(factory) { }. A ready Zenject factory ships in the sample.

Usage

// 1. Define states — plain classes with a parameterless constructor
public class MenuState : IDefaultState
{
    public Task OnEnter() => ShowMenu();
    public Task OnExit()  => HideMenu();
}

public class LevelState : IPayloadState<int>
{
    public Task OnEnter(int levelId) => LevelLoader.Load(levelId);
}

// 2. Derive a machine and register the states
public class GameStateMachine : StateMachine
{
    public GameStateMachine()            // uses the default Activator factory
    {
        RegisterState<MenuState>();
        RegisterState<LevelState>();
    }
}

// 3. Create and drive it — transitions are awaited
var machine = new GameStateMachine();
await machine.Enter<MenuState>();
await machine.Enter<LevelState, int>(3);

Need injected dependencies inside your states? Pass an IStateFactory to the base constructor — see the Zenject for StateMachine sample.


Samples

Zenject for StateMachine — a ZenjectStateFactory (backed by IInstantiator, so states can [Inject] dependencies), an installer, and a sample machine. Import via Window → Package Manager → select StateMachineSamples tab.


Notes

  • By default states are created with ActivatorStateFactory and therefore need a parameterless constructor; pass a custom IStateFactory to inject dependencies into states.
  • RegisterState<T>() is optional pre-registration; an unregistered state is created lazily on its first Enter.
  • Re-entering the current state is a no-op. Calling Enter during an in-progress transition queues the target and runs it once the current transition completes (only the most recent request is kept).

License

Distributed under the MIT License. Free for personal and commercial use.

Author — Egor Shesterikov.

Releases

Contributors

Languages