Physik: One-Way-Plattformen (Collider und Tiles, mit Drop-through) #155

Closed
opened 2026-09-18 22:57:40 +02:00 by meleki · 3 comments
Owner

Aus #144 („Später“). Plattformen, durch die man von unten durchspringt und auf denen man von oben landet.

Befund

  • Zwei Wege bewegen Objekte gegen Collider, beide brauchen die Regel:
    1. Move mit „Collide with solids“ → unsere Geometrie in Solids (#150/#151). Dort ist es einfach: Ein One-Way-Collider blockiert nur, wenn die Kontaktnormale in die erlaubte Richtung zeigt und der Mover vorher nicht schon in ihm steckte.
    2. Dynamic Bodies in Rapier. Die JS-Version von Rapier 0.20 hat kein modifySolverContacts (gibt es nur in Rust), sondern nur den Hook filterContactPair: pro Paar ganz an oder ganz aus, entschieden vor dem Step. Die Entscheidung muss dort aus Position und Geschwindigkeit fallen — geprüft, ob das ohne Ruckeln trägt, ist es nicht.
  • Für Tilemaps: One-Way pro Tile-Art, damit Plattform-Tiles keinen eigenen Collider brauchen.

Zu bauen

  • Am Collider: Schalter One-way + Richtung (Vorgabe: nach oben, d. h. blockiert nur von oben). Dreht mit dem Objekt.
  • Tile-Art: One-way als Option.
  • Solids: Regel wie oben; Move meldet Treffer nur, wenn blockiert wurde.
  • PhysicsSystem: filterContactPair mit derselben Regel, Zustand „steckt schon drin“ pro Paar merken, bis sie sich trennen.
  • „Durchfallen lassen“ (Drop-through, Taste runter): Node Ignore one-way für eine Dauer.
  • Tests: Sprung von unten durch, Landen oben, seitlich hineinlaufen, schneller Fall ohne Tunneln, Dynamic Body gleich wie Move, gegen beide Systeme.

Offen (mit Vorschlag)

  1. Seitliches Hineinlaufen: Vorschlag: nicht blockieren (nur die Normale in Plattform-Richtung zählt), wie in Unity/Godot.
  2. Toleranzwinkel für schräge One-Way-Plattformen: Vorschlag: 45°, als Feld später, falls nötig.
  3. Rapier-Weg zuerst messen: Vorschlag: vor dem Bau ein kurzer Spike, ob filterContactPair bei 60 Hz sauber landet; wenn nicht, bekommen Dynamic Bodies One-Way erst später und der Inspector sagt das.
Aus #144 („Später“). Plattformen, durch die man von unten durchspringt und auf denen man von oben landet. ## Befund - Zwei Wege bewegen Objekte gegen Collider, beide brauchen die Regel: 1. `Move` mit „Collide with solids“ → unsere Geometrie in `Solids` (#150/#151). Dort ist es einfach: Ein One-Way-Collider blockiert nur, wenn die Kontaktnormale in die erlaubte Richtung zeigt **und** der Mover vorher nicht schon in ihm steckte. 2. Dynamic Bodies in Rapier. Die JS-Version von Rapier 0.20 hat **kein** `modifySolverContacts` (gibt es nur in Rust), sondern nur den Hook `filterContactPair`: pro Paar ganz an oder ganz aus, entschieden vor dem Step. Die Entscheidung muss dort aus Position und Geschwindigkeit fallen — geprüft, ob das ohne Ruckeln trägt, ist es nicht. - Für Tilemaps: One-Way pro Tile-Art, damit Plattform-Tiles keinen eigenen Collider brauchen. ## Zu bauen - Am Collider: Schalter **One-way** + Richtung (Vorgabe: nach oben, d. h. blockiert nur von oben). Dreht mit dem Objekt. - Tile-Art: One-way als Option. - `Solids`: Regel wie oben; `Move` meldet Treffer nur, wenn blockiert wurde. - `PhysicsSystem`: `filterContactPair` mit derselben Regel, Zustand „steckt schon drin“ pro Paar merken, bis sie sich trennen. - „Durchfallen lassen“ (Drop-through, Taste runter): Node **Ignore one-way** für eine Dauer. - Tests: Sprung von unten durch, Landen oben, seitlich hineinlaufen, schneller Fall ohne Tunneln, Dynamic Body gleich wie `Move`, gegen beide Systeme. ## Offen (mit Vorschlag) 1. **Seitliches Hineinlaufen:** Vorschlag: nicht blockieren (nur die Normale in Plattform-Richtung zählt), wie in Unity/Godot. 2. **Toleranzwinkel** für schräge One-Way-Plattformen: Vorschlag: 45°, als Feld später, falls nötig. 3. **Rapier-Weg zuerst messen:** Vorschlag: vor dem Bau ein kurzer Spike, ob `filterContactPair` bei 60 Hz sauber landet; wenn nicht, bekommen Dynamic Bodies One-Way erst später und der Inspector sagt das.
admin added this to the Development project 2026-09-18 23:03:37 +02:00
admin moved this to Next in Development on 2026-09-19 11:07:20 +02:00

folgendes issue anlegen, falls noch nicht passiert: One-Way-Platform, Toleranzwinkel für schräge One-Way-Plattformen als Feld (Default die jetzigen 45 grad)

folgendes issue anlegen, falls noch nicht passiert: One-Way-Platform, Toleranzwinkel für schräge One-Way-Plattformen als Feld (Default die jetzigen 45 grad)
Author
Owner

Umgesetzt in 6a31f5e, zusammen mit #153 und #149. Die Vorschläge aus „Offen“ sind so übernommen, der Spike lief vorab.

Was gebaut ist

  • Collider: Schalter One-way und Blocks from (Above/Below/Left/Right). Die Seite dreht und spiegelt mit dem Objekt, der Scene-View zeichnet sie dick.
  • Tiles: Option One-way (from above). One-Way-Zellen verschmelzen nie mit festen Tiles zu einem Rechteck.
  • Regel: Blockiert wird nur ein Kontakt, dessen Normale höchstens 45° von der blockierenden Seite abweicht. Von unten und von der Seite geht es durch, was schon drinsteckt, fällt durch. Eine Funktion (landsOn) für Move, Raycast und Rapier.
  • Node Ignore one-way (Objekt, Sekunden): Durchfallen für eine Dauer.

Spike Rapier (gemessen bei 60 Hz)

  • Der Hook darf die Engine nicht anfassen: schon body.translation() darin wirft, der Fehler wird verschluckt und das Paar kollidiert mit gar nichts. Die Entscheidung fällt deshalb vor jedem Step, der Hook liest sie nur.
  • Landung: 1,9 px Einsinken, danach Ruhe bei 0,009 px. Von unten durch → landet oben. Seitlich → fährt durch. 2000 px/s → landet sauber, mit und ohne CCD.

Unterwegs behoben

  • Bei 2000 px/s steckt ein Body einen Step später 13 px in einer 8 px dünnen Plattform. Eine Landung hält jetzt, solange sich beide überlappen.
  • Ein schlafender Body fiel bei Ignore one-way nicht, er wird jetzt geweckt.
  • Altfehler: Im Play hatten alle Rapier-Collider einer Tilemap die Form ihrer letzten Zelle. Behoben, Regressionstest ist ohne Fix rot.

Tests

  • tests/core/one-way.test.ts, gegen beide Systeme (Move/Editor-Run und Rapier).
  • e2e-Modul 43 queries: Inspector-Schalter, Landen und Durchfallen mit Move, Dynamic Body von unten durch.
  • Lokal: npm run check → 109 Files, 1560 Tests, CHECK EXIT 0; GM_STEPS=39-43 → ALL PASSED – 99 checks. CI zu 6a31f5e lief beim Schreiben noch, die Summe 1073 ist gerechnet — Nachtrag folgt.

Folge-Issue

Der Toleranzwinkel als Feld (Punkt 2) steht in #159.

Umgesetzt in `6a31f5e`, zusammen mit #153 und #149. Die Vorschläge aus „Offen“ sind so übernommen, der Spike lief vorab. ## Was gebaut ist - **Collider:** Schalter **One-way** und **Blocks from** (Above/Below/Left/Right). Die Seite dreht und spiegelt mit dem Objekt, der Scene-View zeichnet sie dick. - **Tiles:** Option **One-way (from above)**. One-Way-Zellen verschmelzen nie mit festen Tiles zu einem Rechteck. - **Regel:** Blockiert wird nur ein Kontakt, dessen Normale höchstens 45° von der blockierenden Seite abweicht. Von unten und von der Seite geht es durch, was schon drinsteckt, fällt durch. Eine Funktion (`landsOn`) für `Move`, Raycast und Rapier. - **Node `Ignore one-way`** (Objekt, Sekunden): Durchfallen für eine Dauer. ## Spike Rapier (gemessen bei 60 Hz) - Der Hook darf die Engine nicht anfassen: schon `body.translation()` darin wirft, der Fehler wird verschluckt und das Paar kollidiert mit gar nichts. Die Entscheidung fällt deshalb **vor** jedem Step, der Hook liest sie nur. - Landung: 1,9 px Einsinken, danach Ruhe bei 0,009 px. Von unten durch → landet oben. Seitlich → fährt durch. 2000 px/s → landet sauber, mit und ohne CCD. ## Unterwegs behoben - Bei 2000 px/s steckt ein Body einen Step später 13 px in einer 8 px dünnen Plattform. Eine Landung hält jetzt, solange sich beide überlappen. - Ein schlafender Body fiel bei `Ignore one-way` nicht, er wird jetzt geweckt. - **Altfehler:** Im Play hatten alle Rapier-Collider einer Tilemap die Form ihrer letzten Zelle. Behoben, Regressionstest ist ohne Fix rot. ## Tests - `tests/core/one-way.test.ts`, gegen beide Systeme (Move/Editor-Run und Rapier). - e2e-Modul 43 `queries`: Inspector-Schalter, Landen und Durchfallen mit `Move`, Dynamic Body von unten durch. - Lokal: `npm run check` → 109 Files, 1560 Tests, `CHECK EXIT 0`; `GM_STEPS=39-43` → `ALL PASSED – 99 checks`. CI zu `6a31f5e` lief beim Schreiben noch, die Summe 1073 ist gerechnet — Nachtrag folgt. ## Folge-Issue Der Toleranzwinkel als Feld (Punkt 2) steht in #159.
admin moved this to Done in Development on 2026-09-19 11:53:39 +02:00
Author
Owner

Nachtrag CI zu 6a31f5e: grün, selbst in den Job-Logs nachgelesen. Gate (Task 144): 109 Testdateien grün, CHECK EXIT 0. Suite (Task 145): ALL PASSED – 1073 checks – die Summe 1073 in README und Wiki ist damit gemessen. Smokes 24 / 19 / 21, alle grün.

**Nachtrag CI zu `6a31f5e`:** grün, selbst in den Job-Logs nachgelesen. Gate (Task 144): 109 Testdateien grün, `CHECK EXIT 0`. Suite (Task 145): `ALL PASSED – 1073 checks` – die Summe 1073 in README und Wiki ist damit gemessen. Smokes 24 / 19 / 21, alle grün.
Sign in to join this conversation.
2 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: meleki/feuerwerk#155