Home

Awesome

UniTaskStateMachine

[日本語]

openupm

Since the return value is UniTask as shown below, you can use a state machine that supports asynchronous.

public interface IState
{
    void Init(BaseNode baseNode);
    UniTask OnEnter(CancellationToken ct = default);
    UniTask OnUpdate(CancellationToken ct = default);
    UniTask OnExit(CancellationToken ct = default);
}

It has the following features.

In addition, the state transition method corresponds to the following.

This package also provide an Editor tool, so you can easily create states.

image

Installation

Dependency

Using UniTask in the package, UniTask must be added in the project.

UniTask

If you are using UniTask (UniRx.Async) before it was split from UniRx

It can be used by defining the following in ProjectSettings / Player / OtherSettings / ScriptingDefineSymbol.

BG_USE_UNIRX_ASYNC

PackageManager

Install via git url

Open Window/Package Manager, and add package from git URL...

https://github.com/k-okawa/UniTaskStateMachine.git?path=Assets/Bg/UniTaskStateMachine

Install via OpenUPM

openupm add com.bg.unitaskstatemachine

UnityPackage

You can download unity package in release page.

How to use

1.Add StateMachineBehaviour Component

image

2.Open GraphEditor

GraphEditorOpen of StateMachineBehaviour added in 1

Or you can open the graph editor with Window / BG UniTaskStateMachine / StateMachineGraph.

image

3.How to add state

3-1.Add StateBehaviour

Add a Component that inherits BaseStateComponent to the GameObject to which StateMachineBehaviour is attached.

Example

namespace Bg.UniTaskStateMachine.Tests.BasicSceneTest
{
    public class StartState : BaseStateComponent
    {
        public override async UniTask OnEnter(CancellationToken ct = default)
        {

        }

        public override async UniTask OnUpdate(CancellationToken ct = default)
        {

        }

        public override async UniTask OnExit(CancellationToken ct = default)
        {

        }
    }
}

image

3-2.Set the added StateBehaviour

Right-click on the Graph editor and select Create State to add State.

image

The added class name will be displayed and can be selected.

image

If None is selected, the state will be nothing.

4.How to add Transition

You can add a Transition by right-clicking on the added State and selecting Make Transition.

image

You can use a function that is public in the Component of the GameObject to which StateMachineBehaviour is attached, has a return value of bool, and has no arguments, as a state transition condition.

image

You can reverse the condition by checking Is Negative.

If you set MethodName to None, the transition will not always meet the conditions.

It is also possible to forcibly execute the transition from the script by calling TriggerNextTransition described later.

image

5.EntryState specification

You must always specify the State to execute first.

It can be set by right-clicking on the State and selecting Set as Entry.

image

TriggerNextTransition

It is possible to translate state manually by transition id without specify condition method.

Transition id is recommended upper camel case that can use in C# field name.

After define transition id, you can generate transition id constant template code by GenerateTransitionIdConst button.

image

Finally, call "StateMachine.TriggerNextTransition(string transitionId)" for manual state transition.

It is able to call with string direct writing, but recommend with readonly string field in the generated template class.

API Reference

StateMachine

It can be accessed from the StateMachine property of the StateMachineBehaviour.

Properties

// Entry state
public BaseNode EntryNode;

// Current state node
public BaseNode CurrentNode { get; private set; }

// current StateMachine state(STOP,START,PAUSE)
public State CurrentState { get; private set; } = State.STOP;

// Timing of OnUpdate
public PlayerLoopTiming LoopTiming = PlayerLoopTiming.Update;

Methods

/// <summary>
/// Start StateMachine
/// </summary>
public async void Start();

/// <summary>
/// stop state machine completely
/// </summary>
public void Stop();

/// pause current state
/// </summary>
public void Pause();

/// <summary>
/// resume current state
/// </summary>
public void Resume();

/// <summary>
/// restart state machine from entry state
/// </summary>
public async UniTask ReStart(CancellationToken ct = default);

/// <summary>
/// force transition to next state
/// </summary>
/// <param name="transitionId">transition id named on graph editor</param>
public void TriggerNextTransition(string transitionId);

/// <summary>
/// whether current state is equivalent
/// </summary>
/// <param name="type">state type</param>
/// <returns>return true if current state is type argument</returns>
public bool IsMatchCurrentStateType(Type type);

/// <summary>
/// almost same with IsMatchCurrentStateType
/// difference is variable length arguments
/// </summary>
/// <param name="types">state types</param>
/// <returns>return true if current state match with any type arguments</returns>
public bool IsMatchAnyCurrentStateType(params Type[] types);

BaseStateComponent

Properties

// Crrent node that has state and transition
protected BaseNode baseNode;

Methods

public virtual void Init(BaseNode baseNode);
public virtual async UniTask OnEnter(CancellationToken ct = default);
public virtual async UniTask OnUpdate(CancellationToken ct = default);
public virtual async UniTask OnExit(CancellationToken ct = default);

BaseNode

Properties

public readonly string Id;
public readonly StateMachine StateMachine;
public bool IsUpdate { get; private set; } = true;

Methods

/// <summary>
/// is match any condition
/// </summary>
/// <returns>return true if node has any true conditions</returns>
public bool IsMatchAnyCondition();

/// <summary>
/// is exist force transition
/// </summary>
/// <returns>return true if node has any force transition</returns>
public bool IsExistForceTransition();

/// <summary>
/// get condition
/// </summary>
/// <param name="id">condition id (transition id named on graph editor)</param>
/// <returns>base condition</returns>
public BaseCondition GetCondition(string id);

/// <summary>
/// get transition ids that node has
/// </summary>
public IEnumerable<string> GetTransitionIds();

BaseCondition

Properties

public BaseNode NextNode { get; }
public Func<bool> ConditionCheckCallback { get; }
public string TransitionId { get; }
public bool IsNegative => isNegative;
public bool IsForceTransition => isForceTransition;