unity atoms logo

Unity Atoms

Subpackages

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:

  1. 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).
  2. 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:

  • OPEN
  • CLOSED
  • LOCKED.

It also has 4 transitions:

  • From OPEN, to CLOSED when dispatching CLOSE.
  • From CLOSED, to LOCKED when dispatching LOCK.
  • From LOCKED, to CLOSED when dispatching UNLOCK.
  • From CLOSED, to OPEN when dispatching OPEN.

States

  • Id
    • the state name / identifier (string)
  • Cooldown
    • if set to above 0 the state machine will re set the state every n th seconds where n is the cooldown specified in seconds. If for example cooldown is set to 1 and the state id is set to SECOND, then the state machine will be set to SECOND every 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);
    }
    }
Previous
Base Atoms