SkillAgentSearch skills...

UnityHFSM

A fast, tried-and-tested hierarchical finite state machine library for Unity, designed to be easy to use yet powerful without compromising performance.

Install / Use

/learn @Inspiaaa/UnityHFSM
About this skill

Quality Score

0/100

Category

Design

Supported Platforms

Universal

README

HFSM for Unity

<p align="center"> <a href="https://github.com/Inspiaaa/UnityHFSM"> <img src="https://img.shields.io/badge/Unity-C%23-blue.svg?&logo=unity" /></a> <a href="https://github.com/Inspiaaa/UnityHFSM/blob/master/LICENSE.md" alt="GitHub license"> <img src="https://img.shields.io/badge/License-MIT-green.svg" /></a> <a href="https://github.com/Inspiaaa/UnityHFSM/releases" alt="GitHub release"> <img src="https://img.shields.io/github/release/Inspiaaa/UnityHFSM.svg" /></a> <a href="https://openupm.com/packages/com.inspiaaa.unityhfsm/"> <img src="https://img.shields.io/npm/v/com.inspiaaa.unityhfsm?label=openupm&registry_uri=https://package.openupm.com" /></a> </p>

A simple yet powerful hierarchical finite state machine for the Unity game engine. It is scalable and customisable by being class-based, but also supports functions (lambdas) for rapid prototyping.

Thanks to overloading, it minimises boilerplate code while still supporting generics.

It has a special focus on the temporal aspects of state transitions, making it ideal for timing and synchronisation sensitive problems.


Why use UnityHFSM?

  • State machines are an amazingly easy way to write and organise game logic. (As soon as you have states, e.g. Walk, Run, Sneak, Jump, you can use a state machine)

  • It's easy and straightforward to use

  • It helps you tame complex behaviour (e.g. weapon handling -> cooling, ammo, reload, fire) or write AI for NPCs

  • Helps you write self documenting code, that is maintainable and readable

  • Reduces the boilerplate code required to write a state machine

  • UnityHFSM is lightweight and efficient, making it perfect for solving small and big problems

  • It is especially designed for Unity, and supports coroutines, which would otherwise be difficult to integrate in a state machine

  • No GC Allocations for state changes / updates / ... after setting up the state machine (-> No unwanted GC related lag spikes because of the state machine)

  • The code is well documented

  • It is not a component (MonoBehaviour) and therefore has a low overhead

  • By being hierarchical, it can reduce the amount of duplicate code between states


In the wiki:

Table of contents:

Installation

Unity Package

To get started, download the latest version of UnityHFSM from the Releases page. Simply extract the zip file and put the folder anywhere in your Assets folder. Et voilà, you're ready to go!

UPM Package

<details> <summary>Add from OpenUPM <em>| via scoped registry</em></summary>

To add OpenUPM to your project:

  • Open Edit/Project Settings/Package Manager

  • Add a new Scoped Registry:

    Name: OpenUPM
    URL:  https://package.openupm.com/
    Scope(s): com.inspiaaa.unityhfsm
    
  • Click <kbd>Save</kbd>

  • Open Package Manager

  • Select My Registries in dropdown top left

  • Select UnityHFSM and click Install

    </details>
<details> <summary>Add from GitHub | <em>no updates through PackMan</em></summary>

You can also add it directly from GitHub on Unity 2019.4+. Note that you won't be able to receive updates through Package Manager this way, you'll have to update manually.

  • Open Package Manager (<kbd>Window</kbd> > <kbd>Package Manager</kbd>)
  • Click <kbd>+</kbd>
  • Select <kbd>Add from Git URL</kbd>
  • Paste
    • https://github.com/Inspiaaa/UnityHFSM.git#upm for the latest stable release (recommended)
    • https://github.com/Inspiaaa/UnityHFSM.git#release for the development version
    • https://github.com/Inspiaaa/UnityHFSM.git#v1.8.0 for a specific version (v1.8.0 here)
  • Click <kbd>Add</kbd>
  • Tip: If you're using VSCode, and you're not getting any IntelliSense, you may have to regenerate the .csproj project files (<kbd>Edit</kbd> > <kbd>Preferences</kbd> > <kbd>External Tools</kbd> > <kbd>Regenerate project files</kbd>)
</details>

Example

Simple State Machine

Here's a simple state machine for an enemy spy in a game.

As you can see, the enemy will try to stay outside of the player's viewing range while extracting intel. When the player goes too far away, it will follow the player again.

The Idea

  • Create the state machine:

    fsm = new StateMachine();
    
  • Add states: Each state is represented by a StateBase object. You can either use one of the built-in state classes (e.g. the State class) to define the state's logic or write a custom class that inherits from StateBase.

    fsm.AddState(
        name,
        new State(
            onEnter,
            onLogic,
            onExit
        )
    );
    
  • Add transitions: Transitions can be defined using TransitionBase objects. They determine when and under which conditions the state machine should switch to another state. As with the states, the simplest option is to use one of the builtin transition types (e.g. the Transition class). If you instead choose to create a custom transition class by inheriting from TransitionBase, see the class-based architecture section for more information.

    fsm.AddTransition( new Transition(
        from,
        to,
        condition
    ));
    
  • Initialize the state machine

    fsm.SetStartState(id);
    fsm.Init();
    
  • Run the state machine:

    void Update() {
        fsm.OnLogic()
    }
    

The Implementation

Creating the state machine

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityHFSM;  // Import the required classes for the state machine.

public class EnemyController : MonoBehaviour
{
    private StateMachine fsm;

    public float playerScanningRange = 4f;
    public float ownScanningRange = 6f;

    void Start()
    {
        fsm = new StateMachine();
    }
}

Adding states

To be able to define the states' logic concisely, we'll need to create some helper methods and properties first. Their implementation is just an example and may differ for your scene setup.

Vector2 PlayerPosition => PlayerController.Instance.transform.position;

float DistanceToPlayer => Vector2.Distance(PlayerPosition, transform.position);

void MoveTowardsPlayer(float speed) {
    transform.position = Vector2.MoveTowards(
        transform.position,
        PlayerPosition,
        speed * Time.deltaTime
    );
}

Next, we can define the states in the Start method:

void Start()
{
    fsm = new StateMachine();

    // Empty state without any logic.
    fsm.AddState("ExtractIntel");

    fsm.AddState("FollowPlayer", new State(
        onLogic: state => MoveTowardsPlayer(1)
    ));

    fsm.AddState("FleeFromPlayer", new State(
        onLogic: state => MoveTowardsPlayer(-1)
    ));

    // This configures the entry point of the state machine.
    fsm.SetStartState("FollowPlayer");
}

Notice the fsm.AddState("ExtractIntel") call. Interestingly, we don't need to pass in a state object here. This is due to one of the many "shortcut" methods that UnityHFSM provides. These are meant to reduce the amount of boilerplate code required for common operations, such as adding blank states. The above line is equivalent to writing:

fsm.AddState("ExtractIntel", new StateBase<string>(needsExitTime: false));

Furthermore, we can actually shorten the other state definitions even more by using shortcut methods. We can for example write the FollowPlayer state in a more concise way:

fsm.AddState("FollowPlayer", onLogic: state => MoveTowardsPlayer(1));

Although this example is using lambda expressions for the states' logic, you can of course also just pass normal functions.

Side note: To keep things simple, we're using strings for the state identifiers. Just keep in mind that UnityHFSM is not limited to this, as it allows you to use any custom type (e.g. enums) for the state identifiers. See the generics chapter for more information.

Adding transitions

void Start()
{
    // ...

    fsm.AddTransition(new Transition(
        "ExtractIntel",
        "FollowPlayer",
        transition => DistanceToPlayer > ownScanningRange
    ));

    fsm.AddTransition(new Transition(
        "FollowPlayer",
        "ExtractIntel",
        transition => DistanceToPlayer < ownScanningRange
    ));

    fsm.AddTransition(new Transition(
        "ExtractIntel",
        "FleeFromPlayer",
        transition => DistanceToPlayer < playerScanningRange
    ));

    fsm.AddTransition(new Transition(
        "FleeFromPlayer",
        "ExtractIntel",
        transition => DistanceToPlayer > playerScanningRange
    ));
}

Although the above code snippet is definitely functional, you may notice that it seems a bit overly verbose and repetitive. That's because it is. We can do better!

Firstly, as creating transitions with the Transition c

View on GitHub
GitHub Stars1.5k
CategoryDesign
Updated19h ago
Forks149

Languages

C#

Security Score

100/100

Audited on Apr 1, 2026

No findings