Godot Version
Godot v.4.3 Stable
Question
I made the doors using NavigationLink3D to fix the bugs where the enemy gets stuck in the doors. The problem is that when the enemy is chasing the player, it can’t pass through the doors and gets stuck. But when it’s patrolling, it works fine. The enemy can’t collide with the doors, so it’s not a matter of getting stuck in them. I think the issue lies in the chasing state, but I’m not sure what the exact bug is. I’ll attach the enemy full code below and chase func only. Btw NavigationLink3D is always enabled and never get disabled
Full script
extends CharacterBody3D
#---------------------------------STATE MACHINE--------------------------------#
enum {
IDLE,
CHASE,
CHASE_LAST,
PATROL,
KILL
}
var state = IDLE
var current_state = IDLE
#------------------------------------------------------------------------------#
@onready var navigation_agent: NavigationAgent3D = $NavigationAgent3D
@onready var animation_player: AnimationPlayer = $AnimationPlayer
@onready var RC: RayCast3D = $RayCast3D
@export var patrol_markers: Node3D
@export var player: CharacterBody3D
var is_waiting_for_patrol: bool = false
var player_heart_beat: bool = false
var is_patrolling: bool = true
var map_ready: bool = false
var can_kill: bool = false
var is_see: bool = false
var check: bool = false
var kill: bool = false
var last_pos: Vector3
var patrol_pos
var body = null
# Параметры движения
var movement_speed: float = 3.0
var agr = false
var rotation_speed: float = 9.0
# Для плавного поворота
var target_direction: Vector3 = Vector3.FORWARD
var current_rotation_velocity: float = 0.0
func _ready() -> void:
await get_tree().process_frame
map_ready = true
func _physics_process(delta):
#--------------------------Гравитация--------------------------------------#
if not is_on_floor():
velocity += get_gravity() * delta
#-----------------------------Вызов функций--------------------------------#
anim()
update_target_position()
chase(delta)
#--------------------------------------------------------------------------#
func can_see(): # проверка наличия игрока в поле зрения
var direction = global_position.direction_to(body.global_position)
var facing = global_transform.basis.tdotz(direction)
var fov = cos(deg_to_rad(100))
if facing > fov:
is_see = true
else:
is_see = false
func rc(player_position: Vector3): # функция рейкаста
var enemy_position = global_transform.origin
RC.global_transform.origin = enemy_position
RC.target_position = to_local(player_position)
RC.force_raycast_update()
var collider = RC.get_collider()
return collider and collider.is_in_group("player") and is_see and check
func _on_timer_timeout() -> void: # перезарядка рейкаста
if check:
can_see()
if rc(player.position):
RC.debug_shape_custom_color = Color(0, 200, 0)
state = CHASE
current_state = CHASE
if can_kill:
get_tree().change_scene_to_file("res://Scenes/death_screen.tscn")
else:
RC.debug_shape_custom_color = Color(200, 0, 0)
if current_state == CHASE:
last_pos = player.global_position
await get_tree().create_timer(2, false).timeout
state = CHASE_LAST
current_state = CHASE_LAST
else:
if velocity == Vector3.ZERO and !is_waiting_for_patrol:
state = PATROL
if state == PATROL and velocity == Vector3.ZERO and !is_waiting_for_patrol:
is_waiting_for_patrol = true
state = IDLE
if !agr:
await get_tree().create_timer(5).timeout
is_waiting_for_patrol = false
if state == IDLE:
is_patrolling = true
state = PATROL
func chase(delta):
if not map_ready:
return
if navigation_agent.is_navigation_finished():
velocity = Vector3.ZERO
return
var current_position: Vector3 = global_position
var next_position: Vector3 = navigation_agent.get_next_path_position()
var direction = current_position.direction_to(next_position)
var horizontal_direction = Vector3(direction.x, 0, direction.z).normalized()
var target_angle = atan2(horizontal_direction.x, horizontal_direction.z)
rotation.y = lerp_angle(rotation.y, target_angle, delta * rotation_speed)
velocity = direction * movement_speed
move_and_slide()
func anim():
if !kill:
match state:
IDLE:
animation_player.play("idle")
player_heart_beat = false
CHASE:
animation_player.play("run")
player_heart_beat = true
CHASE_LAST:
animation_player.play("run")
PATROL:
if agr:
animation_player.play("run_2")
else:
animation_player.play("walk")
player_heart_beat = false
func update_target_position():
match state:
CHASE:
navigation_agent.target_position = player.global_position
movement_speed = 4.8
if agr:
movement_speed = 5.2
CHASE_LAST:
navigation_agent.target_position = last_pos
PATROL:
if is_patrolling:
is_patrolling = false
if agr:
movement_speed = 5.2
else:
movement_speed = 3.0
patrol_pos = get_random_patrol_pos(patrol_markers)
navigation_agent.target_position = patrol_pos.position
func get_random_patrol_pos(parent: Node3D) -> Node:
var child_count = parent.get_child_count()
if child_count == 0:
return null
var random_index = randi() % child_count
return parent.get_child(random_index)
func _kill():
kill = true
state = KILL
animation_player.play("kill")
velocity = Vector3.ZERO
await animation_player.animation_finished
func _on_ray_area_body_entered(_body: Node3D) -> void:
if _body.name == "Player":
check = true
body = _body
func _on_ray_area_body_exited(_body: Node3D) -> void:
if _body.name == "Player":
check = false
body = null
func _on_area_3d_body_entered(_body: Node3D) -> void:
if _body.name == "Player":
can_kill = true
func _on_area_3d_body_exited(_body: Node3D) -> void:
if _body.name == "Player":
can_kill = false
Chase func only
func chase(delta):
if not map_ready:
return
if navigation_agent.is_navigation_finished():
velocity = Vector3.ZERO
return
var current_position: Vector3 = global_position
var next_position: Vector3 = navigation_agent.get_next_path_position()
var direction = current_position.direction_to(next_position)
var horizontal_direction = Vector3(direction.x, 0, direction.z).normalized()
var target_angle = atan2(horizontal_direction.x, horizontal_direction.z)
rotation.y = lerp_angle(rotation.y, target_angle, delta * rotation_speed)
velocity = direction * movement_speed
move_and_slide()
func update_target_position():
match state:
CHASE:
navigation_agent.target_position = player.global_position
movement_speed = 4.8
if agr:
movement_speed = 5.2
CHASE_LAST:
navigation_agent.target_position = last_pos
PATROL:
if is_patrolling:
is_patrolling = false
if agr:
movement_speed = 5.2
else:
movement_speed = 3.0
patrol_pos = get_random_patrol_pos(patrol_markers)
navigation_agent.target_position = patrol_pos.position