saumwacht-godot/scripts/luneraria/view/lune_camera.gd
Martin Stickelbroeck c27a0097c3 M1: spielbarer Drei-Lane-Kern mit Karte, HUD, Menü und Abnahme
Hauptszene scenes/luneraria.tscn, Saumwacht bleibt über --legacy und die
alten QA-Schalter erreichbar. Karte aus Bestandsassets plus neuem Turm,
HUD mit Planung, Lanes und Minimap, Menü umbenannt, Einstellungen werden
aus dem alten Benutzerordner übernommen. Simulation 6,6x schneller
(Raster, Caches, faire Staffelung) und exakt spiegelsymmetrisch.
Regeln 91/91, echte Anwendung 38/38, Legacy unverändert grün.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-27 21:37:23 +02:00

76 lines
2.7 KiB
GDScript

extends Camera3D
## Overview camera: orthographic three-quarter view over the whole map, pan, zoom and
## lane focus. Blau's base sits bottom-left, Rot's top-right, as on the minimap.
var focus := Vector3.ZERO
var yaw: float = 0.0
var pitch: float = deg_to_rad(-56.0)
var zoom: float = 34.0
var reduced_motion: bool = false
var _goal_focus := Vector3.ZERO
var _goal_zoom: float = 34.0
var _dragging: bool = false
func _ready() -> void:
projection = PROJECTION_ORTHOGONAL
near = .1
far = 400.0
current = true
_goal_focus = focus
_apply()
func look_at_point(point: Vector3, new_zoom: float = -1.0, instant: bool = false) -> void:
_goal_focus = Vector3(clampf(point.x, -40, 40), 0, clampf(point.z, -40, 40))
if new_zoom > 0.0:
_goal_zoom = new_zoom
if instant or reduced_motion:
focus = _goal_focus
zoom = _goal_zoom
_apply()
func overview(instant: bool = false) -> void:
look_at_point(Vector3.ZERO, 46.0, instant)
func _apply() -> void:
size = zoom
var back := Vector3(0, 0, 1).rotated(Vector3.RIGHT, pitch).rotated(Vector3.UP, yaw)
position = focus + back * 80.0
look_at(focus, Vector3.UP)
func _process(delta: float) -> void:
var pan := Vector2(Input.get_axis("ui_left", "ui_right"), Input.get_axis("ui_up", "ui_down"))
if Input.is_physical_key_pressed(KEY_A): pan.x -= 1
if Input.is_physical_key_pressed(KEY_D): pan.x += 1
if Input.is_physical_key_pressed(KEY_W): pan.y -= 1
if Input.is_physical_key_pressed(KEY_S): pan.y += 1
if pan != Vector2.ZERO and not _text_focus():
var right := Vector3(cos(yaw), 0, -sin(yaw))
var forward := Vector3(-sin(yaw), 0, -cos(yaw))
_goal_focus += (right * pan.x - forward * pan.y) * zoom * .9 * delta
var t: float = 1.0 if reduced_motion else minf(1.0, delta * 7.0)
focus = focus.lerp(_goal_focus, t)
zoom = lerpf(zoom, _goal_zoom, t)
_apply()
func _text_focus() -> bool:
var owner: Control = get_viewport().gui_get_focus_owner()
return owner is LineEdit or owner is TextEdit
## World input only; the HUD consumes its own clicks before they reach here.
func handle_world_input(event: InputEvent) -> bool:
if event is InputEventMouseButton and event.pressed:
if event.button_index == MOUSE_BUTTON_WHEEL_UP:
_goal_zoom = maxf(12.0, _goal_zoom - 2.5)
return true
if event.button_index == MOUSE_BUTTON_WHEEL_DOWN:
_goal_zoom = minf(60.0, _goal_zoom + 2.5)
return true
if event is InputEventMouseButton and event.button_index == MOUSE_BUTTON_MIDDLE:
_dragging = event.pressed
return true
if event is InputEventMouseMotion and _dragging:
var right := Vector3(cos(yaw), 0, -sin(yaw))
var forward := Vector3(-sin(yaw), 0, -cos(yaw))
_goal_focus -= (right * event.relative.x - forward * event.relative.y) * zoom / 700.0
return true
return false