extends RefCounted class_name FlyMath ## Freelook-Flug-Mathe (Karte t_c46d31c2, Karte 1/3) — bewusst pure Funktionen ## (kein Knoten, keine Kamera): speed_for/wheel_speed/move_delta/forward- und ## Euler-Umrechnung. tests/test_shortcuts.gd prüft headless; orbit_camera.gd ## ist nur der dünne Anwender. ## ## Euler-Konvention identisch zur Orbit-Kamera (_apply in orbit_camera.gd): ## Blickrichtung forward(yaw, pitch) = ## (−sin(yaw)·cos(pitch), −sin(pitch), −cos(yaw)·cos(pitch)) ## pitch positiv = Kamera schaut von OBEN herab (Orbit-Pitch 5…85°), ## im Flug erlaubt: −85…+85° (auch von unten hochschauen). const BASE_SPEED_MPS := 30.0 # Grundgeschwindigkeit (Mausrad ändert sie dauerhaft) const FAST_MULT := 3.0 # Strg halten = schneller (Shooter-Konvention) const SLOW_MULT := 0.3 # Alt halten = langsamer const WHEEL_MULT := 1.15 # Mausrad-Tick: +15 % / −13 % const SPEED_MIN_MPS := 2.0 const SPEED_MAX_MPS := 300.0 const PITCH_MAX_DEG := 85.0 # Flug-Blick grenzwertig (±) — kein Gimbal-Kippen ## Flug-Tempo: Strg dominiert, wenn (unüblicherweise) beide Modifier unten sind. static func speed_for(base_mps: float, fast: bool, slow: bool) -> float: if fast: return base_mps * FAST_MULT if slow: return base_mps * SLOW_MULT return base_mps ## Mausrad-Tick: Grundgeschwindigkeit DAUERHAFT ändern (nicht nur solange ## gedrückt) — up=true schneller, down=false langsamer, immer geklemmt. static func wheel_speed(base_mps: float, up: bool) -> float: var next := base_mps * WHEEL_MULT if up else base_mps / WHEEL_MULT return clampf(next, SPEED_MIN_MPS, SPEED_MAX_MPS) ## Bewegungs-Delta für einen Frame. forward = BLICKRICHTUNG (3D, mit Pitch — ## W/S fliegen dorthin, wo man hinsieht), right = horizontal-rechts, und ## vertikal ist Welt-UP (Leertaste/Shift heben/senken unabhängig vom Blick — ## Shooter-Konvention, Sly-Wunsch 17.10.). ## Diagonal-Eingaben werden normalisiert (kein √2-Tempo). Rückgabe: Meter. static func move_delta(forward: Vector3, right: Vector3, fwd_input: float, side_input: float, vert_input: float, speed_mps: float, delta_s: float) -> Vector3: var dir := forward * fwd_input + right * side_input + Vector3.UP * vert_input if dir.length_squared() < 0.0000001: return Vector3.ZERO return dir.normalized() * speed_mps * delta_s ## Blickrichtung → (yaw_deg, pitch_deg) in der Orbit-Konvention (siehe oben). static func euler_from_forward(forward: Vector3) -> Vector2: var f := forward if f.length_squared() < 0.0000001: return Vector2(0.0, 0.0) f = f.normalized() var pitch := rad_to_deg(asin(clampf(-f.y, -1.0, 1.0))) var yaw := rad_to_deg(atan2(-f.x, -f.z)) return Vector2(yaw, pitch) ## (yaw_deg, pitch_deg) → Blickrichtung (Inverse von euler_from_forward). static func forward_from_euler(yaw_deg: float, pitch_deg: float) -> Vector3: var yaw := deg_to_rad(yaw_deg) var pitch := deg_to_rad(clampf(pitch_deg, -PITCH_MAX_DEG, PITCH_MAX_DEG)) return Vector3( -sin(yaw) * cos(pitch), -sin(pitch), -cos(yaw) * cos(pitch))