Files
train-game/scripts/camera_rig.gd
2026-09-16 17:55:38 +02:00

78 lines
2.3 KiB
GDScript

extends Node3D
## Aussenkamera: folgt dem Zug, drehbar und zoombar.
const MIN_DIST := 14.0
const MAX_DIST := 140.0
var target: Node3D
var yaw := 0.0
var pitch := deg_to_rad(-34.0)
var distance := 46.0
var _cam: Camera3D
var _focus := Vector3.ZERO
var blocked := false ## zuletzt an einem Hindernis abgefangen
func _ready() -> void:
_cam = Camera3D.new()
_cam.fov = 50.0
_cam.far = 900.0
add_child(_cam)
func activate() -> void:
_cam.current = true
func is_active() -> bool:
return _cam.current
func _unhandled_input(event: InputEvent) -> void:
if not is_active():
return
if event is InputEventMouseButton and event.pressed:
if event.button_index == MOUSE_BUTTON_WHEEL_UP:
distance = clampf(distance * 0.88, MIN_DIST, MAX_DIST)
elif event.button_index == MOUSE_BUTTON_WHEEL_DOWN:
distance = clampf(distance * 1.14, MIN_DIST, MAX_DIST)
func _process(delta: float) -> void:
if not is_active():
if target:
_focus = target.global_position
return
var turn := 0.0
if Input.is_key_pressed(KEY_A) or Input.is_key_pressed(KEY_LEFT):
turn += 1.0
if Input.is_key_pressed(KEY_D) or Input.is_key_pressed(KEY_RIGHT):
turn -= 1.0
yaw += turn * 1.5 * delta
if Input.is_key_pressed(KEY_R):
pitch = clampf(pitch + 0.9 * delta, deg_to_rad(-82.0), deg_to_rad(-8.0))
if Input.is_key_pressed(KEY_F):
pitch = clampf(pitch - 0.9 * delta, deg_to_rad(-82.0), deg_to_rad(-8.0))
if target:
_focus = _focus.lerp(target.global_position, clampf(delta * 5.0, 0.0, 1.0))
blocked = false
position = _focus
var dir := Vector3(0, 0, 1).rotated(Vector3.RIGHT, pitch).rotated(Vector3.UP, yaw)
var wanted: Vector3 = dir * distance
_cam.position = _clip_to_view(wanted)
_cam.look_at(global_position + Vector3(0, 2.5, 0), Vector3.UP)
## Zieht die Kamera vor ein Hindernis, statt hindurchzuschauen.
func _clip_to_view(local_target: Vector3) -> Vector3:
var from: Vector3 = global_position + Vector3(0, 2.5, 0)
var to: Vector3 = global_position + local_target
var space := get_world_3d().direct_space_state
var q := PhysicsRayQueryParameters3D.create(from, to, 2)
var hit := space.intersect_ray(q)
if hit.is_empty():
return local_target
var p: Vector3 = hit["position"] + (from - hit["position"]).normalized() * 0.7
blocked = true
return p - global_position
func snap_to(p: Vector3) -> void:
_focus = p
position = p