Godot Version
4.5.1
Question
Hey there, I need your help, please. I am working on a horror game and working on a pursuer enemy known as The Shooter. Has anything in the script been tested out and worked well until I ran into a problem with the AI starting to freak out while in wandering and pursuit mode. I tried to figure it out to fix it by adding a hash symbol to narrow down which one was causing the problem, but ended up nowhere.
I looked into the is_wandering function and tried to understand why it is doing that, but couldn't. It looks right for me, but there's something wrong with it and I can't figure it out.
Here code for the Shooter AI
extends CharacterBody3D
#---------------------------------------------------
@onready var pursuit_player: Area3D = $PlayerDetectionRange/PursuitPlayer
@onready var aiming_at_player: Area3D = $PlayerDetectionRange/AimingAtPlayer
@onready var aim_cast: Node3D = $AimCast
@onready var rng = RandomNumberGenerator.new()
@onready var navigation_agent_3d: NavigationAgent3D = $NavigationAgent3D
@onready var anim_player: AnimationPlayer = $Danny_the_Shooter_anim/AnimationPlayer
@onready var anim_tree: AnimationTree = $Danny_the_Shooter_anim/AnimationTree
#---------------------------------------------------
# wandering
var wander_radius = 10.0
var wander_target_prox = 0.5
var current_wander_target: Vector3
#---------------------------------------------------
# hunt down player
var is_pursuiting_player: bool = false
var is_Aiming_at_player: bool = false
#---------------------------------------------------
#Overall
var Killed : bool
var SPEED = walk_speed
var walk_speed: float = 2.5
var run_speed: float = 4.5
var rotation_speed: float = 2.0
var player_target: CharacterBody3D = null
#---------------------------------------------------
# countdown time to kill player while in sight aim
@onready var kill_raycast: RayCast3D = $AimCast/AimCast3D
@onready var kill_timer: Timer = $Kill_Timer
var can_kill = false
var countdown_time = 5
#---------------------------------------------------
enum State{
wandering,
pursuiting,
targeting,
}
var currentState : State = State.wandering
func _ready() -> void:
#kill_timer.timeout.connect(_on_kill_timer_timeout)
pursuit_player.body_entered.connect(_on_pursuit_player_body_entered)
pursuit_player.body_exited.connect(_on_pursuit_player_body_exited)
#
aiming_at_player.body_entered.connect(_on_aiming_at_player_body_entered)
aiming_at_player.body_exited.connect(_on_aiming_at_player_body_exited)
func _physics_process(delta):
match currentState:
State.wandering:
is_wandering(delta)
State.pursuiting:
is_pursuit(delta)
State.targeting:
is_aiming(delta)
if !is_pursuiting_player and player_target == null:
currentState = State.wandering
if is_pursuiting_player and !is_Aiming_at_player and player_target != null:
currentState = State.pursuiting
if is_Aiming_at_player and !is_pursuiting_player and player_target != null:
currentState = State.targeting
func is_wandering(_delta: float):
if navigation_agent_3d.is_navigation_finished():
set_new_wander_target()
var next_path_position = navigation_agent_3d.get_next_path_position()
var direction = global_transform.origin.direction_to(next_path_position)
SPEED = walk_speed
velocity = direction * SPEED
var look_dir = lerp_angle(deg_to_rad(global_rotation_degrees.y), atan2(velocity.x, velocity.z), rotation_speed)
global_rotation_degrees.y = rad_to_deg(look_dir)
move_and_slide()
aim_cast.visible = false
func set_new_wander_target():
var random_offset = Vector3(randf_range(-wander_radius, wander_radius),
0, randf_range(-wander_radius, wander_radius)
)
current_wander_target = global_transform.origin + random_offset
navigation_agent_3d.target_position = current_wander_target
func is_pursuit(_delta: float):
var player_direction = (player_target.global_transform.origin - global_transform.origin).normalized()
SPEED = walk_speed
velocity = player_direction * SPEED
var look_dir = lerp_angle(deg_to_rad(global_rotation_degrees.y), atan2(velocity.x, velocity.z), rotation_speed)
global_rotation_degrees.y = rad_to_deg(look_dir)
move_and_slide()
aim_cast.visible = false
if is_pursuiting_player and !is_Aiming_at_player and player_target == null:
velocity = Vector3.UP
func is_aiming(delta):
var target_player_direction = (player_target.global_position - global_position).normalized()
var target_player_angle = atan2(target_player_direction.x, target_player_direction.z)
rotation.y = lerp_angle(rotation.y, target_player_angle, rotation_speed * delta)
aim_cast.visible = true
Aiming_at_player()
func Aiming_at_player():
pass
# if player_target:
# kill_raycast.target_position = kill_raycast.to_local(player_target.global_transform.origin)
# kill_raycast.force_raycast_update()
# if kill_raycast.is_colliding():
# var collider = kill_raycast.get_collider()
# if collider == player_target:
# if !can_kill:
# print("Player sighted. Starting countdown...")
# can_kill = true
# kill_timer.start()
# else:
# reset_kill_timer()
# else:
# reset_kill_timer()
#func _on_kill_timer_timeout() -> void:
# if can_kill:
# if countdown_time > 0:
# print("Counting down in ", countdown_time)
# countdown_time -= 1
# kill_timer.start()
# if countdown_time == 0:
# print("player dead")
# kill_timer.stop()
#func reset_kill_timer():
# if can_kill:
# print("Player lost. Countdown reset.")
# can_kill = false
# kill_timer.stop()
func _on_pursuit_player_body_entered(body: CharacterBody3D) -> void:
if body.is_in_group("player"):
player_target = body
print("Enemy detected player!")
currentState = State.pursuiting
func _on_pursuit_player_body_exited(_body: CharacterBody3D) -> void:
player_target = null
print("Enemy lost player!")
currentState = State.wandering
func _on_aiming_at_player_body_entered(body: CharacterBody3D) -> void:
if body.is_in_group("player"):
player_target = body
print("Player is in range!")
currentState = State.targeting
func _on_aiming_at_player_body_exited(body: CharacterBody3D) -> void:
if body.is_in_group("player"):
player_target = body
print("Player is out of range!")
#reset_kill_timer()
currentState = State.pursuiting