{"id":21714427,"url":"https://github.com/real-serious-games/fluent-state-machine","last_synced_at":"2025-04-13T11:08:16.305Z","repository":{"id":70745381,"uuid":"45805336","full_name":"Real-Serious-Games/Fluent-State-Machine","owner":"Real-Serious-Games","description":"Fluent API for creating state machines in C#","archived":false,"fork":false,"pushed_at":"2019-08-12T01:36:35.000Z","size":87,"stargazers_count":284,"open_issues_count":1,"forks_count":42,"subscribers_count":22,"default_branch":"master","last_synced_at":"2025-04-13T11:08:13.019Z","etag":null,"topics":["finite-state-machine","fsm","game-development","hierarchical-state-machine","hsm","nested-states","state-machine","unity","unity3d"],"latest_commit_sha":null,"homepage":null,"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/Real-Serious-Games.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","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":"2015-11-09T00:10:43.000Z","updated_at":"2025-03-29T23:47:10.000Z","dependencies_parsed_at":null,"dependency_job_id":"e5af68b0-902e-4c2c-8e96-93f356771635","html_url":"https://github.com/Real-Serious-Games/Fluent-State-Machine","commit_stats":null,"previous_names":[],"tags_count":2,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Real-Serious-Games%2FFluent-State-Machine","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Real-Serious-Games%2FFluent-State-Machine/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Real-Serious-Games%2FFluent-State-Machine/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Real-Serious-Games%2FFluent-State-Machine/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Real-Serious-Games","download_url":"https://codeload.github.com/Real-Serious-Games/Fluent-State-Machine/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248703198,"owners_count":21148118,"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":["finite-state-machine","fsm","game-development","hierarchical-state-machine","hsm","nested-states","state-machine","unity","unity3d"],"created_at":"2024-11-26T00:35:08.431Z","updated_at":"2025-04-13T11:08:16.284Z","avatar_url":"https://github.com/Real-Serious-Games.png","language":"C#","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Fluent-State-Machine  [![Build Status](https://travis-ci.org/Real-Serious-Games/Fluent-State-Machine.svg)](https://travis-ci.org/Real-Serious-Games/Fluent-State-Machine) [![NuGet](https://img.shields.io/nuget/dt/Fluent-State-Machine.svg)](https://www.nuget.org/packages/Fluent-State-Machine/)\r\nFluent API for creating [hierarchical finite state machines](http://aigamedev.com/open/article/hfsm-gist/) in C#.\r\n\r\n\u003c!-- START doctoc generated TOC please keep comment here to allow auto update --\u003e\r\n\u003c!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE --\u003e\r\n\r\n\r\n- [A basic example - creating a state machine with a single state](#a-basic-example---creating-a-state-machine-with-a-single-state)\r\n- [Conditions](#conditions)\r\n- [Using multiple states](#using-multiple-states)\r\n- [Nesting states](#nesting-states)\r\n- [Pushing and popping nested states](#pushing-and-popping-nested-states)\r\n- [Events](#events)\r\n- [Custom states](#custom-states)\r\n- [Examples](#examples)\r\n  - [Example 1](#example-1)\r\n  - [Unity Example](#unity-example)\r\n\r\n\u003c!-- END doctoc generated TOC please keep comment here to allow auto update --\u003e\r\n\r\n## A basic example - creating a state machine with a single state\r\n\r\nReference the dll and include the namespace:\r\n\r\n    using RSG;\r\n    \r\nCreate a state via the builder class:\r\n\r\n    var rootState = new StateMachineBuilder()\r\n        .State(\"main\")\r\n            .Enter(state =\u003e {\r\n                Console.WriteLine(\"Entered main state\");\r\n            })\r\n            .Update((state, deltaTime) =\u003e {\r\n                Console.WriteLine(\"Updating main state\");\r\n            })\r\n        .End()\r\n        .Build();\r\n        \r\nCalling `Build()` on our StateMachineBuilder sets up all the states we've specified and returns a root\r\nstate which is automatically created to be the parent of any top-level states we create. \r\n\r\nThe function in `Enter` will be called once when we first enter the state, and the function in `Update`\r\nwill be called every time we update the state.\r\n\r\nOnce our state machine is set up, we then need to set the initial state:\r\n\r\n    rootState.ChangeState(\"main\");\r\n    \r\nFinally, we can update our currently active state with a specified time since the last update as follows:\r\n\r\n    rootState.Update(timeSinceLastFrame); \r\n    \r\n## Conditions \r\n\r\nAs well as Enter and Update, conditions can be set up, which are functions called when the state is\r\nupdated only when a specified predicate is satisfied.\r\n\r\n    var rootState = new StateMachineBuilder()\r\n        .State(\"main\")\r\n            .Enter(state =\u003e {\r\n                Console.WriteLine(\"Entered main state\");\r\n            })\r\n            .Update((state, deltaTime) =\u003e {\r\n                Console.WriteLine(\"Updating main state\");\r\n            })\r\n            .Condition(() =\u003e SomeBoolean, state =\u003e {\r\n                Console.WriteLine(\"Condition satisfied\");\r\n            })\r\n        .End()\r\n        .Build();\r\n        \r\nThe predicate to the condition is also a function, and this function will be invoked every time the state\r\nis updated.\r\n\r\n## Using multiple states\r\n\r\nSwitch between states by using `IState.ChangeState(stateName)` \r\n\r\n    var rootState = new StateMachineBuilder()\r\n        .State(\"main\")\r\n            .Enter(state =\u003e {\r\n                Console.WriteLine(\"Entered main state\");\r\n            })\r\n            .Update((state, deltaTime) =\u003e {\r\n                Console.WriteLine(\"Updating main state\");\r\n            })\r\n            .Condition(() =\u003e SomeBoolean, state =\u003e {\r\n                state.Parent.ChangeState(\"secondary\");\r\n            })\r\n            .Exit(state =\u003e {\r\n                Console.WriteLine(\"Exited main state\");\r\n            })\r\n        .End()\r\n        .State(\"secondary\")\r\n            .Enter(state =\u003e {\r\n                Console.WriteLine(\"Entered secondary state\")\r\n            })\r\n        .End()\r\n        .Build();\r\n        \r\n`ChangeState` will attempt to exit the current state and change to the specified one. Note that since\r\nboth our \"main\" state and \"secondary\" states are children of the root state, we actually need to call\r\nChangeState on the main state's parent (which in this case is the root state). \r\n\r\nThe function specified in `Exit` will be called when we exit the main state.\r\n\r\n## Nesting states\r\n\r\nNested states, sometimes referred to as *super-states* are useful for encapsulating different parts of\r\nlogic and simplifying transitions between states.\r\n\r\n    var rootState = new StateMachineBuilder()\r\n        .State(\"main\")\r\n            .Enter(state =\u003e {\r\n                Console.WriteLine(\"Entered main state\");\r\n            })\r\n            .Update((state, deltaTime) =\u003e {\r\n                Console.WriteLine(\"Updating main state\");\r\n            })\r\n            .Condition(() =\u003e SomeBoolean, state =\u003e {\r\n                state.ChangeState(\"child\");\r\n            })\r\n            .Exit(state =\u003e {\r\n                Console.WriteLine(\"Exited main state\");\r\n            })\r\n            .State(\"child\")\r\n                .Enter(state =\u003e {\r\n                    Console.WriteLine(\"Entered secondary state\")\r\n                })\r\n            .End()\r\n        .End()\r\n        .Build();\r\n\r\nOur second state is now a child of the main state. This example doesn't really add any extra \r\nfunctionality but serves to show how a nesting can work in its most basic form. Note that `Exit` is\r\nnot called on the main state when we change to it even though it is no longer being updated, since\r\nit is still technically active but only the end of the tree is updated.\r\n\r\n## Pushing and popping nested states\r\n\r\nIn addition to a list of children, each state contains a stack of active children\r\n\r\n    var rootState = new StateMachineBuilder()\r\n        .State(\"main\")\r\n            .Enter(state =\u003e {\r\n                Console.WriteLine(\"Entered main state\");\r\n            })\r\n            .Update((state, deltaTime) =\u003e {\r\n                Console.WriteLine(\"Updating main state\");\r\n            })\r\n            .Condition(() =\u003e SomeBoolean, state =\u003e {\r\n                state.PushState(\"firstChild\");\r\n            })\r\n            .Exit(state =\u003e {\r\n                Console.WriteLine(\"Exited main state\");\r\n            })\r\n            .State(\"firstChild\")\r\n                .Enter(state =\u003e {\r\n                    Console.WriteLine(\"Entered secondary state\")\r\n                })\r\n                .Condition(() =\u003e SomeBoolean, state =\u003e {\r\n                    state.Parent.PushState(\"secondChild\");\r\n                })\r\n            .End()\r\n            .State(\"secondChild\")\r\n                .Enter(state =\u003e {\r\n                    Console.WriteLine(\"Entered third child state\")\r\n                })\r\n                .Condition(() =\u003e ShouldPopState, state =\u003e {\r\n                    state.Parent.PopState();\r\n                })\r\n            .End()\r\n        .End()\r\n        .Build();\r\n\r\nIn this example, we will start out in `main`, then when `SomeBoolean` is true we'll go to `firstChild`.\r\nIf `SomeBoolean` is still true we will transition to `secondChild`, and then when `ShouldPopState` is\r\ntrue we will go back the previous state on the stack (in this case `firstChild`). \r\n\r\n## Events\r\n\r\nThe state builder doesn't give us a reference to the new states it creates, but if we want to trigger \r\nan action on the currently active state that we don't want to run every frame (like a *condition*)\r\nwe can use an *event*.\r\n\r\n    var rootState = new StateMachineBuilder()\r\n        .State(\"main\")\r\n            .Enter(state =\u003e {\r\n                Console.WriteLine(\"Entered main state\");\r\n            })\r\n            .Update((state, deltaTime) =\u003e {\r\n                Console.WriteLine(\"Updating main state\");\r\n            })\r\n            .Condition(() =\u003e SomeBoolean, state =\u003e {\r\n                state.ChangeState(\"child\");\r\n            })\r\n            .State(\"child\")\r\n                .Enter(state =\u003e {\r\n                    Console.WriteLine(\"Entered secondary state\")\r\n                })\r\n                .Event(\"MyEvent\", state =\u003e {\r\n                    Console.WriteLine(\"MyEvent triggered\");\r\n                });\r\n            .End()\r\n        .End()\r\n        .Build();\r\n\r\nInvoking the event on the root state will trigger it on whatever the currently active state is:\r\n\r\n    rootState.TriggerEvent(\"MyEvent\"); \r\n\r\n## Custom states\r\n\r\nSometimes we will want to have data shared by the Enter, Update, Condition, Event and Exit functions\r\nof a state, specific to that state, or we might want to have some methods that are internal to just\r\nthat state. This an be achieved by creating a sub-class of `AbstractState` that has our extra \r\nfunctionality in it, and specifying this when we create the state.\r\n\r\n    class MainState : AbstractState\r\n    {\r\n        public string updateMessage = \"Updating main state\";\r\n        \r\n        public void PushChildState()\r\n        {\r\n            PushState(\"child\");\r\n        }\r\n    }\r\n    \r\nNote that since this class inherits from AbstractState, it has full access to methods for pushing, \r\npopping and changing states. Since this will be newed-up by the state builder it must have either\r\nno constructor or a parameterless constructor.\r\n\r\nSetting up the state is basically the same as it would otherwise be except that we now also specify\r\nthe type of the state. The name field is now optional since the builder can just automatically take \r\nthe name from the name of the class it uses, although since no two states that are children of the\r\nsame state can have the same name, this can only be done once per type of state for each group.\r\n\r\n    var rootState = new StateMachineBuilder()\r\n        .State\u003cMainState\u003e()\r\n            .Enter(state =\u003e {\r\n                Console.WriteLine(\"Entered main state\");\r\n            })\r\n            .Update((state, deltaTime) =\u003e {\r\n                Console.WriteLine(state.updateMessage);\r\n            })\r\n            .Condition(() =\u003e SomeBoolean, state =\u003e {\r\n                state.PushChildState();\r\n            })\r\n            .State(\"child\")\r\n                .Enter(state =\u003e {\r\n                    Console.WriteLine(\"Entered secondary state\")\r\n                })\r\n                .Event(\"MyEvent\", state =\u003e {\r\n                    Console.WriteLine(\"MyEvent triggered\");\r\n                });\r\n            .End()\r\n        .End()\r\n        .Build();\r\n        \r\n## Examples\r\n\r\nIn the `Examples` directory there are a couple of example projects to demonstrate usage of the library.\r\n\r\n### Example 1\r\n\r\nThis sample demonstrates a fairly simple state machine with custom nested states, from a console app.\r\n\r\n### Unity Example\r\n\r\nThis sample comes as a Unity project and shows how one could set up the library for use in a Unity \r\ngame, as well as using *events* to trigger actions in response to Unity physics collision events. ","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Freal-serious-games%2Ffluent-state-machine","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Freal-serious-games%2Ffluent-state-machine","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Freal-serious-games%2Ffluent-state-machine/lists"}