godot-wild-jam-66/npc/Employee.gd

96 lines
3.1 KiB
GDScript

extends CharacterBody3D
const RAYCAST_VIS_SCN: PackedScene = preload("res://npc/RaycastVis.tscn")
const MOTION_SPEED_DEADZONE: float = 0.05
@export var vision_sensor: Node3D
@export_group("Detection", "detection_")
@export var detection_normal_profile: DetectionProfile
@export var detection_chase_profile: DetectionProfile
@export var detection_ray_length: float = 15.0
@export_range(0.0, 45.0, 0.5, "radians_as_degrees") var detection_ray_cone_angle: float = TAU / 32.0
@export_range(0, 4, 1) var detection_ray_cone_angle_steps: int = 3
@onready var num_rays: int = 1 + (detection_ray_cone_angle_steps * 6)
var raycast_vis: Array[RaycastVis]
var prop: Prop = null
var ray_idx: int = 0
var line_of_sight: bool = false
var los_last_ray_idx: int = 0
var los_distance: float = 0.0
var detection_profile: DetectionProfile
var detected: float = 0.0
var prop_last_pos: = Vector3.ZERO
func _ready() -> void:
Harbinger.subscribe("active_prop", func(p): prop = p[0])
for i in range(num_rays):
var vis: = RAYCAST_VIS_SCN.instantiate()
vision_sensor.add_child(vis)
raycast_vis.append(vis)
detection_profile = detection_normal_profile
func _process(delta: float) -> void:
DebugOverlay.display({ los = line_of_sight, los_distance = los_distance, detected = detected }, self)
if Input.is_action_just_pressed("debug_draw_raycast"):
for vis in raycast_vis:
vis.visible = !vis.visible
func _physics_process(delta: float) -> void:
if !prop:
detected = 0.0
return
#region perception
var dir_to_prop: = (prop.global_position - vision_sensor.global_position).normalized()
var dir: = dir_to_prop
var cone_angle_axis: = dir.cross(dir.rotated(Vector3.UP, 0.5)).normalized()
dir = dir.rotated(
cone_angle_axis,
((ray_idx + 5) / 6) * (detection_ray_cone_angle / detection_ray_cone_angle_steps)
)
dir = dir.rotated(
dir_to_prop,
((ray_idx + 5) % 6) * (TAU / 6.0)
)
var space_state: = get_world_3d().direct_space_state
var query: = PhysicsRayQueryParameters3D.create(
vision_sensor.global_position,
vision_sensor.global_position + (dir * detection_ray_length)
)
var result: = space_state.intersect_ray(query)
if result:
if result.collider == prop:
raycast_vis[ray_idx].update_vis(result.position, Color.GREEN)
line_of_sight = true
los_last_ray_idx = ray_idx
los_distance = (result.position - vision_sensor.global_position).length()
else:
raycast_vis[ray_idx].update_vis(result.position, Color.ORANGE)
if los_last_ray_idx == ray_idx:
line_of_sight = false
else:
raycast_vis[ray_idx].update_vis(vision_sensor.global_position + (dir * detection_ray_length), Color.DARK_RED)
if los_last_ray_idx == ray_idx:
line_of_sight = false
ray_idx = (ray_idx + 1) % num_rays
#endregion
#region detection
if line_of_sight:
var prop_speed: = prop.velocity.length()
if los_distance <= detection_profile.motion_range && prop_speed > MOTION_SPEED_DEADZONE:
detected += prop_speed * detection_profile.motion_sensitivity * delta
else:
detected -= detection_profile.decay * delta
else:
detected -= detection_profile.decay * delta
detected = clampf(detected, 0.0, 1.0)
#endregion