d2eca15ecd
Camera: desired_focus_position.y now directly tracks the player's global Y. The existing smooth lerp on focus_position handles interpolation, so the camera follows the player up hills and down slopes without losing vertical sight. Previously only horizontally-deadzoned XZ was tracked. jajce_world.gd: expanded terrain-snapping coverage to include all nodes under WorldObjects (ResourceNodes, StorageSites, ActivitySites) and the FortressBlockout's children. Added a type guard so non-Node3D children are safely skipped. This prevents houses, activity props, storage crates, and resource nodes from floating above or sinking below the sculpted terrain surface.
92 lines
2.7 KiB
GDScript
92 lines
2.7 KiB
GDScript
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)
|