Enemy wont stop chasing the player

Godot Version

4.3

Question

Hello! i watched a tutorial on how to code a state machine and i swear i did everything right but when i run it the enemy won't stop chasing the player when it should go back to its wandering state

extends State
class_name Wander

var Sentry_dir: Vector3
var Sentry_time: float = 0.0

@onready var ent_chaser: CharacterBody3D = get_parent().get_parent()
var player: CharacterBody3D = null
func randomize_variables():
	Sentry_dir = Vector3(randf_range(-1.0, 1.0), 0.0, randf_range(-1.0, 1.0))
	Sentry_time = randf_range(5.0, 10.0)
	
func _ready() -> void:
	player = get_tree().get_first_node_in_group("Player")
	
func enter():
	randomize_variables()
	
func _process(delta: float) -> void:
	if Sentry_time < 0.0:
		randomize_variables()
		
	Sentry_time -= delta

	if ent_chaser.global_position.distance_to(player.global_position) < ent_chaser.Chase_Distance:
		emit_signal("Transitioned", self, "Chase")
		Global.chase == true
		Global.idle == false
		print("chase")
	
func _physics_process(delta: float) -> void:
	ent_chaser.velocity = Sentry_dir * ent_chaser.WalkSpeed
	
	if not ent_chaser.is_on_floor():
		ent_chaser.velocity += ent_chaser.get_gravity()

extends State
class_name Chase

@onready var ent_chaser: CharacterBody3D = get_parent().get_parent()
var player: CharacterBody3D = null

@onready var animation_tree: AnimationTree = $"../../AnimationTree"

# Called when the node enters the scene tree for the first time.
func _ready() -> void:
	player = get_tree().get_first_node_in_group("Player")
	
func _process(delta: float):
	ent_chaser.nav_agent.set_target_position(player.global_position)
	
	if ent_chaser.global_position.distance_to(player.global_position) > ent_chaser.Chase_Distance:
		emit_signal("Transitioned", self, "Wander")
		Global.chase == false
		Global.idle == true
		print("idle")

func _physics_process(delta: float):
	if ent_chaser.nav_agent.is_navigation_finished():
		return
	
	var next_positon: Vector3 = ent_chaser.nav_agent.get_next_path_position()
	ent_chaser.velocity = ent_chaser.global_position.direction_to(next_positon) * ent_chaser.RunSpeed

Hi!

Seems strange to me that you wrote:

Global.chase == false
Global.idle == true

You’re comparing booleans where it seems you should be assigning them, like:

Global.chase = false
Global.idle = true

with a single = sign.

If that’s not what’s causing the issue, could you give more context: any error/warning in console? Is the “idle” print showing up?

3 Likes

Good eye.

Godot gives a warning. (STANDALONE_EXPRESSION):Standalone expression (the line may have no effect).

off-topic: I am wondering if there is ever a reason why this should not be flagged as an error.

Yup, because it’s a technically valid code, it’s just very weird to look at in that context.
Imagine a function that looks like this:

func try_kill_enemy() -> bool:
    var enemy_killed: bool = false
    # Do something
    return enemy_killed

The function tries to kill an enemy - as its name suggests - and returns true or false depending on whether the enemy got successfully killed or not.
You can now call the function like this:

var enemy_killed: bool = try_kill_enemy()
if enemy_killed:
    # Do something

But, you may also want to just try to kill an enemy, regardless of the function outcome, like this:

try_kill_enemy()

And that would be valid code, although it returns a boolean that just loses itself into the void. An expression such as Global.idle == true is the same thing (as it returns a boolean), just much more weird to read from a human perspective.

Hello! sorry for not getting back to you sooner but i realized i probably should also post the state machine itself

extends Node
class_name StateMachine

@export var InitialState: State

var current_state: State = null
var states: Dictionary = {}

func _ready() -> void:
	for child in get_children():
		if child is State:
			states[child.name.to_lower()] = child
			child.Transitioned.connect(on_child_transitioned)
			
	if InitialState:
		current_state = InitialState
		InitialState.enter()


func _process(delta: float):
	if current_state:
		current_state._process(delta)
		
func _physics_process(delta: float):
	if current_state:
		current_state._physics_process(delta)

func on_child_transitioned(state, new_state_name):
	if state != current_state:
		return
		
	var new_state = states[new_state_name.to_lower()]
	
	if current_state:
		current_state.exit()
		
	new_state.enter()
	current_state = new_state

The on_child_transitioned function seems okay to me.

What you can do is add lots of print() here and there, so that you can have a clear understanding of what your code is doing. Typically, doing something like this is very useful:

func on_child_transitioned(state, new_state_name):
    if state != current_state:
        return

    print('Trying to transition to state %s' % [new_state_name])

That would give you the info that the transition is occuring, and to which state. If there’s no print, that means there’s no transition, and in that case, you can just add prints earlier in the code, and do that until you find the issue.

Also, have you read my first answer and made sure it was not related to your bug?

1). okay I’ll try that
2). yes, it wasn’t the main bug but you did help me with the music player again!!!

1 Like

ive noticed this specific line has a yellow flag on it but wont give me any context

emit_signal("Transitioned", self, "Sentry")

Well, I don’t know what it can be, but I’ve never seen this syntax for emitting signals. Is it a method in your State class?

heres the state class

extends Node
class_name State
signal Transitioned(state: State, new_state_name: String)

# Called when the node enters the scene tree for the first time.
func enter():
	pass

func exit():
	pass
	
func _process(_delta: float) -> void:
	pass
	
func _physics_process(_delta: float) -> void:
	pass