imjp94 / gd-YAFSM

Yet Another Finite State Machine for godot
MIT License
534 stars 25 forks source link
godot

gd-YAFSM icongd-YAFSM (godot-Yet Another Finite State Machine)

Designer-friendly Finite State Machine implemented in "Godotic" way

This version is only compatible with Godot 4.x, check out godot3 branch for older version

Content

Feature

For more detail, see CHANGELOG.md

Installation

  1. Install directly from Godot Asset Library

or

  1. Download this respository, move addons/imjp94.yafsm to your {project_dir}

  2. Enable it from Project -> Settings -> Plugins

or

  1. Install with gd-plug
plug("imjp94/gd-YAFSM")

Getting Started

Editor

Getting Started

  1. Add StateMachinePlayer iconStateMachinePlayer node from "Create New Node" window.

  2. Select created node and the state machine editor should shows up.

  3. Click on "Create StateMachine" button to get started.

Finally, Right-Click on graph to add state node and Shift + Drag on node to start connect(Shift + Drag again on line to reconnect)

Special states:

Code

After setup StateMachine with editor, you can connect to the following signals from a StateMachinePlayer:

Signal Example Example code snippet of KinematicBody connect "updated" signal

And control StateMachinePlayer by accessing parameter:

var smp = get_node("StateMachinePlayer")
smp.set_trigger("jump")
smp.set_param("jump_count", 1)
smp.get_param("on_floor", false)
smp.has_param("velocity")

That's it!

For most of the case, you don't have to inherit from any custom class by this plugin, simply just connect signals to your existing node and you're good to go.

See documentation for more details

Nested FSM

The only different between nested/normal FSM is how state/parameters are accessed.

State

var normal_state = "Idle"
var nested_state = "App/Game/Play" # EndState can be Entry/Exit

StateDirectory class is provided to traverse state path like file directory:

const StateDirectory = preload("addons/imjp94.yafsm/src/StateDirectory.gd")

# Handle "transited" signal
func _on_normal_state_transited(from, to):
    match to:
        "Entry":
            print("Enter")
        "Game":
            print("Game")
        "Exit":
            print("Exit")

# Handle "transited" signal
func _on_nested_state_transited(from, to):
    var to_dir = StateDirectory.new(to)

    match to_dir.next(): # Initial next() required to move to base state
        "Entry":
            print("Enter")
        "Game":
            match to_dir.next(): # It can be called recursively, until return null
                "Entry":
                    print("Game Enter") # Game/Entry
        "Exit":
            print("Exit")

Parameter

Behind the scene, StateMachinePlayer always differentiate parameters into 2 types: global & local

var smp = get_node("StateMachinePlayer")
var global_param = smp.get_param("state")
var local_param = smp.get_param("App/Game/playing")
local_param = smp.get_nested_param("App/Game", "playing")
smp.set_param("App/Game/End/victory", true)
smp.set_nested_param("App/Game", "paused", true)

Besides of controlling StateMachinePlayer, it's useful to set arbitrary value with set_param

var smp = get_node("StateMachinePlayer")
smp.set_param("game", preload("game.scn"))
var game_scn = smp.get_param("game")

Debug

Demo

Check out gd-YAFSM-demo for how you can integrate gd-YAFSM into you project and manage app state with StateMachine

Documentation

Refer to Documentation located in addons/imjp94.yafsm/README.md