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
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?
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