From 0c62d0c0eddfc993628169e3f7e708bc0b461949 Mon Sep 17 00:00:00 2001 From: yuki Date: Sat, 22 Nov 2025 10:44:59 -0300 Subject: [PATCH] add basic state machine --- classes/state_machine/state_machine.gd | 57 ++++++++++++++++++++++ classes/state_machine/state_machine.gd.uid | 1 + 2 files changed, 58 insertions(+) create mode 100644 classes/state_machine/state_machine.gd create mode 100644 classes/state_machine/state_machine.gd.uid diff --git a/classes/state_machine/state_machine.gd b/classes/state_machine/state_machine.gd new file mode 100644 index 0000000..694d19e --- /dev/null +++ b/classes/state_machine/state_machine.gd @@ -0,0 +1,57 @@ +## Virtual base class for all nodes that deal directly with states. +## Extend this class and override its methods to implement a state machine. +class_name StateMachine extends Node + + +## The initial state of the state machine. If not set, the first child node is used. +@export var initial_state: State + +## The state machine's current loaded state +@onready var state: State = _get_initial_state() + + +func _ready() -> void: + assert(state != null, "initial state is null") + for state_node: State in find_children("*", "State"): + # fixes duplicate connections (not sure why) + if not state_node.finished.is_connected(_transition_to_next_state): + state_node.finished.connect(_transition_to_next_state) + await owner.ready + state.enter("") + + +func _unhandled_input(event: InputEvent) -> void: + state.handle_input(event) + + +func _process(delta: float) -> void: + state.state_update(delta) + + +func _physics_process(delta: float) -> void: + state.state_physics_update(delta) + + +## Called when initial state is not specified. +## Returns the first child node by default. +## Override if necessary, but don't call it directly. +func _get_initial_state() -> State: + return initial_state if initial_state != null else get_child(0) + + +## Transitions the active state out after receiving a finished signal. +func _transition_to_next_state(target_state_path: String, data: Dictionary[StringName, Variant] = {}) -> void: + print("+++ TRANSITION CALLED: ", target_state_path) + print("+++ has node? ", has_node(target_state_path)) + print("+++ all children: ", get_children().map(func(c: Node) -> StringName: return c.name)) + + assert(has_node(target_state_path), owner.name + ": Trying to transition to state " + target_state_path + " but it does not exist.") + + if not has_node(target_state_path): + printerr(owner.name + ": Trying to transition to state " + target_state_path + " but it does not exist.") + return + + var previous_state_path: StringName = state.name + state.exit() + state = get_node(target_state_path) + state.enter(previous_state_path, data) diff --git a/classes/state_machine/state_machine.gd.uid b/classes/state_machine/state_machine.gd.uid new file mode 100644 index 0000000..6fb82f1 --- /dev/null +++ b/classes/state_machine/state_machine.gd.uid @@ -0,0 +1 @@ +uid://diths5s8vd7lr