Improving NPC behavior

simple change of condition logic made a bit more challenging it for player

func _physics_process(delta: float) -> void:
	if not is_on_floor():
		velocity.y -= gravity * delta

	if player_is_visible():
		navigation_agent_3d.target_position = player.global_position
		var next_position = navigation_agent_3d.get_next_path_position()
		var direction = global_position.direction_to(next_position)
		look_at_target(direction)
		velocity.x = direction.x * SPEED
		velocity.z = direction.z * SPEED
	else:
		var next_position = navigation_agent_3d.get_next_path_position()
		var direction = global_position.direction_to(next_position)
		look_at_target(direction)
		velocity.x = direction.x * SPEED
		velocity.z = direction.z * SPEED

	move_and_slide()

ok this could be further improved as I figure out the physic frame on first frame haven’t collided with wall

extends CharacterBody3D
@onready var navigation_agent_3d: NavigationAgent3D = $NavigationAgent3D
var gravity: float = 9.8
const SPEED = 5.0
var player
var game_ready = false
func _ready() -> void:
	player = get_tree().get_first_node_in_group("Player")
	await get_tree().physics_frame
	await get_tree().physics_frame
	game_ready = true


func _physics_process(delta: float) -> void:
	if game_ready == true:
		if not is_on_floor():
			velocity.y -= gravity * delta
		
		if player_is_visible():
			navigation_agent_3d.target_position = player.global_position
			var next_position = navigation_agent_3d.get_next_path_position()
			var direction = global_position.direction_to(next_position)
			look_at_target(direction)
			velocity.x = direction.x * SPEED
			velocity.z = direction.z * SPEED
		else:
			var next_position = navigation_agent_3d.get_next_path_position()
			var direction = global_position.direction_to(next_position)
			look_at_target(direction)
			velocity.x = direction.x * SPEED
			velocity.z = direction.z * SPEED

		move_and_slide()
	else:
		pass

# Main Logic with raycast from NPC to Player (no offsets for now)
func player_is_visible() -> bool:
	if player == null:
		return false

	var space_state = get_world_3d().direct_space_state
	var from = global_position 
	var to = player.global_position 
	var query = PhysicsRayQueryParameters3D.create(from, to)
	query.exclude = [self]
	var result = space_state.intersect_ray(query)
	if result.is_empty():
		return true
	else:
		return result.collider == player

func look_at_target(direction: Vector3) -> void:
	var adjusted_direction = direction
	adjusted_direction.y = 0
	if adjusted_direction.length_squared() < 0.0001:
		return
	look_at(global_position + adjusted_direction, Vector3.UP, true)