extends Node3D @export var target: Node3D @export var follow_offset := Vector3(0, 10, 8) @export var follow_speed := 12.0 @export var rotation_smooth_speed := 18.0 @export var focus_smooth_speed := 14.0 @export var mouse_sensitivity := 0.002 @export var deadzone_right := 3.0 @export var deadzone_forward := 3.0 @export var min_zoom_offset := Vector3(0, 5, 4) @export var max_zoom_offset := Vector3(0, 16, 12) @export var zoom_step := 0.1 var yaw := 0.0 var smoothed_yaw := 0.0 var zoom := 0.5 var zoom_smooth := 0.5 var focus_position := Vector3.ZERO var desired_focus_position := Vector3.ZERO func _ready() -> void: Input.set_mouse_mode(Input.MOUSE_MODE_CAPTURED) if target: focus_position = target.global_position desired_focus_position = focus_position global_position = focus_position + min_zoom_offset.lerp(max_zoom_offset, zoom_smooth) func _unhandled_input(event: InputEvent) -> void: if event is InputEventMouseMotion: yaw -= event.relative.x * mouse_sensitivity if event is InputEventMouseButton: if event.button_index == MOUSE_BUTTON_WHEEL_UP and event.pressed: zoom = maxf(zoom - zoom_step, 0.0) elif event.button_index == MOUSE_BUTTON_WHEEL_DOWN and event.pressed: zoom = minf(zoom + zoom_step, 1.0) if event.is_action_pressed("ui_cancel"): Input.set_mouse_mode(Input.MOUSE_MODE_VISIBLE) func _physics_process(delta: float) -> void: if target == null: return var rot_t := 1.0 - exp(-rotation_smooth_speed * delta) var focus_t := 1.0 - exp(-focus_smooth_speed * delta) var follow_t := 1.0 - exp(-follow_speed * delta) smoothed_yaw = lerp_angle(smoothed_yaw, yaw, rot_t) rotation = Vector3(deg_to_rad(-55), smoothed_yaw, 0) var target_pos := target.global_position var diff := target_pos - desired_focus_position var cam_right := Vector3.RIGHT.rotated(Vector3.UP, smoothed_yaw).normalized() var cam_forward := Vector3.FORWARD.rotated(Vector3.UP, smoothed_yaw).normalized() var diff_right := diff.dot(cam_right) var diff_forward := diff.dot(cam_forward) if abs(diff_right) > deadzone_right: desired_focus_position += cam_right * (diff_right - sign(diff_right) * deadzone_right) if abs(diff_forward) > deadzone_forward: desired_focus_position += ( cam_forward * (diff_forward - sign(diff_forward) * deadzone_forward) ) desired_focus_position.y = target_pos.y focus_position = focus_position.lerp(desired_focus_position, focus_t) zoom_smooth = lerpf(zoom_smooth, zoom, follow_t) var active_offset := min_zoom_offset.lerp(max_zoom_offset, zoom_smooth) var rotated_offset := active_offset.rotated(Vector3.UP, smoothed_yaw) var desired_position := focus_position + rotated_offset global_position = global_position.lerp(desired_position, follow_t)