Unity Atoms / FSM
Finite state machine implemented using Unity Atoms.
What is it?
A finite state machine implementation for Unity Atoms. The package was inspired by this open source Unity FSM repo. The FSM in Unity Atoms is actually derrived from StringVariable, so everything you can do with a StringVariable you can also do with a FSM.
Concepts
A finite state machine is composed of 2 things:
- States - a list of possible states. A state is either a string of a sub finite state machine (oh yes, FSM's can be nested).
- Transitions - a list of possible transitions. State can only be altered to another state based on the transitions defined here.
In order to change state you need to dispatch a command and that command must be defined in your transitions for the current state.
Example
Here is a simple example taken from here
"CLOSE" "LOCK"
+-------->---------+ +-------->---------+
| | | |
+----+ +------+ +------+
|OPEN| |CLOSED| |LOCKED|
+----+ +------+ +------+
| | | |
+--------<---------+ +--------<---------+
"OPEN" "UNLOCK"
This FSM has 3 states:
OPENCLOSEDLOCKED.
It also has 4 transitions:
- From
OPEN, toCLOSEDwhen dispatchingCLOSE. - From
CLOSED, toLOCKEDwhen dispatchingLOCK. - From
LOCKED, toCLOSEDwhen dispatchingUNLOCK. - From
CLOSED, toOPENwhen dispatchingOPEN.
States
Id- the state name / identifier (string)Cooldown- if set to above 0 the state machine will re set the state everynth seconds wherenis the cooldown specified in seconds. If for example cooldown is set to 1 and the state id is set toSECOND, then the state machine will be set toSECONDevery second. This means that you will receive that value in the FSM's Changed Event.Sub Machine- FSMs can be nested. If specified this sub machine will take over resolving the state value.
Transitions
From State- The state that we transition from.To State- The state that we transition to.Command- The command that needs to be dispatched in order to begin this transition.Test Condition- If specified this condition must be true in order to start the transition.Raise Event To Complete Transition- If set to true the transition will not complete automatically, Instead you will need to manually Raise the Complete Current Transition Event.
Hooks
OnUpdate- Calls a handler with the state value each Update.OnFixedUpdate- Calls a handler with the state value each FixedUpdate.DispatchWhen- Defines a command that is going to automatically be dispatched when the condition provided is met.OnStateCooldown- Calls the handler of every state cooldown.
Funcs
Reset- Reset the FSM.Dispatch- Dispatch / raise a command.
Simple example
A simple example showing how you can move a rigidibody towards a target depending on if the current state of the FMS is set to CHASING or not.
public class EnemyMovement : MonoBehaviour
{
[SerializeField]
private StringReference _tagToTarget;
[SerializeField]
private FloatReference _shootingRange = new FloatReference(5f);
[SerializeField]
private FloatReference _moveSpeedMultiplier = new FloatReference(2f);
[SerializeField]
private FiniteStateMachineReference _enemyState;
void Awake()
{
Transform target = null;
AtomTags.OnInitialization(() => target = AtomTags.FindByTag(_tagToTarget.Value).transform);
var body = GetComponent<Rigidbody2D>();
_enemyState.Machine.OnUpdate((deltaTime, value) =>
{
if (target)
{
body.Move((target.position - transform.position), value == "CHASING" ? 2f : 0f, deltaTime);
}
}, gameObject);
_enemyState.Machine.DispatchWhen(command: "ATTACK", (value) => target != null && value == "CHASING" && (_shootingRange.Value >= Vector3.Distance(target.position, transform.position)), gameObject);
_enemyState.Machine.DispatchWhen(command: "CHASE", (value) => target != null && value == "ATTACKING" && (_shootingRange.Value < Vector3.Distance(target.position, transform.position)), gameObject);
}
}