{"id":13662229,"url":"https://github.com/Inspiaaa/UnityHFSM","last_synced_at":"2025-04-25T07:30:59.660Z","repository":{"id":37398985,"uuid":"251574086","full_name":"Inspiaaa/UnityHFSM","owner":"Inspiaaa","description":"A simple yet powerful class-based hierarchical finite state machine for Unity","archived":false,"fork":false,"pushed_at":"2024-05-28T09:38:00.000Z","size":2194,"stargazers_count":987,"open_issues_count":11,"forks_count":115,"subscribers_count":29,"default_branch":"master","last_synced_at":"2024-08-02T05:13:46.668Z","etag":null,"topics":["coroutines","csharp","finite-state-machine","fsm","gamedev","hierarchical","lightweight","state-machine","unity","unity3d"],"latest_commit_sha":null,"homepage":"","language":"C#","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Inspiaaa.png","metadata":{"files":{"readme":"README.md","changelog":"Changelog.md","contributing":null,"funding":null,"license":"LICENSE.md","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2020-03-31T10:44:31.000Z","updated_at":"2024-08-01T13:58:11.000Z","dependencies_parsed_at":"2023-11-15T00:24:27.387Z","dependency_job_id":"4fa65b9c-ff58-48d6-a34b-d9c5488d9941","html_url":"https://github.com/Inspiaaa/UnityHFSM","commit_stats":null,"previous_names":[],"tags_count":7,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Inspiaaa%2FUnityHFSM","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Inspiaaa%2FUnityHFSM/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Inspiaaa%2FUnityHFSM/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Inspiaaa%2FUnityHFSM/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Inspiaaa","download_url":"https://codeload.github.com/Inspiaaa/UnityHFSM/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":223988064,"owners_count":17236838,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["coroutines","csharp","finite-state-machine","fsm","gamedev","hierarchical","lightweight","state-machine","unity","unity3d"],"created_at":"2024-08-02T05:01:52.807Z","updated_at":"2025-04-25T07:30:59.644Z","avatar_url":"https://github.com/Inspiaaa.png","language":"C#","readme":"![HFSM for Unity](https://raw.githubusercontent.com/Inspiaaa/UnityHFSM/master/docs/Banner.png)\n\n\u003cp align=\"center\"\u003e\n  \u003ca href=\"https://github.com/Inspiaaa/UnityHFSM\"\u003e\n    \u003cimg src=\"https://img.shields.io/badge/Unity-C%23-blue.svg?\u0026logo=unity\" /\u003e\u003c/a\u003e\n  \u003ca href=\"https://github.com/Inspiaaa/UnityHFSM/blob/master/LICENSE.md\" alt=\"GitHub license\"\u003e\n    \u003cimg src=\"https://img.shields.io/badge/License-MIT-green.svg\" /\u003e\u003c/a\u003e\n  \u003ca href=\"https://github.com/Inspiaaa/UnityHFSM/releases\" alt=\"GitHub release\"\u003e\n    \u003cimg src=\"https://img.shields.io/github/release/Inspiaaa/UnityHFSM.svg\" /\u003e\u003c/a\u003e\n  \u003ca href=\"https://openupm.com/packages/com.inspiaaa.unityhfsm/\"\u003e\n    \u003cimg src=\"https://img.shields.io/npm/v/com.inspiaaa.unityhfsm?label=openupm\u0026registry_uri=https://package.openupm.com\" /\u003e\u003c/a\u003e\n\u003c/p\u003e\n\nA 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**.\n\nThanks to overloading, it minimises boilerplate code while still supporting **generics**.\n\nIt has a special focus on the temporal aspects of state transitions, making it ideal for timing and synchronisation sensitive problems.\n\n---\n\n### Why use UnityHFSM?\n\n- 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)\n\n- It's **easy** and **straightforward** to use\n\n- It helps you **tame complex behaviour** (e.g. weapon handling -\u003e cooling, ammo, reload, fire) or write AI for NPCs\n\n- Helps you write **self documenting code**, that is **maintainable** and **readable**\n\n- **Reduces the boilerplate** code required to write a state machine\n\n- UnityHFSM is **lightweight** and **efficient**, making it perfect for solving small and big problems\n\n- It is **especially designed for Unity**, and supports **coroutines**, which would otherwise be difficult to integrate in a state machine\n\n- **No GC Allocations** for state changes / updates / ... after setting up the state machine (-\u003e No unwanted GC related lag spikes because of the state machine)\n\n- The code is **well documented**\n\n- It is **not a component** (MonoBehaviour) and therefore has a **low overhead**\n\n- By being hierarchical, it can reduce the amount of duplicate code between states\n\n---\n\n**In the wiki:**\n\n- [Full overview over features](https://github.com/Inspiaaa/UnityHFSM/wiki/Feature-Overview)\n\n- [How to use UnityHFSM outside of Unity](https://github.com/Inspiaaa/UnityHFSM/wiki/Using-UnityHFSM-outside-of-Unity)\n\n**Table of contents:**\n\n- [Fast prototyping](#simple-state-machine)\n\n- [Hierarchical features](#hierarchical-state-machine)\n\n- [Timing of state changes](#timing-of-state-changes)\n\n- [Hierarchical timing](#hierarchical-timing)\n\n- [Multiple state change patterns](#state-change-patterns)\n\n- [Control flow of OnLogic](#control-flow-of-onlogic)\n\n- [Unity **coroutines**](#unity-coroutines)\n\n- [Scalable (class-based)](#class-based-architecture)\n\n- [Generics](#generics)\n\n- [Debugging Tips](#debugging-tips)\n\n## Installation\n\n### Unity Package\n\nTo get started, download the latest version of UnityHFSM from the [Releases](https://github.com/Inspiaaa/UnityHFSM/releases) page. Simply extract the zip file and put the folder anywhere in your `Assets` folder. Et voilà, you're ready to go!\n\n### UPM Package\n\n\u003cdetails\u003e\n\u003csummary\u003eAdd from OpenUPM \u003cem\u003e| via scoped registry\u003c/em\u003e\u003c/summary\u003e\n\nTo add OpenUPM to your project:\n\n- Open `Edit/Project Settings/Package Manager`\n\n- Add a new Scoped Registry:\n  \n  ```\n  Name: OpenUPM\n  URL:  https://package.openupm.com/\n  Scope(s): com.inspiaaa.unityhfsm\n  ```\n\n- Click \u003ckbd\u003eSave\u003c/kbd\u003e\n\n- Open Package Manager\n\n- Select ``My Registries`` in dropdown top left\n\n- Select ``UnityHFSM`` and click ``Install``\n  \n  \u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003eAdd from GitHub | \u003cem\u003eno updates through PackMan\u003c/em\u003e\u003c/summary\u003e\n\nYou 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.\n\n- Open Package Manager (\u003ckbd\u003eWindow\u003c/kbd\u003e \u003e \u003ckbd\u003ePackage Manager\u003c/kbd\u003e)\n- Click \u003ckbd\u003e+\u003c/kbd\u003e\n- Select \u003ckbd\u003eAdd from Git URL\u003c/kbd\u003e\n- Paste\n  - `https://github.com/Inspiaaa/UnityHFSM.git#upm` for the latest stable release (**recommended**)\n  - `https://github.com/Inspiaaa/UnityHFSM.git#release` for the development version\n  - `https://github.com/Inspiaaa/UnityHFSM.git#v1.8.0` for a specific version (`v1.8.0` here)\n- Click \u003ckbd\u003eAdd\u003c/kbd\u003e\n- Tip: If you're using VSCode, and you're not getting any IntelliSense, you may have to regenerate the `.csproj` project files (\u003ckbd\u003eEdit\u003c/kbd\u003e \u003e \u003ckbd\u003ePreferences\u003c/kbd\u003e \u003e \u003ckbd\u003eExternal Tools\u003c/kbd\u003e \u003e \u003ckbd\u003eRegenerate project files\u003c/kbd\u003e)\n\n\u003c/details\u003e\n\n---\n\n## Example\n\n## Simple State Machine\n\nHere's a simple state machine for an enemy spy in a game.\n\n![](https://raw.githubusercontent.com/Inspiaaa/UnityHFSM/master/docs/StateDiagrams/EnemySpyExample/Simple.png)\n\nAs 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.\n\n### The Idea\n\n- **Create the state machine:**\n  \n  ```csharp\n  fsm = new StateMachine();\n  ```\n\n- **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`.\n  \n  ```csharp\n  fsm.AddState(\n      name,\n      new State(\n          onEnter,\n          onLogic,\n          onExit\n      )\n  );\n  ```\n\n- **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](#class-based-architecture) for more information.\n  \n  ```csharp\n  fsm.AddTransition( new Transition(\n      from,\n      to,\n      condition\n  ));\n  ```\n\n- **Initialise the state machine**\n  \n  ```csharp\n  fsm.SetStartState(id);\n  fsm.Init();\n  ```\n\n- **Run the state machine:**\n  \n  ```csharp\n  void Update() {\n      fsm.OnLogic()\n  }\n  ```\n\n### The Implementation\n\n#### Creating the state machine\n\n```csharp\nusing System.Collections;\nusing System.Collections.Generic;\nusing UnityEngine;\nusing UnityHFSM;  // Import the required classes for the state machine.\n\npublic class EnemyController : MonoBehaviour\n{\n    private StateMachine fsm;\n\n    public float playerScanningRange = 4f;\n    public float ownScanningRange = 6f;\n\n    void Start()\n    {\n        fsm = new StateMachine();\n    }\n}\n```\n\n#### Adding states\n\nTo 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.\n\n```csharp\nVector2 PlayerPosition =\u003e PlayerController.Instance.transform.position;\n\nfloat DistanceToPlayer =\u003e Vector2.Distance(PlayerPosition, transform.position);\n\nvoid MoveTowardsPlayer(float speed) {\n    transform.position = Vector2.MoveTowards(\n        transform.position,\n        PlayerPosition,\n        speed * Time.deltaTime\n    );\n}\n```\n\nNext, we can define the states in the `Start` method:\n\n```csharp\nvoid Start()\n{\n    fsm = new StateMachine();\n\n    // Empty state without any logic.\n    fsm.AddState(\"ExtractIntel\");\n\n    fsm.AddState(\"FollowPlayer\", new State(\n        onLogic: state =\u003e MoveTowardsPlayer(1)\n    ));\n\n    fsm.AddState(\"FleeFromPlayer\", new State(\n        onLogic: state =\u003e MoveTowardsPlayer(-1)\n    ));\n\n    // This configures the entry point of the state machine.\n    fsm.SetStartState(\"FollowPlayer\");\n}\n```\n\nNotice 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: \n\n```csharp\nfsm.AddState(\"ExtractIntel\", new StateBase\u003cstring\u003e(needsExitTime: false));\n```\n\nFurthermore, 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:\n\n```csharp\nfsm.AddState(\"FollowPlayer\", onLogic: state =\u003e MoveTowardsPlayer(1));\n```\n\nAlthough this example is using lambda expressions for the states' logic, you can of course also just pass normal functions.\n\n\u003e **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](#generics) chapter for more information.\n\n#### Adding transitions\n\n```csharp\nvoid Start()\n{\n    // ...\n\n    fsm.AddTransition(new Transition(\n        \"ExtractIntel\",\n        \"FollowPlayer\",\n        transition =\u003e DistanceToPlayer \u003e ownScanningRange\n    ));\n\n    fsm.AddTransition(new Transition(\n        \"FollowPlayer\",\n        \"ExtractIntel\",\n        transition =\u003e DistanceToPlayer \u003c ownScanningRange\n    ));\n\n    fsm.AddTransition(new Transition(\n        \"ExtractIntel\",\n        \"FleeFromPlayer\",\n        transition =\u003e DistanceToPlayer \u003c playerScanningRange\n    ));\n\n    fsm.AddTransition(new Transition(\n        \"FleeFromPlayer\",\n        \"ExtractIntel\",\n        transition =\u003e DistanceToPlayer \u003e playerScanningRange\n    ));\n}\n```\n\nAlthough 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!\n\nFirstly, as creating transitions with the `Transition` class is so common, there are shortcut methods to minimise the amount of boilerplate code. The first transition is equivalent to writing:\n\n```csharp\nfsm.AddTransition(\"ExtractIntel\", \"FollowPlayer\",\n    transition =\u003e DistanceToPlayer \u003e ownScanningRange);\n```\n\nSecondly, it is a common pattern to define a transition in one direction when a condition is true, and in the other direction when it is false. UnityHFSM provides a special feature to implement this in code: two way transitions. Using a combination of this feature and shortcut methods we can reduce the original code snippet to the following:\n\n```csharp\nfsm.AddTwoWayTransition(\"ExtractIntel\", \"FollowPlayer\",\n    transition =\u003e DistanceToPlayer \u003e ownScanningRange);\n\nfsm.AddTwoWayTransition(\"ExtractIntel\", \"FleeFromPlayer\",\n    transition =\u003e DistanceToPlayer \u003c playerScanningRange);\n```\n\n#### Initialising and running the state machine\n\n```csharp\nvoid Start()\n{\n    // ...\n\n    // Initialises the state machine and must be called before OnLogic() \n    // is called.\n    fsm.Init();\n}\n\nvoid Update()\n{\n    fsm.OnLogic();\n}\n```\n\nAt this point, the basic version of the enemy controller is finished and ready to use :D\n\nBy the way, using all of the aforementioned tricks, the entire definition of the state machine can be reduced to remarkably few lines of code:\n\n```csharp\nvoid Start()\n{\n    fsm = new StateMachine();\n\n    fsm.AddState(\"ExtractIntel\");\n    fsm.AddState(\"FollowPlayer\", onLogic: state =\u003e MoveTowardsPlayer(1));\n    fsm.AddState(\"FleeFromPlayer\", onLogic: state =\u003e MoveTowardsPlayer(-1));\n\n    fsm.SetStartState(\"FollowPlayer\");\n\n    fsm.AddTwoWayTransition(\"ExtractIntel\", \"FollowPlayer\",\n        transition =\u003e DistanceToPlayer \u003e ownScanningRange);\n    fsm.AddTwoWayTransition(\"ExtractIntel\", \"FleeFromPlayer\",\n        transition =\u003e DistanceToPlayer \u003c playerScanningRange);\n\n    fsm.Init();\n}\n```\n\n## Hierarchical State Machine\n\nWhen dealing with more complex behaviour, it is natural to break the problem down into sub-problems. This approach maps beautifully to state machines. First you define the states and then in a later step you can add the logic and behaviour to them. When designing a state, it can sometimes be useful to draw another state diagram just for it and to think about how you can break down its behaviour. The result of this is a hierarchy of states - a hierarchical state machine.\n\nHow can we implement this in UnityHFSM?\n\nBecause the `StateMachine` class inherits from `StateBase`, it can be treated as a normal state. This allows for the nesting of state machines together with states. On top of this, UnityHFSM provides additional features to describe the timing / exit conditions for a state machine (more on this later).\n\n### Expanding on the previous example\n\n![](https://raw.githubusercontent.com/Inspiaaa/UnityHFSM/master/docs/StateDiagrams/EnemySpyExample/Hierarchical.png)\n\nIn the previous example, we left the `Extract Intel` state empty. Let's change this. When extracting intel, the spy should first collect data and then send it, repeating the process once completed.\n\nSo that you can see a visual difference, let's make it that the enemy spins when it enters the `Send Data` state, like it's sending the data out in all directions.\n\n### The Idea\n\n- Create a separate state machine for the nested states (States in `Extract Intel`)\n\n- Add the nested states to the new state machine\n\n- Add the new state machine to the main state machine as a normal state\n\n### The Implementation\n\n#### Separate FSM for the ExtractIntel state\n\n```csharp\nvoid Start()\n{\n    // This is the main state machine.\n    fsm = new StateMachine();\n\n    // This is the nested state machine.\n    var extractIntel = new StateMachine();\n    fsm.AddState(\"ExtractIntel\", extractIntel);\n\n    // ...\n}\n```\n\n#### Adding States and Transitions\n\nWe want the enemy to stay in the `CollectData` and in the `SendData` state for 5 seconds each. There are a few ways we can implement this.\n\nOne option would be to use the built-in `TransitionAfter` class to define a transition that only activates after a certain delay. Using it would look like this:\n\n```csharp\nextractIntel.AddTransition(new TransitionAfter(\"CollectData\", \"SendData\", 5));\n```\n\nAnother option could be to use UnityHFSM's timing features. We can add transitions that do not have a condition and should therefore make the state machine instantly advance to the next state. At the same time, we declare that the `CollectData` and `SendData` states require \"exit time\", meaning that the transitions should only occur once the active state is ready and can cleanly exit. See the code snippet below and the next section for more details.\n\n```csharp\nvoid RotateAtSpeed(float speed)\n    =\u003e transform.eulerAngles += new Vector3(0, 0, speed * Time.deltaTime);\n\nvoid Start()\n{\n    fsm = new StateMachine();\n\n    var extractIntel = new StateMachine();\n    fsm.AddState(\"ExtractIntel\", extractIntel);\n\n    extractIntel.AddState(\"SendData\",\n        onLogic: state =\u003e {\n            // When the state has been active for more than 5 seconds,\n            // notify the fsm that the state can cleanly exit.\n            if (state.timer.Elapsed \u003e 5)\n                state.fsm.StateCanExit();\n\n            // Make the enemy turn at 100 degrees per second.\n            RotateAtSpeed(100f);\n        },\n        // This means the state won't instantly exit when a transition should\n        // happen but instead the state machine waits until it is given permission\n        // to change state.\n        needsExitTime: true\n    );\n\n    extractIntel.AddState(\"CollectData\",\n        // The canExit function is another way to define when the state is\n        // allowed to exit (it calls `fsm.StateCanExit()` internally).\n        canExit: state =\u003e state.timer.Elapsed \u003e 5,\n        needsExitTime: true\n    );\n\n    extractIntel.SetStartState(\"CollectData\");\n\n    // A transition without a condition.\n    extractIntel.AddTransition(\"SendData\", \"CollectData\");\n    extractIntel.AddTransition(\"CollectData\", \"SendData\");\n\n    // ...\n}\n```\n\n## Timing of State Changes\n\nThe timing of state changes is controlled by the active state's `needsExitTime` property.\n\nWhen `needsExitTime = false`, the state can exit at any point in time, e.g. because of a transition, regardless of its current internal state.\n\nWhen `needsExitTime = true`, this cannot happen. It indicates to the state machine, that it may need more time before it is ready to exit. Any transition that should happen is delayed and becomes the \"pending transition\". This is very useful when you do not want an action to be interrupted before it has ended, like in the above case. (This can be skipped / overridden by forcing a transition using `forceInstantly = true`).\n\nBut when is the right time for the state machine to finally change states? This is where the `fsm.StateCanExit()` method comes. Calling `fsm.StateCanExit()` notifies the state machine that the state can cleanly exit. If a transition is pending, it will be executed.\n\nHere's what happens in the general case when a transition should happen:\n\n1. The state machine calls `activeState.OnExitRequest()`. If the state can exit, it should call `fsm.StateCanExit()`.\n\n2. If the state couldn't exit when `OnExitRequest()` was called, the active state has to notify the state machine at a later point in time that it can exit by calling the `fsm.StateCanExit()` method. This can e.g. happen in an `OnLogic` call or when an event is triggered.\n\n```mermaid\nflowchart TD\n  Start([Transition]) --\u003e NeedsExitTime{\"Does active state\u003cbr\u003eneed exit time?\"}\n\n  NeedsExitTime --\u003e|No| End([Change State])\n\n  NeedsExitTime --\u003e|Yes| OnExitRequest[\"FSM calls\u003cbr\u003eactiveState.OnExitRequest()\"]\n  OnExitRequest --\u003e StateCanExit{\"Does the active state\u003cbr\u003ecall fsm.StateCanExit()?\"}\n  StateCanExit --\u003e|Yes| End\n  StateCanExit --\u003e|No| Later\n\n  subgraph \"Delayed State Change\"\n    Later[\"Later in\u003cbr\u003eOnLogic / an event / ...\"]\n    Later --\u003e CallStateCanExit[\"fsm.StateCanExit()\"]\n  end\n\n  CallStateCanExit --\u003e End\n```\n\nAnother quality of life feature is the `canExit` property of the `State` class. It allows you to write in a declarative way when the state is ready to exit. Internally, the passed `canExit` function is called on each exit request and on each logic call if a transition is pending. If it returns true, it calls `fsm.StateCanExit()`.  In the above example, the `SendData` code could look like this using this feature:\n\n```csharp\nextractIntel.AddState(\"SendData\",\n    onLogic: state =\u003e RotateAtSpeed(100f),\n    canExit: state =\u003e state.timer.Elapsed \u003e 5,\n    needsExitTime: true\n);\n```\n\n## Hierarchical Timing\n\n\u003e Warning: This is one of the most advanced and complex features of UnityHFSM. The other chapters are easier to follow.\n\n### The Idea\n\nAlthough the `CollectData` and `SendData` states both have their `needsExitTime` properties set to true, the state machine will instantly exit them, if the player moves too close or too far away. After all, nested state machine that holds these two states does not have its own `needsExitTime` property set to true.\n\nTo fix this, we can simply set `needsExitTime = true` in the `ExtractIntel` state machine. This however raises another question: When is the nested state machine allowed to exit? And how can we indicate that it is allowed to exit?\n\nThe answer is by using **exit transitions**. These are special transitions that are only checked, when the parent state machine has a pending transition - in other words: when the parent state machine wants the nested state machine to exit. When the transition succeeds, the nested state machine exits so that the parent state machine can transition to its next state. To be exact, the nested state machine only exits when an exit transition succeeds *and* its currently active state is ready to exit (which is always true when `needsExitTime = false`, otherwise when `StateCanExit()` is called).\n\nLet's use this feature in our example. Here's what we'll be doing: \n\n- While collecting data, the spy should be able to exit the `ExtractIntel` state at any time. This can be achieved by adding an exit transition from `ExtractIntel`. \n\n- Once all the data has been collected, it should send it out, regardless of where the player is. It does not matter if the player is too far away, as the data has already been collected. To keep it simple, let's make it that the enemy also tries to finish the sending phase, even if it risks being discovered by the player by getting to close.\n\n![](https://raw.githubusercontent.com/Inspiaaa/UnityHFSM/master/docs/StateDiagrams/EnemySpyExample/HierarchicalWithExitTransition.png)\n\n### The Implementation\n\nFirstly, we'll edit the `Start` method so that the nested state machine has `needsExitTime` set to true:\n\n```csharp\nvar extractIntel = new StateMachine(needsExitTime: true);\n```\n\nSecondly, the state machine should be able to exit `CollectData` at any time, meaning that its `needsExitTime` should be set to false. This means that we'll also need to use a `TransitionAfter` transition later.\n\n```csharp\nextractIntel.AddState(\"CollectData\");  // needsExitTime = false by default\n```\n\nFinally, we also have to add the transitions. The `SendData --\u003e CollectData` transition can stay the same. As `CollectData` can instantly exit now (`needsExitTime = false`), but we only want to transition to `SendData` after 5 seconds, we'll have to use a `TransitionAfter` transition. Furthermore, we have to add the exit transition.\n\n```csharp\nextractIntel.AddTransition(\"SendData\", \"CollectData\");\n\n// Exit transition without a condition.\nextractIntel.AddExitTransition(\"CollectData\");\n\nextractIntel.AddTransition(new TransitionAfter(\"CollectData\", \"SendData\", 5));\n```\n\nIn the above code snippet, there are two important things to note:\n\n- The line adding the exit transition uses a shortcut method. It's roughly the same as writing: \n  \n  ```csharp\n  extractIntel.AddExitTransition(new Transition(\"CollectData\", \"\"));\n  ```\n  \n  We don't need to define a condition, as it won't be checked unless the main state machine is trying to transition from `ExtractIntel` to another state.\n\n- The order of the transitions matters. Transitions that are added first, are also checked first and therefor have a higher precedence. In this case, we want to prioritise exit transition over the normal transition to the `SendData` state, hence it is added first.\n\n\u003e **Tip:** When debugging a complex hierarchical state machine, it can be really handy to know not only which state is active in the root state machine (using `fsm.ActiveStateName`) but also which states are active in nested state machines.\n\u003e \n\u003e That's why UnityHFSM has a feature to do just this:\n\u003e \n\u003e ```csharp\n\u003e print(fsm.GetActiveHierarchyPath());\n\u003e ```\n\u003e \n\u003e Example output: `/ExtractIntel/CollectData`.\n\u003e \n\u003e Then you can use this on each `Update()` call to debug the state machine.\n\nHow could we change the code so that the enemy spy aborts sending the data when the player gets too close? This means that we would want to instantly exit the `ExtractIntel` state and go to the `FleeFromPlayer` state. This requires us to override the `needsExitTime` property of `ExtractIntel` by marking its outbound transition to `FleeFromPlayer` as a forced transition with `forceInstantly = true`.\n\n```csharp\nfsm.AddTwoWayTransition(\"ExtractIntel\", \"FleeFromPlayer\",\n    transition =\u003e DistanceToPlayer \u003c playerScanningRange,\n    forceInstantly: true);\n```\n\nTo keep it simple, the transitions between `ExtractIntel` and `FleeFromPlayer` are forced in both directions (because we're using a two way transition). In our example it does not make any difference. However, if you wanted to only force it in one direction, you could simply create two separate transitions.\n\n## State Change Patterns\n\nThe state machine supports three ways of changing states:\n\n1. Using `Transition` objects as described earlier. You can even have multiple transitions that connect the same two states. They are checked on every `OnLogic` call and can be seen as a type of **polling**.\n   \n   ```csharp\n   fsm.AddTransition(\n       new Transition(\n           from,\n           to,\n           condition\n       )\n   );\n   ```\n\n2. Calling the `RequestStateChange` method: Instead of using `Transition` objects to manage state changes, each state can individually also manage its own transitions by **directly** calling the `RequestStateChange` method.\n   \n   ```csharp\n   fsm.RequestStateChange(state, forceInstantly: false);\n   ```\n   \n   **Example**\n   \n   ```csharp\n   fsm.AddState(\"FollowPlayer\", new State(\n       onLogic: (state) =\u003e\n       {\n           MoveTowardsPlayer(1);\n   \n           if (DistanceToPlayer() \u003c ownScanningRange)\n           {\n               fsm.RequestStateChange(\"ExtractIntel\");\n           }\n       }\n   ));\n   ```\n\n3. Using \"Trigger Transitions\": These are normal transitions that are only checked when a certain trigger (an event) is activated.\n   \n   These are really handy when a polling-based solution does not fit or is not efficient enough. Trigger Transitions let you effortlessly leverage the efficiency of **event-based** transitions, in combination with the full power of the existing high-level transition types.\n   \n   ```csharp\n   fsm.AddTriggerTransition(triggerName, transition);\n   ```\n   \n   **Example**\n   \n   ```csharp\n   // Flappy Bird Example\n   fsm.AddTriggerTransition(\n       \"OnCollision\",\n       new Transition(\"Alive\", \"Dead\")\n   );\n   \n   // Later\n   fsm.Trigger(\"OnCollision\");\n   ```\n\nTherefore, UnityHFSM supports **both polling-based and event-based** transitions, as well as the feature to bypass the concept of transitions all together. That's pretty cool.\n\nThere is also a slight variation of the `Transition` state change behaviour, that allows you to change to a specific state **from any** other state (a \"global\" transition as opposed to a \"local\" / \"direct\" transition). They have the same `forceInstantly` / `needsExitTime` handling as normal transitions.\n\n```csharp\nfsm.AddTransitionFromAny( new Transition(\n    from,\n    to,\n    condition\n));\n\n// For Trigger Transitions\nfsm.AddTriggerTransitionFromAny(\n    triggerName,\n    transition\n);\n```\n\n**Example**\n\n```csharp\nfsm.AddTransitionFromAny( new Transition(\n    \"\",    // From can be left empty, as it has no meaning in this context\n    \"Dead\",\n    t =\u003e (health \u003c= 0)\n));\n\n// For Trigger Transitions\nfsm.AddTriggerTransitionFromAny(\n    \"OnDamage\",\n    new Transition(\"\", \"Dead\", t =\u003e (health \u003c= 0))\n);\n```\n\n## Control Flow of OnLogic\n\nEvery StateMachine's `OnLogic` method manages the automatic transitions via `Transition` (`TransitionBase`) objects and the active state's logic function.\n\nHere's what happens:\n\n1. The state machine checks all global transitions (transitions from any state) and sees if a transition should occur. If this is the case, the state machine will advance to the new state, and call the new state's `OnLogic` function.\n\n2. If this is not the case, the fsm checks all direct transitions (transitions that go directly from the active state to another state) and sees if a transition should occur. If this is the case, the state machine will move on to the new state, and call the new state's `OnLogic` function.\n\n3. If this is not the case, the fsm will finally call the currently active state's `OnLogic` function.\n\nBecause global transitions are checked first, they have the highest priority. The reason for this design decision can be illustrated with the following example: When the health of the player drops below 0, regardless of which state the fsm is in, it should transition to the `Dead` state.\n\nAs you can see on the steps mentioned above, only one transition can occur per `OnLogic` call. This has many reasons, one of which being that the state machine does not get stuck in an infinite loop.\n\n```mermaid\nflowchart TD\n    Start([OnLogic])\n    GlobalTransitions{Is a global\u003cbr\u003etransition triggered?}\n    LocalTransitions{Is a direct\u003cbr\u003etransition triggered?}\n    ActiveState[\"ActiveState.OnLogic()\"]\n    Transition\n    NewState[\"NewState.OnLogic()\"]\n    Return([Return])\n\n    Start --\u003e GlobalTransitions\n    GlobalTransitions --\u003e |No| LocalTransitions\n    GlobalTransitions --\u003e |Yes| Transition\n    LocalTransitions --\u003e  |No| ActiveState\n    LocalTransitions --\u003e  |Yes| Transition\n\n    Transition --\u003e NewState\n\n    ActiveState --\u003e Return\n    NewState --\u003e Return\n```\n\nThere is however a way to perform multiple transitions in one `OnLogic` call: As soon as the state machine enters a state that is marked as a **\"ghost state\"**, it will instantly try all of its outbound transitions. If any one succeeds, it will instantly transition to the next state.\n\nExample:\n\n```csharp\nfsm.AddState(\"A\");\nfsm.AddState(\"B\", new State(isGhostState: true));\nfsm.AddState(\"C\");\n\nfsm.AddTriggerTransition(\"Event\", \"A\", \"B\");\nfsm.AddTransition(\"B\", \"C\");\n```\n\nAt a later point, when `Event` is triggered, the state machine will exit `A`, enter `B`, exit `B`, enter `C` and finally call the `OnLogic` method of `C`:\n\n```csharp\nfsm.Trigger(\"Event\");\n```\n\n## Unity Coroutines\n\nBy using the `CoState` class you can run coroutines. This class handles the following things automatically:\n\n- Starting the coroutine\n\n- Optional: Running the coroutine again once it has completed\n\n- Terminating the coroutine when the state exits\n\nAs a result of a [limitation of the C# language](https://stackoverflow.com/questions/35473442/yield-return-in-the-lambda-expression), you can sadly not use lambda expressions to define IEnumerators (=\u003e coroutines).\n\nHere's how we could use it in our example: We can replace the `SendData` state with a more advanced one, which makes the spy turn in one direction for two seconds, and then in the other direction for the same duration.\n\n```csharp\nIEnumerator SendData() \n{\n    var timer = new Timer();\n\n    while (timer.Elapsed \u003c 2)\n    {\n        RotateAtSpeed(100f);\n        // Wait until the next frame.\n        yield return null;\n    }\n\n    while (timer.Elapsed \u003c 4)\n    {\n        RotateAtSpeed(-100f);\n        yield return null;\n    }\n\n    // Because needsExitTime is true, we have to tell the FSM when it can\n    // safely exit the state.\n    fsm.StateCanExit();\n}\n\nvoid Start()\n{\n    // ...\n\n    extractIntel.AddState(\"SendData\", new CoState(\n        this,   // Pass in the MonoBehaviour that should run the coroutine.\n        SendData,\n        loop: true,  // Repeat the coroutine once finished.\n        needsExitTime: true\n    ));\n\n    // ...\n}\n```\n\nThe `CoState` class also allows you to pass in an iterator function that takes the `CoState` as a parameter. One of the side effects of the way the UnityHFSM is internally implemented regarding its inheritance hierarchy and its support for generics, is that the function has to take the state as a `CoState\u003cstring, string\u003e` object and not simply as `CoState`:\n\n```csharp\nIEnumerator SendData(CoState\u003cstring, string\u003e state)\n{\n    // ...\n}\n```\n\n\u003e **Tip:** When designing your state machine, it can sometimes be difficult to decide when to use a nested state machine and when to use a coroutine via `CoState`. Although both can usually achieve the desired outcome, one may be a lot simpler to implement than the other. As a rule of thumb, use a coroutine if you notice that your state diagram resembles a flowchart, otherwise use a hierarchical state machine.\n\n## Custom Events\n\nBy default, UnityHFSM uses three main events:\n- On Enter: The state machine has switched to this state.\n- On Logic: The state machine checks the polling-based transitions and updates the active state.\n- On Exit: The state machine has switched to another state.\n\nIn Unity, having one update function (on logic) is often not enough, as we sometimes want to run code in the `FixedUpdate` or `LateUpdate` calls. In UnityHFSM we can add such custom events to the state machine via the **action system**. For more information and usage instructions you can check out the complete [feature overview wiki page](https://github.com/Inspiaaa/UnityHFSM/wiki).\n\n## Class-Based Architecture\n\nUnityHFSM is fundamentally designed in an object-oriented manner which allows you to easily create custom state and transition types. By simply inheriting from the common base classes (`StateBase`, `TransitionBase`), custom states and transitions can be developed. This is also how the built-in state and transition types, such as `CoState` and `TransitionAfter`, have been implemented internally.\n\nHere's an overview over the methods you can override:\n\n**Creating custom states**\n\n```csharp\nclass CustomState : StateBase\n{\n    public CustomState()\n        : base(needsExitTime: false, isGhostState: false) { }\n\n    public override void Init() { }\n\n    public override void OnEnter() { }\n    public override void OnLogic() { }\n    public override void OnExit() { }\n\n    public override void OnExitRequest() { }\n}\n```\n\n**Creating custom transitions**\n\n```csharp\nclass CustomTransition : TransitionBase\n{\n    public CustomTransition(string from, string to)\n        : base(from, to, forceInstantly: false) { }\n\n    public override bool ShouldTransition() { return true; }\n\n    public override void Init() { }\n\n    public override void BeforeTransition() { }\n    public override void AfterTransition() { }\n}\n```\n\nWhen developing custom state and transition classes, it's also worth understanding how UnityHFSM handles generics (see below) and how the inheritance hierarchy is structured (see the [wiki](https://github.com/Inspiaaa/UnityHFSM/wiki/State-Classes)), so that you can support custom actions (events) in your classes.\n\n## Generics\n\nUnityHFSM also provides first-class support for generics. This means that if you do not want to use  strings for the state identifiers and the events, you can easily use a different datatype, e.g. `int`, enums, custom classes, even booleans...\n\nAdvantages of using types other than string:\n\n- It can improve the **safety** of your state machine as it can prevent the issue of typos in the string state names leading to errors. Using enums can ensure that the state identifiers are checked at compile-time.\n\n- It can offer a better mapping to different kinds of problems, e.g. using `int` events for HTTP response status codes instead of strings.\n\n- It can greatly **improve the base performance** of state machines. Using ints or enums as the state identifiers can speed up the internal mechanics by up to 50% depending on the use case. \n  \n  \u003e **Side note:** UnityHFSM is already highly optimised and in most cases the time spent in the user's code far outweighs the internal time spent performing transitions / update calls in the state machine. However, in the rare case where reducing the execution time by a few nanoseconds makes a difference, you can use ints or enums to reduce UnityHFSM's overhead to a minimum.\n\nEvery nested state machine can use its own type for the state identifiers (`TStateId`). The only thing that all state machines in a given hierarchy have to share, is the type for the triggers / events (`TEvent`), so that they can be passed down the hierarchy.\n\nTo realise this, the `StateMachine` class takes 3 generic type parameters: `TOwnId`, `TStateId` and  `TEvent`. `TStateId` is the type for the state names in a state machine, i.e. of its child states. If you use a state machine as a state (-\u003e it would be a nested state machine), it itself needs an identifier for the parent state machine. This is the `TOwnId` type parameter. Lastly `TEvent` is the type for the triggers and events. The order of the parameters is: `StateMachine\u003cTOwnId, TStateId, TEvent\u003e`.\n\nThere are also multiple overloads for the `StateMachine` class that reduce the boilerplate code needed to get started:\n\n- `StateMachine` = `StateMachine\u003cstring, string, string\u003e`\n\n- `StateMachine\u003cTStateId\u003e` = `StateMachine\u003cTStateId, TStateId, string\u003e`\n\n- `StateMachine\u003cTStateId, TEvent\u003e` = `StateMachine\u003cTStateId, TStateId, TEvent\u003e`\n\nHere's a small example that shows how you can mix different types:\n\n```csharp\nenum PlayerStates {\n    IDLE, MOVE, JUMP\n}\n\nenum MoveStates {\n    WALK, DASH\n}\n\nenum Events {\n    ON_DAMAGE, ON_WIN\n}\n```\n\n```csharp\nvar fsm = new StateMachine\u003cPlayerStates, Events\u003e();\nvar moveFsm = new StateMachine\u003cPlayerStates, MoveStates, Events\u003e();\nvar idleFsm = new StateMachine\u003cPlayerStates, string, Events\u003e();\n\nfsm.AddState(PlayerStates.IDLE, idleFsm);\nfsm.AddState(PlayerStates.MOVE, moveFsm);\nfsm.AddState(PlayerStates.JUMP, new State\u003cPlayerStates, Events\u003e());\n// Or simply using shortcut methods: fsm.AddState(PlayerStates.JUMP);\n\nmoveFsm.AddState(MoveStates.WALK);\nmoveFsm.AddState(MoveStates.DASH);\nmoveFsm.AddTransition(new Transition\u003cMoveStates\u003e(MoveStates.WALK, MoveStates.DASH));\n// Or simply: fsm.AddTransition(MoveStates.WALK, MoveStates.DASH);\n\nidleFsm.AddState(\"Animation 1\", new State\u003cstring, Events\u003e());\nidleFsm.AddState(\"Animation 2\");\n\n// ...\n```\n\n### A Short Note on Performance\n\nUnityHFSM is engineered with both power and performance in mind. It’s designed to deliver robust functionality without compromising on efficiency, making it an **ideal choice for both small and complex projects** in Unity.\n\nThe source code has been carefully benchmarked and optimised to maintain consistent performance across a wide range of use cases.\n\nIt follows a \"pay only for what you use\" design philosophy, both in terms of **memory and performance**. That means that supporting more features does not come at the price of performance. For example, thanks to **lazy initialisation**, UnityHFSM remains extremely lightweight for smaller scenarios, when less features are used. At the same time, its scalable architecture and advanced features are fully capable of handling the demands of larger, more complex projects efficiently.\n\n## Debugging Tips\n\nHere are a couple of tips and tricks you can use to debug complex state machines:\n\n- **Error messages**: When UnityHFSM detects a problem, it throws an exception with a detailed error message that can help you pinpoint the problem and find a solution.\n  - Usually, the **first error** that is thrown is the most important one, as the following ones are most likely a just a consequence of the first error.\n  - As the error messages span multiple lines, you have to **click on the error** in the console in the Unity Editor in order to see the full message.\n\n  **Example error message:**\n  ```\n  StateMachineException: \n  In state machine 'Root/Fight'\n  Context: Switching states\n  Problem: The state \"Wait\" has not been defined yet / doesn't exist.\n  Solution: \n  1. Check that there are no typos in the state names and transition from and to names\n  2. Add this state before calling Init / OnEnter / OnLogic / RequestStateChange / ...\n  ```\n\n  It explains where this error occurred (in the `Fight` child state machine), what went wrong (target state of the transition was not found), and possible solutions.\n\n- When a hierarchical state machine is not behaving as expected, you can call the `GetActiveHierarchyPath()` method on the root state machine and print its result to the console. It tells you **which states are currently active** within a hierarchical state machine:\n\n  ```csharp\n  print(fsm.GetActiveHierarchyPath());  // e.g. \"/ExtractIntel/CollectData\"\n  ```\n\n- Alternatively, if you prefer a more **visual approach**, you can use the **animator graph** feature. It creates an `AnimatorController` in the Unity Editor that lets you understand the structure of a state hierarchy visually. At the same time, it can show you in real-time which state the state machine is currently in.\n\n  ![Animator Graph Example](https://raw.githubusercontent.com/Inspiaaa/UnityHFSM/d679f37e70eada76f7742a53b3eed03bb6a4dfe3/docs/Images/AnimatorGraphVideo.gif)\n\n  You can find a tutorial on this topic in the [wiki](https://github.com/Inspiaaa/UnityHFSM/wiki/Visualising-State-Machines-with-Animator-Graphs).\n\n- If you are working on more advanced code and want to produce accurate information regarding the path to the current state **from within the state itself**, without having access to the root state machine, you can use the inspection-related code. The `UnityHFSM.Inspection` namespace is the foundation for dynamic tools like the animator graph feature, but can also be used for debugging (it's what is used for the built-in error messages). In particular, the `StateMachineWalker` class could be of interest:\n\n  ```csharp\n  print(StateMachineWalker.GetStringPathOfState(this.fsm));  \n  // Prints the path to the current state.\n  // E.g. \"Root/Fight/Hit\"\n  ```\n\n# Development\n\nIf you want to develop new code for UnityHFSM or contribute to the project, you can take a look at the [development wiki page](https://github.com/Inspiaaa/UnityHFSM/wiki/Development). It gives you a brief introduction to the project file structure and a short guide on how to run the unit tests.\n\n---\n\nFor more documentation check out the [Wiki](https://github.com/Inspiaaa/UnityHFSM/wiki).\n","funding_links":[],"categories":["C\\#","Open Source Repositories"],"sub_categories":["AI"],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FInspiaaa%2FUnityHFSM","html_url":"https://awesome.ecosyste.ms/projects/github.com%2FInspiaaa%2FUnityHFSM","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FInspiaaa%2FUnityHFSM/lists"}