Home

Awesome

State Machine

A robust and flexible state machine implementation for Unity, designed to handle complex game logic with ease, based on Unity Mecanim. The AdvancedStateMachine class allows you to define states, transitions, and conditions using various parameter types such as floats, integers, booleans, and triggers.

Features

How to install

From OpenUPM:

Namepackage.openupm.com
URLhttps://package.openupm.com
Scope(s)com.legustavinho

Basic Usage

Defining States

Create custom states by inheriting from the State<T> class, where T is the type of your parameter identifiers (e.g., string, enum).

public class IdleState : State<string>
{
    public IdleState() : base("Idle") { }

    protected override void OnStateEntered()
    {
        Console.WriteLine("Entered Idle State");
    }

    protected override void OnStateUpdated()
    {
        Console.WriteLine("Updating Idle State");
    }

    protected override void OnStateExited()
    {
        Console.WriteLine("Exited Idle State");
    }
}

public class MoveState : State<string>
{
    public MoveState() : base("Move") { }

    protected override void OnStateEntered()
    {
        Console.WriteLine("Entered Move State");
    }

    // Implement other methods as needed
}

Creating the State Machine

Instantiate the AdvancedStateMachine<T> class, providing an "AnyState" and a name for your state machine.

var anyState = new State<string>("Any");
var stateMachine = new AdvancedStateMachine<string>(anyState, "CharacterStateMachine");

Adding Parameters

Add parameters that will be used in transitions. Parameters can be of types Float, Int, Bool, or Trigger.

stateMachine.AddParameter("IsMoving", ParameterType.Bool);
stateMachine.AddParameter("Speed", ParameterType.Float);

Defining Transitions

Connect states and define conditions for transitions using the ConnectTo method and adding conditions.

var idleState = new IdleState();
var moveState = new MoveState();

// Add states to the state machine
stateMachine.AddNode(idleState);
stateMachine.AddNode(moveState);

// Connect Idle to Move when IsMoving is true
var toMoveTransition = idleState.ConnectTo(
    moveState, 
    priority: 0, 
    NodeConnectionDirection.Unidirectional
);
toMoveTransition.AddCondition("IsMoving", BoolParameterCondition.True);

// Connect Move to Idle when IsMoving is false
var toIdleTransition = moveState.ConnectTo(
    idleState, 
    priority: 0, 
    NodeConnectionDirection.Unidirectional
);
toIdleTransition.AddCondition("IsMoving", BoolParameterCondition.False);

Starting the State Machine

Start the state machine by specifying the initial state.

stateMachine.Start(idleState);

Updating Parameters and State Machine

Change parameter values and update the state machine, typically within your application's update loop.

// Simulate movement input
stateMachine.SetBool("IsMoving", true);

// Update the state machine
stateMachine.Update();

// Output:
// Entered Move State

// Simulate stopping movement
stateMachine.SetBool("IsMoving", false);

// Update the state machine
stateMachine.Update();

// Output:
// Exited Move State
// Entered Idle State

Advanced Usage

Using Triggers

Triggers are parameters that reset after being consumed in a transition.

stateMachine.AddParameter("Jump", ParameterType.Trigger);

var jumpState = new State<string>("Jump");
stateMachine.AddNode(jumpState);

// Transition from any state to Jump when Jump trigger is set
var jumpTransition = anyState.ConnectTo(
    jumpState, 
    priority: 1, 
    NodeConnectionDirection.Unidirectional
);
jumpTransition.AddCondition("Jump");

// Set the trigger
stateMachine.SetTrigger("Jump");

// Update the state machine
stateMachine.Update();

// Output:
// Entered Jump State

Priority-Based Transitions

Manage transitions that can occur under the same conditions by assigning priorities.

// High priority transition
var highPriorityTransition = idleState.ConnectTo(
    jumpState, 
    priority: 0, 
    NodeConnectionDirection.Unidirectional
);
highPriorityTransition.AddCondition("Jump");

// Lower priority transition
var lowPriorityTransition = idleState.ConnectTo(
    moveState, 
    priority: 1, 
    NodeConnectionDirection.Unidirectional
);
lowPriorityTransition.AddCondition("IsMoving", BoolParameterCondition.True);

In this scenario, if both Jump and IsMoving conditions are true, the state machine will transition to jumpState due to its higher priority (lower priority value).

Condition Operations

Specify whether all conditions need to be met (WhenAll) or any of them (WhenAny) for a transition.

// Transition when all conditions are met
var transitionAll = idleState.ConnectTo(
    moveState,
    priority: 0,
    NodeConnectionDirection.Unidirectional,
    ConditionOperation.WhenAll
);
transitionAll.AddCondition("IsMoving", BoolParameterCondition.True);
transitionAll.AddCondition("Speed", FloatParameterCondition.Greater, 0.5f);

// Transition when any condition is met
var transitionAny = idleState.ConnectTo(
    moveState,
    priority: 1,
    NodeConnectionDirection.Unidirectional,
    ConditionOperation.WhenAny
);
transitionAny.AddCondition("IsMoving", BoolParameterCondition.True);
transitionAny.AddCondition("Speed", FloatParameterCondition.Greater, 0.5f);

Subscribing to Events

Monitor state machine events like start, stop, and transitions, as well as state-specific events.

// State machine events
stateMachine.OnStart += (sm) => Console.WriteLine("State Machine Started");
stateMachine.OnStop += (sm) => Console.WriteLine("State Machine Stopped");
stateMachine.OnTransit += (fromState, toState) =>
{
    Console.WriteLine($"Transitioned from {fromState?.Name} to {toState?.Name}");
};

// State events
idleState.OnStateEnter += (state) => Console.WriteLine($"{state.Name} Entered");
idleState.OnStateExit += (state) => Console.WriteLine($"{state.Name} Exited");

Notes

License

This project is licensed under the MIT License.

Contributing

Contributions are welcome! Please submit a pull request or open an issue for any bugs or feature requests.