I already made a state machine that and this is its code
extends Node
class_name StateMachine
@export var initial_state : State
var current_state : State
var states = {}
func _ready() -> void:
for child in get_children():
if child is State:
states[child.name.to_lower()] = child
child.Transitioned.connect(on_child_transition)
if initial_state:
initial_state.enter()
current_state = initial_state
func _process(_delta: float) -> void:
if current_state:
current_state.update(_delta)
func _physics_process(_delta: float) -> void:
if current_state:
current_state.physics_update(_delta)
func on_child_transition(_state, _new_state_name):
if _state != current_state:
return
var new_state = states.get(_new_state_name.to_lower())
if !new_state:
return
if current_state:
current_state.exit()
Ah; your pat value will be nill, parents are only ready when all of their children are ready, so you cannot get a @onready value from a parent or grand-parent.
The Idle State will be “readied” first so it copies the parent.parent.patrol_path which isn’t ready yet and is null. Then the state machine is readied, and finally the enemy, this is where you could set the patrol path instead.
Godot readies children before parents.
Try assigning this value from the enemy script instead.
Instead of these two lines try asserting for a louder error.
assert(pat, "patrol node is null, need to set pat")
I notice your randomize_wander also assumes pat is a NodePath? Since it’s using get_node(pat) if you don’t get an error on this line then pat must be null.