Skip to content

Instantly share code, notes, and snippets.

@firebelley
Created August 27, 2024 21:18
Show Gist options
  • Select an option

  • Save firebelley/96f2f82e3feaa2756fe647d8b9843174 to your computer and use it in GitHub Desktop.

Select an option

Save firebelley/96f2f82e3feaa2756fe647d8b9843174 to your computer and use it in GitHub Desktop.
class_name CallableStateMachine
var state_dictionary = {}
var current_state: String
func add_states(
normal_state_callable: Callable,
enter_state_callable: Callable,
leave_state_callable: Callable
):
state_dictionary[normal_state_callable.get_method()] = {
"normal": normal_state_callable,
"enter": enter_state_callable,
"leave": leave_state_callable
}
func set_initial_state(state_callable: Callable):
var state_name = state_callable.get_method()
if state_dictionary.has(state_name):
_set_state(state_name)
else:
push_warning("No state with name " + state_name)
func update():
if current_state != null:
(state_dictionary[current_state].normal as Callable).call()
func change_state(state_callable: Callable):
var state_name = state_callable.get_method()
if state_dictionary.has(state_name):
_set_state.call_deferred(state_name)
else:
push_warning("No state with name " + state_name)
func _set_state(state_name: String):
if current_state:
var leave_callable = state_dictionary[current_state].leave as Callable
if !leave_callable.is_null():
leave_callable.call()
current_state = state_name
var enter_callable = state_dictionary[current_state].enter as Callable
if !enter_callable.is_null():
enter_callable.call()
@Chum4k3r
Copy link

@mbnewsom All your code can be in a single script. I've updated it below, also fixed the typo in the State._init declaration.

I'll probably update this code further once I test it with the new variadic arguments, so we will be allowed to declare this as a single external Node, extend its script to declare the states and give arguments to .update() and .state_change(), allowing transient variables, like delta and controller inputs, being passed into, instead of holding global references, to access these values inside the state functions.

# callable_state_machine.gd
class_name CallableStateMachine


class_name State

    var normal: Callable
    var enter: Callable
    var leave: Callable

    func _init(normal: Callable, enter: Callable, leave: Callable):
        self.normal = normal
        self.enter = enter
        self.leave = leave


var state_dictionary: Dictionary[String, State] = {}
var current_state: String

func add_state(
    normal_state_callable: Callable,
    enter_state_callable: Callable,
    leave_state_callable: Callable
):
    state_dictionary[normal_state_callable.get_method()] = \
        State.new(normal_state_callable, enter_state_callable, leave_state_callable)

func set_initial_state(state_callable: Callable):
    var state_name = state_callable.get_method()
    if state_dictionary.has(state_name):
        _set_state(state_name)
    else:
        push_warning("No state with name " + state_name)


func update():
    if current_state != "":  #null here was a bug. Strings default to "", not null
        state_dictionary[current_state].normal.call()


func change_state(state_callable: Callable):
    var state_name = state_callable.get_method()
    if state_dictionary.has(state_name):
        _set_state.call_deferred(state_name)
    else:
        push_warning("No state with name " + state_name)


func _set_state(state_name: String):
    if current_state:
        var leave_callable = state_dictionary[current_state].leave
        if leave_callable.is_valid():
	    leave_callable.call()
	
    current_state = state_name
    var enter_callable = state_dictionary[current_state].enter
    if enter_callable.is_valid():
        enter_callable.call()

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment