Why the enemy isn't moving?

Godot Version

godot 4.3

Question

the enemy’s code

extends CharacterBody2D
class_name enemy

enum {patrol, chasing}

@export var patrol_node : Path2D

@onready var patrol_path = patrol_node

func _physics_process(delta: float) -> void:
	move_and_slide()

the patrol state code

extends State
class_name EnemyIdle

@export var wall_detection : RayCast2D
@export var enemy: CharacterBody2D
@export var animation : AnimationPlayer
@export var speed := 5.0
@export var wander_range = 1
@export var margin = 1

@onready var move_direction : Vector2
@onready var wander_time : float
@onready var patr = get_parent().get_parent()
@onready var pat = patr.patrol_path

var direction = Vector2()
var patrol_points
var patrol_index = 0

func randomize_wander() -> void:
	if pat:
		patrol_points = get_node(pat).curve.get_baked_points()

func enter():
	randomize_wander()

func update(_delta: float):
	if wander_time > 0:
		wander_time -= _delta
	else:
		randomize_wander()

func physics_update(_delta: float):
	if wall_detection.is_colliding() and margin <= 1 and margin >= 0:
		randomize_wander()
	if !pat:
		return
	var target = patrol_points[patrol_index]
	if enemy.position.distance_to(target) < 1:
		patrol_index = wrapi(patrol_index + 1, 0, patrol_points.size())
		target = patrol_points[patrol_index]
	enemy.velocity = (target - enemy.position).normalized() * speed
	if enemy.position.x > 0:
		animation.play("rightRotate")
	if enemy.position.x < 0:
		animation.play("leftRotate")
	if enemy.position.y < 0:
		animation.play("upRotate")
	if enemy.position.y > 0:
		animation.play("downRotate")


func _on_player_detection_body_entered(_body: Node2D) -> void:
	pass

Are you sure that “pat” has a value?

yes it supposed to be

None of the patrol state functions are being called.

then what should I do to call them ?

extends CharacterBody2D
class_name enemy

enum {patrol, chasing}

@export var patrol_node : Path2D

@onready var patrol_path = patrol_node

func _physics_process(delta: float) -> void:
	patrol_node.physics_process(delta) # add call to a patrol function
	move_and_slide()

Usually there is a state machine node between the states and the entity.

well, there is a state machine I made for the states

I tried to used this but I got error

You will need to post the error, I have no idea what error you recieved otherwise.

What does the state machine look like?

it says non exist function in Path2D

Ah I don’t know why I thought patrol_node would be your state machine. Find your state machine and call one of it’s functions.

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.

I didn’t get it, please explain more

@luismatricardi is correct, pat does not have a value.

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.

extends CharacterBody2D
class_name enemy

enum {patrol, chasing}

@export var patrol_node : Path2D

func _ready() -> void: #
     $StateMachine/StateIdle.pat = patrol_node #

func _physics_process(delta: float) -> void:
	move_and_slide()

it doesn’t work, the enemy still static in his location

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.


What did you do that “doesn’t work”?

the error is from pat being null