Help with idle animation not playing

Godot Version

v4.2.2.stable

Question

Hi,

I need help with my enemy script not playing my idle animation. I have created a script that allows my enemy to patrol and chase. In the patrolling state the enemy choses a random point from a list and then moves to the chosen point. When moving it plays the walking animation. When reaching the chosen point it is supposed to stop for a few seconds until choosing another random point to move to.

The thing is it is supposed to play an idle animation once the enemy has reached the point, but instead it continues to play the walking animation and does not play the idle animation.

Here is the script:

extends CharacterBody3D

@onready var move_timer = get_node("move_timer")
var is_paused = false 

@onready var get_nav_map = get_parent().get_node("NavigationRegion3D")
@onready var nav_agent = get_node("NavigationAgent3D")
@onready var FOV_caster = get_node("FOV_cast")

@onready var speed = 60.0
@onready var pursuit_speed = 200.0
var all_points = []
var next_point = 0

var in_pursuit = false
var is_at_post = false
var seen_player = false
@export var neg_pos = 1
@export var is_patrolling_guard = false

func _ready():
	
	add_to_group("enemy")
	
	for x in get_parent().get_node("all_patrolling_points_Guard1").get_children():
		all_points.append(x.global_position + Vector3(0,1,0))

func _physics_process(delta):
	if Input.is_action_just_pressed("ui_down"):
		in_pursuit = false
	if is_patrolling_guard or in_pursuit:
		check_alert_state(delta)
	if not is_patrolling_guard and not in_pursuit:
		back_to_post(delta)
	move_and_slide()

func get_new_target(new_target):
	nav_agent.set_target_position(new_target)

func check_alert_state(delta):
	if not is_paused:
		if not in_pursuit:
			patrolling(delta)
		if in_pursuit:
			pursuit_state(delta)

func patrolling(delta):
	if !is_paused:
		await get_tree().process_frame
		var dir_dir
		nav_agent.target_position = all_points[next_point]
		dir_dir = nav_agent.get_next_path_position() - global_position
		dir_dir = dir_dir.normalized()
		velocity = velocity.lerp(dir_dir * speed * delta,1.0)
		dir_dir.y = 0
		if dir_dir.length() > 0.01:
			look_at(global_transform.origin - dir_dir)
		#look_at(global_transform.origin - dir_dir)
		$demon/AnimationPlayer.play("walking")
		$demon/AnimationPlayer.speed_scale = 2.0

func pursuit_state(delta):
	var enemy_current_map_pos = global_position
	var current_target = nav_agent.get_next_path_position()
	var change_dir = (current_target - enemy_current_map_pos).normalized()
	velocity = change_dir * pursuit_speed * delta
	#look_at(Vector3(Global.player_current_pos.x,self.global_transform.origin.y,Global.player_current_pos.z),Vector3(0,1,0))
	var look_dir = Vector3(Global.player_current_pos.x, self.global_transform.origin.y, Global.player_current_pos.z) - global_transform.origin
	look_dir = look_dir.normalized()
	look_at(global_transform.origin - look_dir, Vector3(0,1,0))
	$demon/AnimationPlayer.play("running crawl")
	$demon/AnimationPlayer.speed_scale = 1.5

func back_to_post(delta):
	if not is_at_post:
		await get_tree().process_frame
		var dir_dir
		nav_agent.target_position = all_points[0]
		dir_dir = nav_agent.get_next_path_position() - global_position
		dir_dir = dir_dir.normalized()
		velocity = velocity.lerp(dir_dir * speed * delta,1.0)
		dir_dir.y = 0
		look_at(global_transform.origin + dir_dir)
	if is_at_post:
		direction_transition()

func direction_transition():
	var default_rot = get_parent().get_node("reset_guard_rotation/posted_guard1_look_at").global_position
	var reset_rotation = Basis.looking_at(default_rot * neg_pos)
	var current_rotation = basis.get_rotation_quaternion()
	basis = current_rotation.slerp(reset_rotation, 0.1)
	velocity = Vector3.ZERO

func _on_navigation_agent_3d_target_reached():
	if is_patrolling_guard and not in_pursuit:
		$demon/AnimationPlayer.play("idle looking")
		$demon/AnimationPlayer.speed_scale = 1.0
		is_paused = true
		move_timer.start()
		next_point = randi() % all_points.size()
		nav_agent.set_target_position(all_points[next_point])
		#next_point += 1
		#if next_point >= all_points.size():
			#next_point = all_points[-1]
			#next_point = 0
	if not is_patrolling_guard and not in_pursuit:
		is_at_post = true

func _on_timer_timeout():
	check_sight()
	get_new_target(Global.player_current_pos)

func check_sight():
	if seen_player:
		var fov_direction = (Global.player_current_pos - FOV_caster.global_transform.origin).normalized()
		FOV_caster.look_at(FOV_caster.global_transform.origin - fov_direction, Vector3(0,1,0))
		#FOV_caster.look_at(Global.player_current_pos, Vector3(0,1,0))
	if FOV_caster.is_colliding():
		var collider = FOV_caster.get_collider()
		if collider.is_in_group("player"):
			in_pursuit = true
			is_at_post = false

func _on_enemy_fov_body_entered(body):
	if body.is_in_group("player"):
		seen_player = true

func _on_enemy_fov_body_exited(body):
	if body.is_in_group("player"):
		seen_player = false
		in_pursuit = false
		is_at_post = false
		is_patrolling_guard = true

func _on_move_timer_timeout():
	if is_paused:
		next_point = randi() % all_points.size()
		nav_agent.set_target_position(all_points[next_point])
		is_paused = false
		$demon/AnimationPlayer.play("walking")
		$demon/AnimationPlayer.speed_scale = 2.0

I apologize for the long script as I can’t paste snippets of the code, everything works together. Any help would be appreciated!

Have you checked if the if-condition is ever true when the target-position is reached? Add a print inside the if-statement to check if it gets called at all:

if is_patrolling_guard and not in_pursuit:
		print("Playing Idle animation")
		$demon/AnimationPlayer.play("idle looking")

It definitely prints “Playing Idle animation” once the enemy has reach the chosen point, but it just continues to play the walking animation and not the idle animation

this might be due the “await get_tree().process.frame” in back_to_post. It probably plays the idle animation and then the await is done and calls the walking animation again

I tried to remove it but it did not work. it still plays the walking animation instead of the idle animation when stopped at the chosen point

Put some print statements in your methods to check which method is getting called that overwrites your idle animation

I managed to fix it! I just added another if statement in the patrolling function:

func patrolling(delta):
	if !is_paused:
		await get_tree().process_frame
		var dir_dir
		nav_agent.target_position = all_points[next_point]
		dir_dir = nav_agent.get_next_path_position() - global_position
		dir_dir = dir_dir.normalized()
		if dir_dir.length() < 0.1:
			velocity = Vector3.ZERO
			$demon/AnimationPlayer.play("idle looking")
			$demon/AnimationPlayer.speed_scale = 1.0
		else:
			velocity = velocity.lerp(dir_dir * speed * delta,1.0)
			dir_dir.y = 0
			if dir_dir.length() > 0.01:
				var target_rotation = Basis(Vector3(0, 1, 0), atan2(dir_dir.x, dir_dir.z))
				global_transform.basis = global_transform.basis.slerp(target_rotation, 0.1)
				#look_at(global_transform.origin - dir_dir)
			#look_at(global_transform.origin - dir_dir)
			$demon/AnimationPlayer.play("walking")
			$demon/AnimationPlayer.speed_scale = 2.0

Thank you for your help!

1 Like