71 lines
No EOL
3 KiB
GDScript
71 lines
No EOL
3 KiB
GDScript
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)) |