Created
January 5, 2024 19:34
-
-
Save N-Carter/64b3f20b80eba78d3d34c2713efd3bcf to your computer and use it in GitHub Desktop.
A simple state machine
This file contains hidden or 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
@icon("res://editor/icons/state_machine.svg") | |
class_name StateMachine | |
extends Node | |
@export var _host_path : NodePath # If unset, the host is self | |
var _host : Node | |
enum Mode { | |
MANUAL, | |
PROCESS, | |
PHYSICS_PROCESS | |
} | |
@export var _mode : Mode = Mode.MANUAL | |
@export var _is_logging := false | |
var _current_state := _no_op # Use set_state() to choose the first state before advancing | |
var _is_new_state := 0 # If greater than 0, this is still a new state | |
var _state_time : float # How many seconds this state has been current | |
var _delta : float # Records whatever delta time interval applies for this step | |
var _exit_function := _no_op # If not "", called when set_state() changes state | |
var _logging_name : String | |
func _ready() -> void: | |
# FIXME: Now we're using Callable, don't really need _host except for displaying the right name while logging. | |
_host = get_node_or_null(_host_path) | |
if not is_instance_valid(_host): | |
_host = self | |
_logging_name = owner.name if owner else _host.name | |
# You automatically get process notifications if a class in the hierarchy implements the relevant | |
# function, but if that's not the case, you have to manually activate them: | |
match _mode: | |
Mode.PROCESS: | |
set_process(true) | |
Mode.PHYSICS_PROCESS: | |
set_physics_process(true) | |
func _notification(what: int) -> void: | |
match what: | |
NOTIFICATION_PROCESS: | |
if _mode == Mode.PROCESS: | |
# print("Advancing %s in process mode" % get_path()) | |
advance(get_process_delta_time()) | |
NOTIFICATION_PHYSICS_PROCESS: | |
if _mode == Mode.PHYSICS_PROCESS: | |
# print("Advancing %s in physics process mode" % get_path()) | |
advance(get_physics_process_delta_time()) | |
func advance(delta : float) -> void: | |
_delta = delta | |
_call_state() | |
_state_time += delta | |
func set_state(new_state : Callable) -> void: | |
if _current_state != new_state: | |
_current_state = new_state | |
_is_new_state = 2 | |
_state_time = 0.0 | |
if _exit_function != _no_op: | |
_exit_function.call() | |
_exit_function = _no_op | |
log_message("New state: %s", _current_state) | |
else: | |
log_message("Tried to switch to this state while already in it: %s", new_state.get_method()) | |
func is_state(state : Callable) -> bool: | |
return _current_state == state | |
func get_state_name() -> String: | |
return _current_state.get_method() | |
func _call_state() -> void: | |
_current_state.call() | |
if _is_new_state > 0: | |
_is_new_state -= 1 | |
# Used instead of null: | |
func _no_op() -> void: | |
pass | |
func log_message(format : String, substitutions = null) -> void: | |
if _is_logging: | |
if substitutions: | |
print("[%s] %s" % [_logging_name, format % substitutions]) | |
else: | |
print("[%s] %s" % [_logging_name, format]) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment