Giter Club home page Giter Club logo

unity-fsm's Introduction

unity-fsm

What is it?

A lightweight finite state machine implementation that supports custom and conditional transitions

What are the requirements?

  • Unity 2018.x

How to use it.

Full example:

var fsm = new FiniteStateMachine<string>("closed", "open", "locked");
fsm.AddTransition("closed", "open", OPEN_COMMAND)
   .AddTransition("closed", "locked", LOCK_COMMAND, customTransition) // using a custom transition
   .AddTransition("locked", "closed", UNLOCK_COMMAND, () => user.HasKey()) // using a condition
   .AddTransition("open", "closed", CLOSE_COMMAND)
   .OnEnter(open, () => Debug.Log("The door is now open!"))
   .OnExit(closed, HandleDoorIsNoLongerClosed);

A finite state machine is composed of STATES and TRANSITIONS, and is controled by COMMANDS

Imagine the example diagram modelling a door:

          "CLOSE"               "LOCK"
    +-------->---------+  +-------->---------+
    |                  |  |                  |
 +----+              +------+              +------+
 |OPEN|              |CLOSED|              |LOCKED|
 +----+              +------+              +------+
    |                  |  |                  |
    +--------<---------+  +--------<---------+
           "OPEN"                "UNLOCK"

Here we have 3 states, OPEN, CLOSED, and LOCKED, with 4 transitions, controlled by 4 commands OPEN, CLOSE, LOCK and UNLOCK.

Now let's break it down

Step 1: Create it

First let's create the fsm object using it's constructor that takes any number of STATES. A state can be any IComparable. In this case we will use strings.

var fsm = new FiniteStateMachine<string>("closed", "open", "locked");

we could also use the static helper FromEnum, that will take an enum and use every value as a state

var fsm =  FiniteStateMachine<DoorStates>.FromEnum();

Step 2: Configure it

Next we can configure the finite state machine's transitions using a fluent interface to add the transitions.

AddTransition takes, a from STATE, to STATE and a COMMAND that will trigger it. It literally reads like, _when in state X, I can move to state Y with the command Z. It also optionally takes a condition check function or a custom transition for further control

fsm.AddTransition("closed", "open", OPEN_COMMAND)
   .AddTransition("closed", "locked", LOCK_COMMAND, customTransition) // using a custom transition
   .AddTransition("locked", "closed", UNLOCK_COMMAND, () => user.HasKey()) // using a condition
   .AddTransition("open", "closed", CLOSE_COMMAND);

Condition functions

The condition function is useful for guarding transitions that depend on state outside the domain of the thing you are modelling. In this case "does the user have the key" is a peice of state that does not belong to the door, but should control whether or not the door can be unlocked.

Custom transitions

Implement your own custom transition by extending the abstract class Transition<TState>. Here you can control the transition between the states, such as animating objects, or the timing of the transition.

Step 3 (optional): Add your own hooks

Optional, but quite likely required will be to add your own hooks. Again using the fluent interface, you can get callbacks when you enter, or exit a specific state, or go between 2 specific states, or just any change

fsm
    // Called on every state change
    .OnChange((fromState, toState) => { Debug.Log("State has changed from fromState to toState"); });
    // Called on when we go from open to closed
    .OnChange("locked", "open", () => { Debug.Log("The door was unlcocked"); });
    // Called on entry to the specified state
    .OnEnter("open", () => Debug.Log("The door is open everyone!"))
    // Called on exit from the specified state
    .OnExit("closed", HandleDoorIsNoLongerClosed);

Step 4: Begin and start issuing commands

fsm.Begin(closed);
fsm.IssueCommand(openCommand); // door should now be open
fsm.IssueCommand(lockCommand); // nothing will happen (no transition from open using lock command)


// get the current state
fsm.CurrentState; // will equal "open"

Motivation

The door example above could be modelled quite easily with a class containing 2 boolean values for our states isLocked and isOpen. Then a bunch of functions Open() Close() Lock() Unlock for our commands. The problem is the user can then call Lock() and Open() and the state would look like this:

isLocked = true;
isOpen = true;

This is not a valid state for our door model. We would have to add checks in all those functions. It's not a lot in this case but in other situations it can turn into a lot of over complicated code. Using the FSM, we can configure which transitions are allowed, and it cannot possibly get into an invalid state, because that state doesn't exist!

To recreate this model we would need checks in all of our command functions and to expose and implement the required event hooks. There are a lot of situations where we can just replace this with a fsm.

FAQs

Can I use this to control my game states?

This is not a solution for high level so called "game state" management. It can be used to control that but normally those require fully connected graphs. and integration with asset loaded. It's not recommended. The term state is a misnomer in this context anyway.

Is there an easy way to add commands to go to any state from any state?

No, that would negate the need to have a finite state machine. By using this you are describing how a domain is allowed to change state. If you want to go anywhere from anywhere, you probably just want a single enum variable and the ability to set it.

unity-fsm's People

Contributors

kkjamie avatar

Watchers

James Cloos avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.