Last active
October 5, 2021 12:09
-
-
Save NovemberDev/ca8557899f8f360e22873a1f75c290f6 to your computer and use it in GitHub Desktop.
Godot Finite state machine script / class in one file
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# godot_finite_state_machine.gd | |
# by: @november_dev | |
# | |
# The finite state machine takes in a number of states and transitions. | |
# | |
# A state can contain a function that will be executed or looped while the state is active. | |
# | |
# The transition gets a function that returns a boolean truthiness value, which determines | |
# if the transition should happen or not. | |
# | |
# The transition can also get a before_transition and after_transition function. | |
# | |
# In this example, some conditions are not given and the function f() is just a shorthand for | |
# funcref(self, func_name) | |
# | |
# Basically, we define states, which execute logic until a condition from a transition is true. | |
# In this example we go from moving to in air, if we dont touch the floor. If we do touch the floor, | |
# we go from in air back to moving. | |
# | |
# | |
# Usage: | |
# | |
# var fsm = load("res://scripts/fsm.gd").fsm.new() | |
# | |
# func _ready(): | |
# fsm.state("moving", f("fsm_moving"), true) | |
# fsm.state("in_air", f("fsm_in_air"), true) | |
# fsm.on("entry").transition("moving", f("cond_true")) | |
# | |
# #implement your own condition functions | |
# fsm.on("moving").transition("in_air", f("cond_in_air")) | |
# fsm.on("in_air").transition("moving", f("cond_on_floor")) | |
# | |
#func _physics_process(delta): | |
# fsm.update() | |
# | |
#func f(func_name: String): | |
# return funcref(self, func_name) | |
# | |
#func cond_true(): | |
# return true | |
class fsm: | |
var states = {} | |
var transitions = {} | |
var current_state = "entry" | |
var last_inserted_state: String | |
func _init(): | |
state("entry") | |
state("anystate") | |
transitions["anystate"] = [] | |
# call this method during processing | |
func update(): | |
if (!states[current_state].executed and !states[current_state].loop) or states[current_state].loop: | |
if states[current_state].logic != null: | |
states[current_state]["logic"].call_func() | |
states[current_state].executed = true | |
handle_transition("anystate") | |
handle_transition(current_state) | |
func handle_transition(state: String): | |
if transitions.has(state): | |
for s in transitions[state]: | |
if s.condition != null: | |
if s["condition"].call_func(): | |
if s.before_transition != null: | |
s["before_transition"].call_func() | |
states[current_state].executed = false | |
current_state = s.to_state | |
if s.after_transition != null: | |
s["after_transition"].call_func() | |
# call this method to record the current state | |
func on(state: String): | |
state(state) | |
last_inserted_state = state | |
return self | |
# creates a new state | |
func state(name: String, logic: FuncRef = null, loop: bool = false): | |
if !states.has(name): | |
states[name] = { | |
executed = false, | |
logic = logic, | |
loop = loop | |
} | |
return self | |
# creates a new transition | |
func transition(to_state: String, condition: FuncRef = null, before_transition: FuncRef = null, after_transition: FuncRef = null): | |
state(to_state) | |
if !transitions.has(last_inserted_state): | |
transitions[last_inserted_state] = [] | |
transitions[last_inserted_state].push_back({ | |
to_state = to_state, | |
condition = condition, | |
from_state = last_inserted_state, | |
after_transition = after_transition, | |
before_transition = before_transition | |
}) | |
return self |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment