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