Table of Contents
The project provides a simple implementation of a finite state machine for use in .Net and Unity.
Finite state machines are a powerful tool for managing the behavior of objects, states, and transitions between them.
- Conditional transitions: You can define conditions under which an object will transition between states.
- Easy to use: The project offers a simple API for defining states and their transitions, making it easy to use even for beginners.
- Performance: The finite state machine implementation is optimized for high performance, making it suitable for use in real Unity projects.
Category | Description |
---|---|
Abstract |
Project API, including the IState and IStateMachine interfaces. |
Finite |
Simple finite state machine implementation providing basic functionality for state management. |
Transitional |
Support for transitions between states based on conditions. |
Add the .dll file from the latest release to your project.
Add Depra.Stateful
to your project using NuGet.
TState
is a state contract. It can be any type,
but the StateMachine
implementation works with IState
.
private readonly IStateMachine _stateMachine = new StateMachine();
public IStateMachine<Sample> StateMachine { get; } = new StateMachine<Sample>();
Create states for your object.
You can override the necessary methods in the states: Enter()
, Exit()
.
Method | Info |
---|---|
Enter() |
Called once upon entering the state |
Exit() |
Called once when exiting the state |
For example, let's create two states:
AwaitingState
- a state for waiting for a certain condition.
public sealed class AwaitingState : IState
{
private readonly IFollower _follower;
public AwaitingState(IFollower follower) => _follower = follower;
void IState.Enter() => _follower.StopFollow();
}
FollowingState
- a state for following a target.
public sealed class FollowingState : IState
{
private readonly Transform _target;
private readonly IFollower _follower;
public FollowingState(Transform target, IFollower follower)
{
_target = target;
_follower = follower;
}
IState.Enter() => _follower.StartFollow(_target);
IState.Exit() => _follower.StopFollow();
}
[RequireComponent(typeof(IFollower))]
public class Sample : MonoBehaviour
{
[SerializeField] private Health health;
public StateMachine<Sample> StateMachine { get; } = new StateMachine<Sample>();
public Transform Target { get; set; }
private AwaitingState _awaitingState;
private FollowingState _followingState;
private void Awake()
{
InstallStates();
}
private void InstallStates()
{
var follower = GetComponent<IFollower>();
_awaitingState = new AwaitingState(follower, this);
_followingState = new FollowingState(follower, Target, this);
}
}
The arguments startingState
and allowReentry
are optional.
You can use the default constructor,
then you will need to set the state in StateMachine
using the SwitchState
method.
We can add transitions between states.
To do this, StatefulTransitionSystem
and IStateTransitions
come in handy.
You can add transitions using the Add
, AddAny
methods, as well as the At
and AnyAt
extension methods.
Method | Description |
---|---|
Add(IState from, IStateTransition transition) |
Takes the source state and the transition |
AddAny(IStateTransition transition) |
Takes a transition between any states |
At(IState from, IState to, params Func<bool>[] conditions) |
Takes the source state, target state, and transition conditions |
AnyAt(IState to, params Func<bool>[] conditions) |
Takes the target state and transition conditions |
Let's consider a situation:
We want to transition from the AwaitingState
to the FollowingState
when the target is found.
We also want to transition back to the AwaitingState
when the target is lost.
The Add
method will help us with this. Let's add two transitions:
From the AwaitingState
to the FollowingState
:
transitions.Add(from: _awaitingState,
new StateTransition(to: _followingState, condition: () => _target != null));
Or using extension methods:
transitions.At(from: _awaitingState, to: _followingState, () => _target != null);
We've added a transition from the AwaitingState
to the FollowingState
with a condition that checks if the target is
not null.
Why is this necessary?
If the target is null, it's logical that we can transition to the FollowingState
.
The assembled transition matrix must be passed to the StatefulTransitionSystem
along with the state machine:
_transitionSystem = new StatefulTransitionSystem(_stateMachine, transitions);
To run the StatefulTransitionSystem
, you need to set the initial state and call the Tick()
method in the Update()
:
private void Update() => _transitionSystem.Tick();
I am an independent developer, and most of the development on this project is done in my spare time. If you're interested in collaborating or hiring me for a project, check out my portfolio and reach out!
Apache-2.0
Copyright (c) 2022-2024 Nikolay Melnikov [email protected]