Flugmodus-Tasten: Leertaste=hoch, Shift=runter, Strg=schneller (Sly 17.10., Shooter-Konvention)
- InputMap (shortcut_keys.gd): editor_fly_up=Space, editor_fly_down=Shift, editor_fly_fast=Ctrl (vorher E/Q, Shift=fast). E/Q sauber entfernt, keine Aliasse (Sly-Freispruch). Alt=langsamer unveraendert, Mausrad bleibt Grundtempo. - Konflikt-Check (verpflichtend): Space/Shift/Ctrl allein triggern KEINE Werkzeug-Aktion — neue Testgruppe prueft InputMap-Matches (kein Undo/Save/ Redo/Pinsel/Freelook-Toggle ohne Combo-Partner) + echtes Terrain-Werkzeug: Tasten-Events starten keinen Strich. Strg+LMB-Senken bleibt LMB-gebunden, Entf/Strg+Z unangetastet. - orbit_camera._enter_fly gibt den GUI-Fokus frei, damit Leertaste keinen fokussierten Button ausloest (Space=ui_accept, Bibliothek=Platzierung). - Livetest: Leertaste hebt/Shift senkt die Kamera im Flug (orbit_camera). - Docs: docs/tasten.md, H-Overlay (help_text.gd), README, Status-/Hilfe- Texte der Kamera aktualisiert. - Test-Hygiene: _test_camera_fly_roundtrip ist jetzt Coroutine (await), view_yaw-Orbit-Toleranz 0.01 Grad (float32-Rundtrip-Drift ~2e-4 Grad/Exit). - Suite: 11 Suiten gruen (test_shortcuts 198 checks).
This commit is contained in:
parent
d6c472603f
commit
db6bcec971
8 changed files with 148 additions and 33 deletions
|
|
@ -305,8 +305,9 @@ Shortcut-Karten — Karten 2/3 ergänzen dort.
|
||||||
Windows-Layouts identisch, keine OS-Hardcodes. Jede Objekt-Operation =
|
Windows-Layouts identisch, keine OS-Hardcodes. Jede Objekt-Operation =
|
||||||
EIN ActionHistory-Eintrag (Ausnahme: reine Kamera-Aktionen).
|
EIN ActionHistory-Eintrag (Ausnahme: reine Kamera-Aktionen).
|
||||||
- **Freelook-Flug:** Rechte Maustaste GEDACHT halten = Umsehen + Flug
|
- **Freelook-Flug:** Rechte Maustaste GEDACHT halten = Umsehen + Flug
|
||||||
(W/S/A/D + E/Q, Umschalt = ×3, Alt = ×0,3, Mausrad = Grundgeschwindigkeit
|
(W/S/A/D + Leertaste hoch/Shift runter, Strg = ×3, Alt = ×0,3, Mausrad =
|
||||||
DAUERHAFT). Umschalt+F = Flug dauerhaft togglen. Orbit bleibt Default —
|
Grundgeschwindigkeit DAUERHAFT). Umschalt+F = Flug dauerhaft togglen.
|
||||||
|
Orbit bleibt Default —
|
||||||
beim Flug-Exit werden Ziel/Winkel/Abstand aus Flugposition/Blickrichtung
|
beim Flug-Exit werden Ziel/Winkel/Abstand aus Flugposition/Blickrichtung
|
||||||
zurückgerechnet (kein Kamera-Sprung, getestet). Mathe pur in
|
zurückgerechnet (kein Kamera-Sprung, getestet). Mathe pur in
|
||||||
`src/editor/fly_math.gd`, Kamera dünn (`src/viewport/orbit_camera.gd`).
|
`src/editor/fly_math.gd`, Kamera dünn (`src/viewport/orbit_camera.gd`).
|
||||||
|
|
|
||||||
|
|
@ -27,9 +27,9 @@ Konventionen (alle Karten):
|
||||||
| Mittlere Maustaste ziehen | Orbit: Um die Karte drehen |
|
| Mittlere Maustaste ziehen | Orbit: Um die Karte drehen |
|
||||||
| Mittlere Maustaste + Umschalt ziehen | Pan: Karte verschieben |
|
| Mittlere Maustaste + Umschalt ziehen | Pan: Karte verschieben |
|
||||||
| Mausrad | Zoom (aus, wenn Mausrad gerade ein Objekt dreht) |
|
| Mausrad | Zoom (aus, wenn Mausrad gerade ein Objekt dreht) |
|
||||||
| **Rechte Maustaste GEDACHT halten** | **Freelook-Flug:** Umsehen + W vor / S zurück / A links / D rechts; E hoch / Q runter. **Funktioniert in JEDEM Werkzeug** (Terrain, Bauen, Spawn-Zonen, Platzieren) — die rechte Maustaste hat ABSOLUTEN Vorrang, kein Werkzeug belegt sie (Karte t_2470de89) |
|
| **Rechte Maustaste GEDACHT halten** | **Freelook-Flug:** Umsehen + W vor / S zurück / A links / D rechts; **Leertaste hoch / Umschalt runter** (Shooter-Konvention wie Half-Life/Counter-Strike — Sly-Wunsch 17.10.). **Funktioniert in JEDEM Werkzeug** (Terrain, Bauen, Spawn-Zonen, Platzieren) — die rechte Maustaste hat ABSOLUTEN Vorrang, kein Werkzeug belegt sie (Karte t_2470de89) |
|
||||||
| Umschalt (im Flug) | schneller fliegen (×3) |
|
| Strg halten (im Flug) | schneller fliegen (×3) — temporär, Godot-üblich |
|
||||||
| Alt (im Flug) | langsamer fliegen (×0,3) |
|
| Alt halten (im Flug) | langsamer fliegen (×0,3) |
|
||||||
| Mausrad (im Flug) | Grundgeschwindigkeit dauerhaft ändern (2–300 m/s) |
|
| Mausrad (im Flug) | Grundgeschwindigkeit dauerhaft ändern (2–300 m/s) |
|
||||||
| **Umschalt+F** | Freelook-Flug dauerhaft an/aus (Flug bleibt ohne gedrückte rechte Maustaste aktiv) |
|
| **Umschalt+F** | Freelook-Flug dauerhaft an/aus (Flug bleibt ohne gedrückte rechte Maustaste aktiv) |
|
||||||
| **F** | Auswahl ins Bild zentrieren (Focus Selection) |
|
| **F** | Auswahl ins Bild zentrieren (Focus Selection) |
|
||||||
|
|
@ -98,7 +98,11 @@ nichts geht versehentlich verloren.
|
||||||
|
|
||||||
Konfliktfreiheit: Die Ziffern 1–4 sind NUR im Terrain-Werkzeug belegt — im
|
Konfliktfreiheit: Die Ziffern 1–4 sind NUR im Terrain-Werkzeug belegt — im
|
||||||
Objekt-/Bauen-Modus sind sie unbenutzt, im Flugmodus (rechte Maustaste
|
Objekt-/Bauen-Modus sind sie unbenutzt, im Flugmodus (rechte Maustaste
|
||||||
gehalten) bleiben E/Q die Kamera.
|
gehalten) bleiben Leertaste/Shift/Strg die Kamera. **Leertaste, Umschalt und
|
||||||
|
Strg triggern im Flug KEINE Werkzeug-Aktion:** Terrain senkt nur bei
|
||||||
|
Strg+gedrücktem Linksklick (nicht bei Strg allein), Entf/Strg+Z & Co. bleiben
|
||||||
|
unangetastet, und der Flug-Einstieg gibt den GUI-Fokus frei, damit Leertaste
|
||||||
|
keinen fokussierten Button auslöst.
|
||||||
|
|
||||||
## Werkzeuge (Karte 3/3: F-Tasten wechseln das Werkzeug)
|
## Werkzeuge (Karte 3/3: F-Tasten wechseln das Werkzeug)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -12,8 +12,8 @@ class_name FlyMath
|
||||||
## im Flug erlaubt: −85…+85° (auch von unten hochschauen).
|
## im Flug erlaubt: −85…+85° (auch von unten hochschauen).
|
||||||
|
|
||||||
const BASE_SPEED_MPS := 30.0 # Grundgeschwindigkeit (Mausrad ändert sie dauerhaft)
|
const BASE_SPEED_MPS := 30.0 # Grundgeschwindigkeit (Mausrad ändert sie dauerhaft)
|
||||||
const FAST_MULT := 3.0 # Umschalt = schneller
|
const FAST_MULT := 3.0 # Strg halten = schneller (Shooter-Konvention)
|
||||||
const SLOW_MULT := 0.3 # Alt = langsamer
|
const SLOW_MULT := 0.3 # Alt halten = langsamer
|
||||||
const WHEEL_MULT := 1.15 # Mausrad-Tick: +15 % / −13 %
|
const WHEEL_MULT := 1.15 # Mausrad-Tick: +15 % / −13 %
|
||||||
const SPEED_MIN_MPS := 2.0
|
const SPEED_MIN_MPS := 2.0
|
||||||
const SPEED_MAX_MPS := 300.0
|
const SPEED_MAX_MPS := 300.0
|
||||||
|
|
@ -21,7 +21,7 @@ const SPEED_MAX_MPS := 300.0
|
||||||
const PITCH_MAX_DEG := 85.0 # Flug-Blick grenzwertig (±) — kein Gimbal-Kippen
|
const PITCH_MAX_DEG := 85.0 # Flug-Blick grenzwertig (±) — kein Gimbal-Kippen
|
||||||
|
|
||||||
|
|
||||||
## Flug-Tempo: Umschalt dominiert, wenn (unüblicherweise) beide Modifier unten sind.
|
## Flug-Tempo: Strg dominiert, wenn (unüblicherweise) beide Modifier unten sind.
|
||||||
static func speed_for(base_mps: float, fast: bool, slow: bool) -> float:
|
static func speed_for(base_mps: float, fast: bool, slow: bool) -> float:
|
||||||
if fast:
|
if fast:
|
||||||
return base_mps * FAST_MULT
|
return base_mps * FAST_MULT
|
||||||
|
|
@ -39,7 +39,8 @@ static func wheel_speed(base_mps: float, up: bool) -> float:
|
||||||
|
|
||||||
## Bewegungs-Delta für einen Frame. forward = BLICKRICHTUNG (3D, mit Pitch —
|
## Bewegungs-Delta für einen Frame. forward = BLICKRICHTUNG (3D, mit Pitch —
|
||||||
## W/S fliegen dorthin, wo man hinsieht), right = horizontal-rechts, und
|
## W/S fliegen dorthin, wo man hinsieht), right = horizontal-rechts, und
|
||||||
## vertikal ist Welt-UP (E/Q heben/senken unabhängig vom Blick).
|
## 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.
|
## Diagonal-Eingaben werden normalisiert (kein √2-Tempo). Rückgabe: Meter.
|
||||||
static func move_delta(forward: Vector3, right: Vector3, fwd_input: float,
|
static func move_delta(forward: Vector3, right: Vector3, fwd_input: float,
|
||||||
side_input: float, vert_input: float, speed_mps: float, delta_s: float) -> Vector3:
|
side_input: float, vert_input: float, speed_mps: float, delta_s: float) -> Vector3:
|
||||||
|
|
|
||||||
|
|
@ -5,8 +5,9 @@ class_name ShortcutKeys
|
||||||
## Konvention (alle Shortcut-Karten): Tastaturcode-basiert — JEDER Action wird
|
## Konvention (alle Shortcut-Karten): Tastaturcode-basiert — JEDER Action wird
|
||||||
## ein InputEventKey mit PHYSICAL keycode zugewiesen (KEY_*-Konstante). Die
|
## ein InputEventKey mit PHYSICAL keycode zugewiesen (KEY_*-Konstante). Die
|
||||||
## physische Taste liegt unabhängig vom Tastaturlayout an derselben Position
|
## physische Taste liegt unabhängig vom Tastaturlayout an derselben Position
|
||||||
## (QWERTZ wie QWERTY bei W/A/S/D/E/Q/F), damit deutsche und Windows-Tastaturen
|
## (QWERTZ wie QWERTY bei W/A/S/D/Leertaste/Shift), damit deutsche und
|
||||||
## identisch bedienen — keine OS-Hardcodes (PLAN.md Plattform-Regel).
|
## Windows-Tastaturen identisch bedienen — keine OS-Hardcodes (PLAN.md
|
||||||
|
## Plattform-Regel).
|
||||||
##
|
##
|
||||||
## Registrierung per Code statt project.godot: KEY_*-Konstanten bleiben im
|
## Registrierung per Code statt project.godot: KEY_*-Konstanten bleiben im
|
||||||
## Quelltext lesbar und der Test kann exakt dieselben Konstanten prüfen.
|
## Quelltext lesbar und der Test kann exakt dieselben Konstanten prüfen.
|
||||||
|
|
@ -23,9 +24,18 @@ const ACTIONS := {
|
||||||
"editor_fly_back": [KEY_S],
|
"editor_fly_back": [KEY_S],
|
||||||
"editor_fly_left": [KEY_A],
|
"editor_fly_left": [KEY_A],
|
||||||
"editor_fly_right": [KEY_D],
|
"editor_fly_right": [KEY_D],
|
||||||
"editor_fly_up": [KEY_E],
|
# Flug-Vertikal + Tempo (Shooter-Konvention, Sly-Wunsch 17.10.):
|
||||||
"editor_fly_down": [KEY_Q],
|
# Leertaste = hoch, Shift = runter (Half-Life/CS-Konvention), Strg halten =
|
||||||
"editor_fly_fast": [KEY_SHIFT],
|
# temporär schneller, Alt halten = langsamer. Mausrad bleibt Grundtempo.
|
||||||
|
# Konflikt-Freiheit: Leertaste/Shift/Strg SIND im Editor sonst nur als
|
||||||
|
# Modifier-Kombos belegt (Strg+Z, Umschalt+Klick …) — allein gedrückt
|
||||||
|
# triggern sie KEINE Werkzeug-Aktion (Tests: test_shortcuts.gd,
|
||||||
|
# Konflikt-Gruppe). Im Flugmodus gibt der Kamera-Einstieg den GUI-Fokus
|
||||||
|
# frei (orbit_camera._enter_fly), damit Leertaste keinen fokussierten
|
||||||
|
# Button auslöst.
|
||||||
|
"editor_fly_up": [KEY_SPACE],
|
||||||
|
"editor_fly_down": [KEY_SHIFT],
|
||||||
|
"editor_fly_fast": [KEY_CTRL],
|
||||||
"editor_fly_slow": [KEY_ALT],
|
"editor_fly_slow": [KEY_ALT],
|
||||||
# --- Auswahl / Objekt-Operationen ----------------------------------------
|
# --- Auswahl / Objekt-Operationen ----------------------------------------
|
||||||
"editor_focus_selection": [KEY_F],
|
"editor_focus_selection": [KEY_F],
|
||||||
|
|
|
||||||
|
|
@ -14,9 +14,9 @@ static func text() -> String:
|
||||||
["Mittlere Maustaste ziehen", "Orbit: um die Karte drehen"],
|
["Mittlere Maustaste ziehen", "Orbit: um die Karte drehen"],
|
||||||
["Mittlere Maustaste + Umschalt ziehen", "Pan: Karte verschieben"],
|
["Mittlere Maustaste + Umschalt ziehen", "Pan: Karte verschieben"],
|
||||||
["Mausrad", "Zoom (aus, während ein Objekt gedreht wird)"],
|
["Mausrad", "Zoom (aus, während ein Objekt gedreht wird)"],
|
||||||
["Rechte Maustaste GEDACHT halten", "Freelook-Flug: Umsehen + W vor / S zurück / A links / D rechts; E hoch / Q runter — funktioniert in JEDEM Werkzeug (Terrain, Bauen, Platzieren), die rechte Maustaste hat immer Vorrang"],
|
["Rechte Maustaste GEDACHT halten", "Freelook-Flug: Umsehen + W vor / S zurück / A links / D rechts; Leertaste hoch / Shift runter (Shooter-Konvention) — funktioniert in JEDEM Werkzeug (Terrain, Bauen, Platzieren), die rechte Maustaste hat immer Vorrang"],
|
||||||
["Umschalt (im Flug)", "schneller fliegen (×3)"],
|
["Strg halten (im Flug)", "schneller fliegen (×3)"],
|
||||||
["Alt (im Flug)", "langsamer fliegen (×0,3)"],
|
["Alt halten (im Flug)", "langsamer fliegen (×0,3)"],
|
||||||
["Mausrad (im Flug)", "Grundgeschwindigkeit dauerhaft ändern (2–300 m/s)"],
|
["Mausrad (im Flug)", "Grundgeschwindigkeit dauerhaft ändern (2–300 m/s)"],
|
||||||
["Umschalt+F", "Flugmodus dauerhaft an/aus"],
|
["Umschalt+F", "Flugmodus dauerhaft an/aus"],
|
||||||
["F", "Auswahl ins Bild zentrieren"],
|
["F", "Auswahl ins Bild zentrieren"],
|
||||||
|
|
|
||||||
|
|
@ -139,7 +139,7 @@ var _tree_select_lock := false # verhindert Rückkopplung Auswahl ↔ Baum
|
||||||
|
|
||||||
func _ready() -> void:
|
func _ready() -> void:
|
||||||
# InputMap-Actions der Tastatur-Shortcuts (Karte t_c46d31c2) — VOR UI/Kamera,
|
# InputMap-Actions der Tastatur-Shortcuts (Karte t_c46d31c2) — VOR UI/Kamera,
|
||||||
# damit die Kamera-Polls (Flug W/A/S/D/E/Q) die Actions sofort kennen.
|
# damit die Kamera-Polls (Flug W/A/S/D + Leertaste/Shift) sofort kennen.
|
||||||
ShortcutKeys.register()
|
ShortcutKeys.register()
|
||||||
manifest = _load_manifest()
|
manifest = _load_manifest()
|
||||||
_build_ui()
|
_build_ui()
|
||||||
|
|
|
||||||
|
|
@ -6,10 +6,12 @@ extends Camera3D
|
||||||
## Mausrad = Zoom — AUSSER ein Objekt/eine Platzier-Vorschau
|
## Mausrad = Zoom — AUSSER ein Objekt/eine Platzier-Vorschau
|
||||||
## ist aktiv: dann dreht das Mausrad das Objekt
|
## ist aktiv: dann dreht das Mausrad das Objekt
|
||||||
## (interaction.gd meldet das per wheel_filter).
|
## (interaction.gd meldet das per wheel_filter).
|
||||||
## Rechte Maustaste GEDACKT = Freelook-Flug (Karte t_c46d31c2): Umsehen +
|
## Rechte Maustaste GEDACHT halten = Freelook-Flug (Karte t_c46d31c2): Umsehen +
|
||||||
## W vor / S zurück / A links / D rechts,
|
## W vor / S zurück / A links / D rechts,
|
||||||
## E hoch / Q runter, Shift = schneller,
|
## Leertaste hoch / Shift runter (Shooter-
|
||||||
## Alt = langsamer. Mausrad ändert im Flug die
|
## Konvention, Sly-Wunsch 17.10.),
|
||||||
|
## Strg = schneller, Alt = langsamer.
|
||||||
|
## Mausrad ändert im Flug die
|
||||||
## GRUNDgeschwindigkeit dauerhaft.
|
## GRUNDgeschwindigkeit dauerhaft.
|
||||||
## Umschalt+F (main.gd) = Freelook dauerhaft an/aus (Flug bleibt auch
|
## Umschalt+F (main.gd) = Freelook dauerhaft an/aus (Flug bleibt auch
|
||||||
## ohne gedrückte rechte Maustaste aktiv).
|
## ohne gedrückte rechte Maustaste aktiv).
|
||||||
|
|
@ -146,7 +148,7 @@ func toggle_fly_lock() -> void:
|
||||||
else:
|
else:
|
||||||
_fly_locked = true
|
_fly_locked = true
|
||||||
_enter_fly()
|
_enter_fly()
|
||||||
_status("Freelook-Flug DAUERHAFT an: W/S/A/D fliegen, E/Q hoch/runter, Umschalt schneller, Alt langsamer — Umschalt+F zurück zum Orbit.")
|
_status("Freelook-Flug DAUERHAFT an: W/S/A/D fliegen, Leertaste hoch / Shift runter, Strg schneller, Alt langsamer — Umschalt+F zurück zum Orbit.")
|
||||||
|
|
||||||
|
|
||||||
## Im Flugmodus? (interaction.gd blockt z. B. Mausrad-Objektdrehung dann ab.)
|
## Im Flugmodus? (interaction.gd blockt z. B. Mausrad-Objektdrehung dann ab.)
|
||||||
|
|
@ -183,7 +185,14 @@ func _enter_fly() -> void:
|
||||||
_fly_yaw = yaw_deg
|
_fly_yaw = yaw_deg
|
||||||
_fly_pitch = pitch_deg
|
_fly_pitch = pitch_deg
|
||||||
_flying = true
|
_flying = true
|
||||||
_status("Freelook-Flug an: W/S/A/D fliegen, E/Q hoch/runter, Umschalt = schnell, Alt = langsam, Mausrad = Grund-Tempo. Rechte Maustaste loslassen = zurück zum Orbit.")
|
# Konflikt-Check (Sly-Wunsch 17.10.): Leertaste ist jetzt „hoch fliegen" —
|
||||||
|
# ein fokussierter Button würde Space/Enter als Klick interpretieren
|
||||||
|
# (Bibliothek-Button = Platzierung starten). Im Flug gehört die Leertaste
|
||||||
|
# der Kamera, also GUI-Fokus freigeben.
|
||||||
|
var vp := get_viewport()
|
||||||
|
if vp != null and vp.gui_get_focus_owner() != null:
|
||||||
|
vp.gui_release_focus()
|
||||||
|
_status("Freelook-Flug an: W/S/A/D fliegen, Leertaste hoch / Shift runter, Strg = schnell, Alt = langsam, Mausrad = Grund-Tempo. Rechte Maustaste loslassen = zurück zum Orbit.")
|
||||||
|
|
||||||
|
|
||||||
func _exit_fly() -> void:
|
func _exit_fly() -> void:
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,7 @@ var checks := 0
|
||||||
func _init() -> void:
|
func _init() -> void:
|
||||||
ShortcutKeys.register()
|
ShortcutKeys.register()
|
||||||
_test_inputmap_actions()
|
_test_inputmap_actions()
|
||||||
|
_test_fly_key_conflicts()
|
||||||
_test_inputmap_actions_3()
|
_test_inputmap_actions_3()
|
||||||
_test_policy_3()
|
_test_policy_3()
|
||||||
_test_fly_math()
|
_test_fly_math()
|
||||||
|
|
@ -24,7 +25,7 @@ func _init() -> void:
|
||||||
await process_frame
|
await process_frame
|
||||||
_test_multi_delete()
|
_test_multi_delete()
|
||||||
_test_focus_center()
|
_test_focus_center()
|
||||||
_test_camera_fly_roundtrip()
|
await _test_camera_fly_roundtrip() # coroutine seit Sly 17.10. (Flug-Vertikal-Livetest)
|
||||||
_test_rotate_90()
|
_test_rotate_90()
|
||||||
_test_nudge_grid()
|
_test_nudge_grid()
|
||||||
_test_lift_multi()
|
_test_lift_multi()
|
||||||
|
|
@ -123,9 +124,9 @@ func _test_inputmap_actions() -> void:
|
||||||
"editor_fly_back": KEY_S,
|
"editor_fly_back": KEY_S,
|
||||||
"editor_fly_left": KEY_A,
|
"editor_fly_left": KEY_A,
|
||||||
"editor_fly_right": KEY_D,
|
"editor_fly_right": KEY_D,
|
||||||
"editor_fly_up": KEY_E,
|
"editor_fly_up": KEY_SPACE,
|
||||||
"editor_fly_down": KEY_Q,
|
"editor_fly_down": KEY_SHIFT,
|
||||||
"editor_fly_fast": KEY_SHIFT,
|
"editor_fly_fast": KEY_CTRL,
|
||||||
"editor_fly_slow": KEY_ALT,
|
"editor_fly_slow": KEY_ALT,
|
||||||
"editor_focus_selection": KEY_F,
|
"editor_focus_selection": KEY_F,
|
||||||
"editor_cancel": KEY_ESCAPE,
|
"editor_cancel": KEY_ESCAPE,
|
||||||
|
|
@ -196,6 +197,68 @@ func _test_inputmap_actions() -> void:
|
||||||
_check(lift_floor, "editor_lift_to_floor → Strg+Bild↑")
|
_check(lift_floor, "editor_lift_to_floor → Strg+Bild↑")
|
||||||
|
|
||||||
|
|
||||||
|
# -------------------------------- Flug-Tasten Konflikt-Check (Sly 17.10.) -----
|
||||||
|
## Sly-Wunsch 17.10.: Leertaste = hoch, Shift = runter, Strg = schneller.
|
||||||
|
## KONFLIKT-CHECK verpflichtend: die drei Tasten dürfen ALLEIN (ohne
|
||||||
|
## Kombos-Partner) KEINE Werkzeug-Aktion auslösen — weder über die InputMap
|
||||||
|
## (falsche Action) noch im Terrain-Werkzeug (Strg allein startet keinen
|
||||||
|
## Strich). Synthetische Events mit PHYSICAL keycode wie die echte Tastatur.
|
||||||
|
func _key_event(keycode: Key, shift := false, ctrl := false) -> InputEventKey:
|
||||||
|
var ev := InputEventKey.new()
|
||||||
|
ev.physical_keycode = keycode
|
||||||
|
ev.keycode = keycode
|
||||||
|
ev.shift_pressed = shift
|
||||||
|
ev.ctrl_pressed = ctrl
|
||||||
|
ev.pressed = true
|
||||||
|
return ev
|
||||||
|
|
||||||
|
|
||||||
|
func _matches(keycode: Key, action: String, shift := false, ctrl := false) -> bool:
|
||||||
|
return InputMap.event_is_action(_key_event(keycode, shift, ctrl), action, false)
|
||||||
|
|
||||||
|
|
||||||
|
func _test_fly_key_conflicts() -> void:
|
||||||
|
print("Testgruppe: Flug-Tasten Konflikt-Check (Leertaste/Shift/Strg)")
|
||||||
|
# Die neuen Flug-Actions feuern auf die EINZELNE Taste.
|
||||||
|
_check(_matches(KEY_SPACE, "editor_fly_up"), "Leertaste allein → editor_fly_up")
|
||||||
|
_check(_matches(KEY_SHIFT, "editor_fly_down"), "Shift allein → editor_fly_down")
|
||||||
|
_check(_matches(KEY_CTRL, "editor_fly_fast"), "Strg allein → editor_fly_fast")
|
||||||
|
# ... aber auf KEINE andere Editor-Action (alle verlangen Partner/andere Taste).
|
||||||
|
_check(not _matches(KEY_SPACE, "editor_fly_forward"), "Leertaste ≠ editor_fly_forward (W bleibt W)")
|
||||||
|
_check(not _matches(KEY_SPACE, "editor_help_overlay"), "Leertaste löst KEIN H aus")
|
||||||
|
_check(not _matches(KEY_SPACE, "editor_brush_raise"), "Leertaste schaltet KEINEN Pinselmodus")
|
||||||
|
_check(not _matches(KEY_SHIFT, "editor_freelook_toggle"), "Shift allein togglet NICHT den Flug (braucht F)")
|
||||||
|
_check(_matches(KEY_F, "editor_freelook_toggle", true), "Umschalt+F togglet den Flug weiter (unangetastet)")
|
||||||
|
_check(not _matches(KEY_SHIFT, "editor_save_as"), "Shift allein speichert NICHT (braucht Strg+S)")
|
||||||
|
_check(not _matches(KEY_SHIFT, "editor_redo_shift_z"), "Shift allein redoet NICHT (braucht Strg+Z)")
|
||||||
|
_check(not _matches(KEY_CTRL, "editor_undo"), "Strg allein macht KEIN Undo (braucht Z)")
|
||||||
|
_check(not _matches(KEY_CTRL, "editor_duplicate_selection"), "Strg allein dupliziert NICHT (braucht D)")
|
||||||
|
_check(not _matches(KEY_CTRL, "editor_lift_to_floor"), "Strg allein setzt NICHT auf den Boden (braucht Bild↑)")
|
||||||
|
_check(not _matches(KEY_CTRL, "editor_redo_y"), "Strg allein redoet NICHT (braucht Y)")
|
||||||
|
# Bestands-Combos unangetastet (Entf-Yolo etc.).
|
||||||
|
_check(_matches(KEY_Z, "editor_undo", false, true), "Strg+Z wirkt weiter (unangetastet)")
|
||||||
|
_check(_matches(KEY_DELETE, "editor_delete_selection"), "Entf wirkt weiter (unangetastet)")
|
||||||
|
# ECHTES Terrain-Werkzeug aktiv: Tasten-Events (Leertaste/Shift/Strg) dürfen
|
||||||
|
# keinen Strich starten — der Pinsel lebt nur von LMB (+Strg-Invert).
|
||||||
|
var res := 8
|
||||||
|
var flat := PackedFloat32Array()
|
||||||
|
flat.resize(res * res)
|
||||||
|
var tfake := ProbeMain.new()
|
||||||
|
tfake.doc = {"terrain": {
|
||||||
|
"resolution": res, "size_x_m": 100.0, "size_z_m": 100.0,
|
||||||
|
"min_height_m": 0.0, "max_height_m": 10.0,
|
||||||
|
"heightmap_r16_b64": TerrainField.encode(flat, 0.0, 10.0),
|
||||||
|
}}
|
||||||
|
var ttool := Node3D.new()
|
||||||
|
ttool.set_script(load("res://src/editor/terrain_tool.gd"))
|
||||||
|
root.add_child(ttool)
|
||||||
|
ttool.setup(tfake, Camera3D.new(), ProbeBuilder.new(), null)
|
||||||
|
_check(bool(ttool.call("activate")), "Terrain-Werkzeug aktiviert (Test-Map)")
|
||||||
|
for keycode in [KEY_SPACE, KEY_SHIFT, KEY_CTRL]:
|
||||||
|
ttool.call("_unhandled_input", _key_event(keycode))
|
||||||
|
_check(not bool(ttool.get("_stroking")), "Terrain: Leertaste/Shift/Strg ALLEIN starten KEINEN Strich")
|
||||||
|
|
||||||
|
|
||||||
# ------------------------------------------------- Karte 3/3 (t_11b5c25b) ----
|
# ------------------------------------------------- Karte 3/3 (t_11b5c25b) ----
|
||||||
|
|
||||||
## Karte 3/3: Datei & Undo, Werkzeugwechsel (F-Tasten), Hilfe + Test als
|
## Karte 3/3: Datei & Undo, Werkzeugwechsel (F-Tasten), Hilfe + Test als
|
||||||
|
|
@ -259,14 +322,15 @@ func _test_policy_3() -> void:
|
||||||
func _test_fly_math() -> void:
|
func _test_fly_math() -> void:
|
||||||
print("Testgruppe: Freelook-Mathe (FlyMath)")
|
print("Testgruppe: Freelook-Mathe (FlyMath)")
|
||||||
_check(absf(FlyMath.speed_for(30.0, false, false) - 30.0) < 0.0001, "Tempo normal = Grund")
|
_check(absf(FlyMath.speed_for(30.0, false, false) - 30.0) < 0.0001, "Tempo normal = Grund")
|
||||||
_check(absf(FlyMath.speed_for(30.0, true, false) - 90.0) < 0.0001, "Umschalt = ×3")
|
_check(absf(FlyMath.speed_for(30.0, true, false) - 90.0) < 0.0001, "Strg = ×3")
|
||||||
_check(absf(FlyMath.speed_for(30.0, false, true) - 9.0) < 0.0001, "Alt = ×0,3")
|
_check(absf(FlyMath.speed_for(30.0, false, true) - 9.0) < 0.0001, "Alt = ×0,3")
|
||||||
_check(absf(FlyMath.speed_for(30.0, true, true) - 90.0) < 0.0001, "Umschalt dominiert bei beiden")
|
_check(absf(FlyMath.speed_for(30.0, true, true) - 90.0) < 0.0001, "Strg dominiert bei beiden")
|
||||||
_check(absf(FlyMath.wheel_speed(30.0, true) - 34.5) < 0.0001, "Mausrad hoch: ×1,15 dauerhaft")
|
_check(absf(FlyMath.wheel_speed(30.0, true) - 34.5) < 0.0001, "Mausrad hoch: ×1,15 dauerhaft")
|
||||||
_check(absf(FlyMath.wheel_speed(30.0, false) - 30.0 / 1.15) < 0.0001, "Mausrad runter: ÷1,15 dauerhaft")
|
_check(absf(FlyMath.wheel_speed(30.0, false) - 30.0 / 1.15) < 0.0001, "Mausrad runter: ÷1,15 dauerhaft")
|
||||||
_check(FlyMath.wheel_speed(299.0, true) == FlyMath.SPEED_MAX_MPS, "Mausrad: Tempo-Grenze oben (300 m/s)")
|
_check(FlyMath.wheel_speed(299.0, true) == FlyMath.SPEED_MAX_MPS, "Mausrad: Tempo-Grenze oben (300 m/s)")
|
||||||
_check(FlyMath.wheel_speed(2.0, false) == FlyMath.SPEED_MIN_MPS, "Mausrad: Tempo-Grenze unten (2 m/s)")
|
_check(FlyMath.wheel_speed(2.0, false) == FlyMath.SPEED_MIN_MPS, "Mausrad: Tempo-Grenze unten (2 m/s)")
|
||||||
# Bewegung: W fliegt in Blickrichtung (3D), A/D horizontal, E/Q weltachsig.
|
# Bewegung: W fliegt in Blickrichtung (3D), A/D horizontal,
|
||||||
|
# Leertaste/Shift weltachsig.
|
||||||
var fwd := FlyMath.forward_from_euler(0.0, 30.0) # Blick nach -Z, leicht abwärts
|
var fwd := FlyMath.forward_from_euler(0.0, 30.0) # Blick nach -Z, leicht abwärts
|
||||||
var right := FlyMath.forward_from_euler(0.0, 0.0).cross(Vector3.UP).normalized()
|
var right := FlyMath.forward_from_euler(0.0, 0.0).cross(Vector3.UP).normalized()
|
||||||
var d := FlyMath.move_delta(fwd, right, 1.0, 0.0, 0.0, 10.0, 1.0)
|
var d := FlyMath.move_delta(fwd, right, 1.0, 0.0, 0.0, 10.0, 1.0)
|
||||||
|
|
@ -274,7 +338,7 @@ func _test_fly_math() -> void:
|
||||||
var side := FlyMath.move_delta(fwd, right, 0.0, 1.0, 0.0, 10.0, 1.0)
|
var side := FlyMath.move_delta(fwd, right, 0.0, 1.0, 0.0, 10.0, 1.0)
|
||||||
_check(absf(side.y) < 0.0001 and absf(side.x - 10.0) < 0.0001, "D straft horizontal rechts (kein Höhen-Anteil)")
|
_check(absf(side.y) < 0.0001 and absf(side.x - 10.0) < 0.0001, "D straft horizontal rechts (kein Höhen-Anteil)")
|
||||||
var up := FlyMath.move_delta(fwd, right, 0.0, 0.0, 1.0, 10.0, 1.0)
|
var up := FlyMath.move_delta(fwd, right, 0.0, 0.0, 1.0, 10.0, 1.0)
|
||||||
_check(absf((up - Vector3.UP * 10.0).length()) < 0.0001, "E hebt weltachsig (+10 m)")
|
_check(absf((up - Vector3.UP * 10.0).length()) < 0.0001, "Leertaste hebt weltachsig (+10 m)")
|
||||||
var diag := FlyMath.move_delta(fwd, right, 1.0, 1.0, 0.0, 10.0, 1.0)
|
var diag := FlyMath.move_delta(fwd, right, 1.0, 1.0, 0.0, 10.0, 1.0)
|
||||||
_check(absf(diag.length() - 10.0) < 0.0001, "Diagonale normalisiert (√2-Falle umgangen)")
|
_check(absf(diag.length() - 10.0) < 0.0001, "Diagonale normalisiert (√2-Falle umgangen)")
|
||||||
_check(FlyMath.move_delta(fwd, right, 0.0, 0.0, 0.0, 10.0, 1.0) == Vector3.ZERO, "keine Eingabe → kein Delta")
|
_check(FlyMath.move_delta(fwd, right, 0.0, 0.0, 0.0, 10.0, 1.0) == Vector3.ZERO, "keine Eingabe → kein Delta")
|
||||||
|
|
@ -433,8 +497,15 @@ func _test_camera_fly_roundtrip() -> void:
|
||||||
cam.call("_apply")
|
cam.call("_apply")
|
||||||
var pos_before: Vector3 = cam.position
|
var pos_before: Vector3 = cam.position
|
||||||
_check(not bool(cam.call("fly_active")), "Orbit-Modus ist Default (kein Flug)")
|
_check(not bool(cam.call("fly_active")), "Orbit-Modus ist Default (kein Flug)")
|
||||||
|
# Konflikt-Check (Sly 17.10.): Flug-Einstieg gibt den GUI-Fokus frei —
|
||||||
|
# Leertaste darf keinen fokussierten Button auslösen (Space = ui_accept).
|
||||||
|
var fokus_btn := Button.new()
|
||||||
|
root.add_child(fokus_btn)
|
||||||
|
fokus_btn.grab_focus()
|
||||||
|
_check(root.gui_get_focus_owner() == fokus_btn, "Button hat GUI-Fokus (Ausgangslage)")
|
||||||
cam.call("_enter_fly")
|
cam.call("_enter_fly")
|
||||||
_check(bool(cam.call("fly_active")), "RMT gedrückt → Flugmodus aktiv")
|
_check(bool(cam.call("fly_active")), "RMT gedrückt → Flugmodus aktiv")
|
||||||
|
_check(root.gui_get_focus_owner() == null, "Flug-Einstieg gibt den GUI-Fokus frei (Leertaste gehört der Kamera)")
|
||||||
cam.position += Vector3(25.0, -10.0, 5.0) # im Flug frei bewegt
|
cam.position += Vector3(25.0, -10.0, 5.0) # im Flug frei bewegt
|
||||||
cam.call("_exit_fly")
|
cam.call("_exit_fly")
|
||||||
_check(not bool(cam.call("fly_active")), "RMT losgelassen → zurück zum Orbit")
|
_check(not bool(cam.call("fly_active")), "RMT losgelassen → zurück zum Orbit")
|
||||||
|
|
@ -452,8 +523,27 @@ func _test_camera_fly_roundtrip() -> void:
|
||||||
_check(bool(cam.call("fly_active")), "Umschalt+F: Dauerflug an (ohne RMT)")
|
_check(bool(cam.call("fly_active")), "Umschalt+F: Dauerflug an (ohne RMT)")
|
||||||
cam.call("toggle_fly_lock")
|
cam.call("toggle_fly_lock")
|
||||||
_check(not bool(cam.call("fly_active")), "Umschalt+F nochmal: zurück zum Orbit")
|
_check(not bool(cam.call("fly_active")), "Umschalt+F nochmal: zurück zum Orbit")
|
||||||
|
# Neue Flug-Tasten (Sly 17.10.): Leertaste hebt, Shift senkt — live durch
|
||||||
|
# orbit_camera._process (Action-Strength-Poll, weltachsig).
|
||||||
|
cam.call("toggle_fly_lock")
|
||||||
|
var y0: float = cam.position.y
|
||||||
|
Input.action_press("editor_fly_up")
|
||||||
|
await process_frame
|
||||||
|
await process_frame
|
||||||
|
Input.action_release("editor_fly_up")
|
||||||
|
_check(cam.position.y > y0, "Leertaste hebt die Kamera im Flug (orbit_camera)")
|
||||||
|
var y1: float = cam.position.y
|
||||||
|
Input.action_press("editor_fly_down")
|
||||||
|
await process_frame
|
||||||
|
await process_frame
|
||||||
|
Input.action_release("editor_fly_down")
|
||||||
|
_check(cam.position.y < y1, "Shift senkt die Kamera im Flug (orbit_camera)")
|
||||||
|
cam.call("toggle_fly_lock")
|
||||||
# Blick-Yaw-Accessor (Karte t_55257792): Orbit → yaw_deg, Flug → Flug-Yaw.
|
# Blick-Yaw-Accessor (Karte t_55257792): Orbit → yaw_deg, Flug → Flug-Yaw.
|
||||||
_check(absf(float(cam.call("view_yaw_deg")) - 30.0) < 0.0001,
|
# Toleranz 0,01°: jeder Flug-Exit rechnet yaw aus der Kamera-Basis zurück
|
||||||
|
# (look_at → euler_from_forward, float32) — der Sly-17.10.-Block oben macht
|
||||||
|
# einen Zyklus mehr, der Drift liegt bei ~2e-4° pro Roundtrip (unsichtbar).
|
||||||
|
_check(absf(float(cam.call("view_yaw_deg")) - 30.0) < 0.01,
|
||||||
"view_yaw_deg im Orbit = Orbit-Yaw (30°)")
|
"view_yaw_deg im Orbit = Orbit-Yaw (30°)")
|
||||||
cam.set("_fly_yaw", -120.0)
|
cam.set("_fly_yaw", -120.0)
|
||||||
cam.set("_flying", true)
|
cam.set("_flying", true)
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue