Coder Social home page Coder Social logo

oceanfsm's Introduction

Ocean Finite State Machine

apu

A code-only, simple and easy to use Finite State Machine for your Unity Projects!

GitHub package.json version GitHub license GitHub last commit OpenUPM

player-fsm-example.mp4

Getting Started

Installation

OpenUPM (Recommended)

openupm add com.macawls.oceanfsm

Git URL (Recommended)

  1. Open the package manager window
  2. Click the plus icon
  3. Choose "Add package from git URL..."
  4. Use the link below.
https://github.com/Macawls/OceanFsm.git

Manual (Not Recommended)

Add the following to your manifest.json.

{
  "scopedRegistries": [
    {
      "name": "OpenUPM",
      "url": "https://package.openupm.com",
      "scopes": [
        "com.macawls.oceanfsm"
      ]
    }
  ],
  "dependencies": { 
    // Replace with latest version or version of your choice
    "com.macawls.oceanfsm": "{version}" 
  }
}

Samples

After adding the package, you can use the import button from the package manager window to inspect the samples.

The samples were created from the Default 3D Unity template.

How to Use

States

1. Functionality

To add functionality to a state, you can override the following methods.

virtual void OnInitialize() { }  // called once
virtual void OnEnter() { } 
virtual void OnExit() { } 
virtual void OnUpdate(float deltaTime) { } // depends on how you decide to update the state machine

2. Creating a State

All states have to inherit from the State<T> class.

Generic Usage

The generic reference type T is used to associate your states and state machines.

You can use whatever you want, but I recommend using an interface to keep things tidy. Lets define IDoor as our reference type.

public interface IDoor
{
    void Close();
    void Open();
}
public class Door : MonoBehaviour, IDoor 
{
    private IPollingMachine<IDoor> _mFsm;
    
    // Instance of IDoor passed to constructor of the builder, because Door implements IDoor
    _mFsm = new PollingBuilder<IDoor>(this) 
    ...
 }

Here, all the states will have access to instance which implements IDoor using the Runner property. Like so.

class Closed : State<IDoor>
{
    public override void OnEnter()
    {
	Runner.Close();
    }
}

States also have a Machine property. If the machine is not castable to IAutonomousStateMachine<T>, it will be null.

class Closed : State<IDoor>
{
    public override void OnUpdate()
    {
        if (PlayerIsNearby() && Input.GetKeyDown(unlockKey))
        {
            Machine.ChangeState<Open>(() => {
                Debug.Log("Player has opened the door");
            });
        }
    }
}

4. Inheritance Example

For a simple use case for inheritance, suppose we have a base class called PlayerStateBase where when we enter a new state, we'd like to play a specific animation. It would look something like this.

[Serializable]
public abstract class PlayerStateBase : State<IPlayer>
{
    [SerializeField] private AnimationClip stateAnimation;

    public override void Enter()
    {
        Runner.PlayAnimation(stateAnimation);
    }
}
[Serializable]
public class Jump : PlayerStateBase
{
    // whatever fields you need
    
    public override void Enter()
    {
        base.Enter(); // play the animation
        // other logic
    }
}

5. Utilities

Since all states have to inherit from State<T> class, you can use the State Generator tool to generate a state boilerplate class for you.

You'll find it under the Tools dropdown menu.

State generation tool

State Machines

1. State Machine Types

There are two types at the moment :)

Autonomous State Machine

  • States are responsible for transitioning to other states.
  • The machine can receive commands to transition to a specific state.
  • Can freely transition to any state.

Polling State Machine

  • The machine holds an internal dictionary of states and their transitions.
  • Transitions are triggered by a condition/predicate.
  • States cannot transition by themselves.

2. Creating the State Machine

There are two builders to aid in creating state machines.

Polling State Machine

private IPollingMachine<IDoor> _mFsm;

private void Awake()
{
    var closed = new Closed();
    var open = new Open();

    _mFsm = new PollingBuilder<IDoor>(this)
        .SetStartingState(nameof(Closed))
        .AddTransition(closed, open, () => {
            return PlayerIsNearby && Input.GetKeyDown(KeyCode.Space) && !_mIsLocked)
        })
        .AddTransition(open, closed, () => {
            return PlayerIsNearby() && Input.GetKeyDown(KeyCode.Space)
        }, onTransition: () => {
            Debug.Log("Closing the door"); // optional
        })
        .Build();
}

Autonomous State Machine

private IAutonomousMachine<IPlayer> _mFsm;

private void Awake()
{
    _mFsm = new AutonomousBuilder<IDoor>(this)
        .SetStartingState(nameof(Idle))
        .AddState(new Idle())
        .AddState(new Walk())
        .AddState(new Jump())
        .Build();
}

3. Command Usage (WIP)

Currently, only the AutonomousStateMachine supports commands. Commands are useful for triggering actions or responding to events from outside the state machine. Conditions and the actions are optional.

private void Awake()
{
    _mFsm = new AutonomousBuilder<IPlayer>(this)
        .SetInitialState(nameof(Idle)) 
        .AddStates(idle, run, jump)
        .Build();
    
    _mFsm.AddCommand("Jump")
        .SetTargetState<Jump>()
        .SetCondition(() => _mFsm.CurrentState is Run && IsGrounded)
        .OnSuccess(() => Debug.Log("Hi mom")) // visual fx for example
        .OnFailure(() => Debug.Log("depression")); // negative sound effect for example
}

private void OnJump(InputAction.CallbackContext ctx)
{
    if (ctx.performed)
    {
        _mFsm.ExecuteCommand("Jump");
    }
}

4. Running the State Machine

It is completely up to you how you want to run the state machine. The key methods are:

void Start();
void Stop();
void Update(float deltaTime);
void Evaluate(); // only for Polling State machines

Important to note that the state machine will not run until you call Start()

if you're using the Polling state machine, I would recommend calling Evaluate() in Late Update.

Evaluate() will continuously check all transitions of the current state. If a transition is met, it will change to the new state.

Using Monobehaviour Hooks

private void OnEnable()
{
    _mFsm.Start();
}

private void OnDisable()
{
    _mFsm.Stop();
}

private void Update()
{
    _mFsm.Update(Time.deltaTime);
}

private void LateUpdate()
{
    _mFsm.Evaluate();
}

5. Which one should I use?

I've found that if it's a simple entity with a few states and transitions, the Polling state machine is good. For example a door, checkpoint, traffic light, treasure chest etc.

If it's an entity that is fairly complex and or reacts to external input , the Autonomous state machine is the way to go. Something like a player, enemy, NPC, UI system etc.

The Autonomous one is easier to use and more flexible. Most of the time I recommend using it.

oceanfsm's People

Contributors

kiraio-moe avatar macawls avatar semantic-release-bot avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

oceanfsm's Issues

package-lock.json not included in package

I really like the user-facing design of this FSM so I wanted to give it a try, but importing via UPM or Git package gives me warnings:

A meta data file (.meta) exists but its asset 'Packages/com.macawls.oceanfsm/package-lock.json' can't be found. When moving or deleting files outside of Unity, please ensure that the corresponding .meta file is moved or deleted along with it.

Couldn't delete Packages/com.macawls.oceanfsm/package-lock.json.meta because it's in an immutable folder.

package-lock.json is indeed missing, but the .meta file is there

Unity 6000.0.5

edit: sample scene is broken as well :-)

[BUG] OceanFSM causing build to fail

Unity will fail to build the project because OceanFSM trying to include Editor code into Runtime by selecting the Platforms at OceanFSM.Editor Assembly Definition to Any Platform instead of only Editor.

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.