Awesome
UniTaskStateMachine
[日本語]
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.
- The OnUpdate loop does not start until OnEnter finishes
- Does not transition to the next state until OnExit finishes
- OnUpdate is called every frame but not during await
In addition, the state transition method corresponds to the following.
- Transition when conditions are match
- Transition to the next state by specifying the TransitionID
- Ignored if the current state does not have the specified TransitionID
This package also provide an Editor tool, so you can easily create states.
Installation
Dependency
Using UniTask in the package, UniTask must be added in the project.
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
2.Open GraphEditor
GraphEditorOpen of StateMachineBehaviour added in 1
Or you can open the graph editor with Window / BG UniTaskStateMachine / StateMachineGraph.
3.How to add state
3-1.Add StateBehaviour
Add a Component that inherits BaseStateComponent to the GameObject to which StateMachineBehaviour is attached.
-
Do not inherit the class that inherits BaseStateComponent.
-
Do not addBaseStateComponent directly.
-
Do not add same state component
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)
{
}
}
}
3-2.Set the added StateBehaviour
Right-click on the Graph editor and select Create State to add State.
The added class name will be displayed and can be selected.
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.
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.
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.
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.
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.
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;