Tilemap: Kollision #132

Closed
opened 2026-09-18 08:03:55 +02:00 by meleki · 1 comment
Owner

Folge-Issue zu #128. Dort ist die Kollision bewusst weggelassen worden: Eine Tilemap ist bisher reine Grafik, und Figuren laufen hindurch.

Befund

  • Das CollisionSystem (src/runtime/systems/CollisionSystem.ts) prüft nur Entities vom Typ collider paarweise gegeneinander (enter/stay/exit). Der Bewegungsstopp (exitDistance) läuft über dieselben Formen. Die Formen sind box und circle (src/core/properties/collider.ts:14).
  • Eine Tilemap liefert bisher keine einzige Form. Pro Layer kennt sie nur "x,y" → Tile-Id (src/core/model/tilemap.ts:6).

Zu bauen

  • Die Tilemap liefert Kollisionsformen an das CollisionSystem, und zwar zu Rechtecken zusammengefasst, nicht eine Box pro Zelle.
  • Die Formen folgen der Transformation der platzierten Tilemap (Position, Drehung, Skalierung) wie jeder Collider.
  • Bewegung stoppt an blockierenden Zellen. Behaviors bekommen die üblichen Kollisions-Events mit der Tilemap als Gegenüber.
  • Malen im Editor und Änderungen zur Laufzeit (siehe #135) bauen die Formen neu.
  • Die Kollisionsformen lassen sich im Scene-View anzeigen, so wie die bisherigen Collider.
  • Tests: Unit-Tests für das Zusammenfassen (Rechteckzahl, Löcher, Ränder), e2e für eine Figur, die an einer Wand stehen bleibt.

Offen (mit Vorschlag)

  • Was blockiert? Ein Schalter solid pro Tile (gilt überall, wo das Tile liegt) oder pro Layer (ein ganzer Layer blockiert). Vorschlag: pro Tile. Wasser oder Wand ist eine Eigenschaft des Tiles, nicht der Ebene.
  • Unsichtbare Layer: Blockiert ein ausgeblendeter Layer weiter? Vorschlag: ja. Sichtbarkeit ist Darstellung, eine unsichtbare Kollisionsebene ist ein gängiges Muster.
  • Trigger: Soll ein Tile auch nur melden statt blockieren (z. B. Wasser → Event)? Vorschlag: erst einmal nur solid, Trigger-Tiles bei Bedarf nachziehen.
Folge-Issue zu #128. Dort ist die Kollision bewusst weggelassen worden: Eine Tilemap ist bisher reine Grafik, und Figuren laufen hindurch. ## Befund - Das `CollisionSystem` (`src/runtime/systems/CollisionSystem.ts`) prüft nur Entities vom Typ `collider` paarweise gegeneinander (enter/stay/exit). Der Bewegungsstopp (`exitDistance`) läuft über dieselben Formen. Die Formen sind `box` und `circle` (`src/core/properties/collider.ts:14`). - Eine Tilemap liefert bisher keine einzige Form. Pro Layer kennt sie nur `"x,y"` → Tile-Id (`src/core/model/tilemap.ts:6`). ## Zu bauen - Die Tilemap liefert Kollisionsformen an das `CollisionSystem`, und zwar **zu Rechtecken zusammengefasst**, nicht eine Box pro Zelle. - Die Formen folgen der Transformation der platzierten Tilemap (Position, Drehung, Skalierung) wie jeder Collider. - Bewegung stoppt an blockierenden Zellen. Behaviors bekommen die üblichen Kollisions-Events mit der Tilemap als Gegenüber. - Malen im Editor und Änderungen zur Laufzeit (siehe #135) bauen die Formen neu. - Die Kollisionsformen lassen sich im Scene-View anzeigen, so wie die bisherigen Collider. - Tests: Unit-Tests für das Zusammenfassen (Rechteckzahl, Löcher, Ränder), e2e für eine Figur, die an einer Wand stehen bleibt. ## Offen (mit Vorschlag) - **Was blockiert?** Ein Schalter `solid` pro Tile (gilt überall, wo das Tile liegt) oder pro Layer (ein ganzer Layer blockiert). Vorschlag: **pro Tile**. Wasser oder Wand ist eine Eigenschaft des Tiles, nicht der Ebene. - **Unsichtbare Layer:** Blockiert ein ausgeblendeter Layer weiter? Vorschlag: ja. Sichtbarkeit ist Darstellung, eine unsichtbare Kollisionsebene ist ein gängiges Muster. - **Trigger:** Soll ein Tile auch nur melden statt blockieren (z. B. Wasser → Event)? Vorschlag: erst einmal nur `solid`, Trigger-Tiles bei Bedarf nachziehen.
admin added this to the Development project 2026-09-18 08:26:24 +02:00
admin moved this to Next in Development on 2026-09-18 08:26:32 +02:00
Author
Owner

Umgesetzt in 4c411c1, Wiki b6f0e5b.

Entscheidungen

Die Rückfrage „Was blockiert?" ist ohne Antwort abgelaufen (MCP-Timeout), gebaut wurde deshalb nach den Vorschlägen oben, vom User danach bestätigt:

  • Schalter Solid am Tile, Vorgabe aus.
  • Ausgeblendete Layer blockieren weiter.
  • Noch keine Trigger-Tiles.

Was gebaut ist

  • Solid ist eine normale Property am Tile (Undo, Gold-Markierung, gespeichert).
  • Die blockierenden Zellen aller Layer werden Zeile für Zeile zu Rechtecken zusammengefasst (src/core/model/tilemapCollision.ts). Gezählt werden nur Zellen innerhalb des Rasters, also dieselben, die gezeichnet werden.
  • Die platzierte Tilemap ist ein eigener Körper im CollisionSystem: Move mit Collide with solids hält an ihr und gleitet entlang, die Rechtecke folgen Position, Drehung und Skalierung, OnCollisionEnter/Stay/Exit und Is touching melden die Tilemap als other. Zwei Tilemaps kollidieren nie miteinander. Zur Laufzeit gemalte Zellen zählen ab dem nächsten Tick.
  • Scene-View: Die Rechtecke werden wie Collider grün umrandet. Im Game-View sind sie unsichtbar.
  • Nebenbei: moveAndSlide rechnet die Formen der Hindernisse einmal pro Aufruf statt pro Teilschritt.

Tests (lokal)

npm run check                                          → 98 Files, 1339 Tests (vorher 1323), CHECK EXIT 0
npm run build                                          → BUILD EXIT 0
GM_STEPS=1-16,24,25,33,36,38,39,40 (frisch geseedet)   → ALL PASSED – 591 checks
  • 16 neue Unit-Tests (tests/core/tilemap-collision.test.ts): Zusammenfassen (Lauf, Block, Säule, L-Form, Ring mit Loch, negative Zellen, 20 Zufallsverteilungen „jede Zelle genau einmal"), welche Zellen blockieren, Lage in der Welt, bündiges Anhalten auch bei großen Schritten, Gleiten über Nahtstellen ohne Hängenbleiben, Transformation, Malen zur Laufzeit, Events, die Nodes im echten Graph.
  • Gegenprobe mit 5 absichtlichen Fehlern, jeder gefangen (versteckte Layer ignoriert, Zeilenlücke nicht getrennt, Skalierung vergessen, Cache nie geleert, Tilemaps kollidieren miteinander).
  • e2e Modul 40, Schritt 40l (+4 Checks): Solid im Inspector einschalten, Umrandung [{0,0,3,3},{4,0,1,1}], eine Kiste hält im Spiel bei x = −170, ohne Solid läuft sie durch.
  • Befund beim Testen (mein Prüfcode): 40l nutzte zuerst die Kiste o1, die in der vollen Kette schon entfernt ist. 40l legt jetzt eine eigene Kiste an und räumt sie weg.

Doku

Wiki: neuer Abschnitt Collision in Tilemaps.md, Solid-Schalter in Tiles.md, tilemapCollision in Architecture.md. Check-Summe 1021 in README und Development.md (gerechnet, CI steht noch aus).

Folge-Issues

  • #140 Kollisionsgröße pro Tile, an den Seiten zu blockierenden Nachbarn bündig (v2)
  • #141 Kollisions-Polygon pro Frame im Texture-Tab, genutzt von der Tilemap (v3)
  • Trigger-Tiles bleiben offen, dafür gibt es noch kein Issue.
Umgesetzt in 4c411c1, Wiki b6f0e5b. ## Entscheidungen Die Rückfrage „Was blockiert?" ist ohne Antwort abgelaufen (MCP-Timeout), gebaut wurde deshalb nach den Vorschlägen oben, vom User danach bestätigt: - Schalter **Solid** am Tile, Vorgabe aus. - Ausgeblendete Layer blockieren weiter. - Noch keine Trigger-Tiles. ## Was gebaut ist - **Solid** ist eine normale Property am Tile (Undo, Gold-Markierung, gespeichert). - Die blockierenden Zellen aller Layer werden Zeile für Zeile zu Rechtecken zusammengefasst (`src/core/model/tilemapCollision.ts`). Gezählt werden nur Zellen innerhalb des Rasters, also dieselben, die gezeichnet werden. - Die platzierte Tilemap ist ein eigener Körper im `CollisionSystem`: **Move** mit *Collide with solids* hält an ihr und gleitet entlang, die Rechtecke folgen Position, Drehung und Skalierung, **OnCollisionEnter/Stay/Exit** und **Is touching** melden die Tilemap als `other`. Zwei Tilemaps kollidieren nie miteinander. Zur Laufzeit gemalte Zellen zählen ab dem nächsten Tick. - Scene-View: Die Rechtecke werden wie Collider grün umrandet. Im Game-View sind sie unsichtbar. - Nebenbei: `moveAndSlide` rechnet die Formen der Hindernisse einmal pro Aufruf statt pro Teilschritt. ## Tests (lokal) ``` npm run check → 98 Files, 1339 Tests (vorher 1323), CHECK EXIT 0 npm run build → BUILD EXIT 0 GM_STEPS=1-16,24,25,33,36,38,39,40 (frisch geseedet) → ALL PASSED – 591 checks ``` - 16 neue Unit-Tests (`tests/core/tilemap-collision.test.ts`): Zusammenfassen (Lauf, Block, Säule, L-Form, Ring mit Loch, negative Zellen, 20 Zufallsverteilungen „jede Zelle genau einmal"), welche Zellen blockieren, Lage in der Welt, bündiges Anhalten auch bei großen Schritten, Gleiten über Nahtstellen ohne Hängenbleiben, Transformation, Malen zur Laufzeit, Events, die Nodes im echten Graph. - Gegenprobe mit 5 absichtlichen Fehlern, jeder gefangen (versteckte Layer ignoriert, Zeilenlücke nicht getrennt, Skalierung vergessen, Cache nie geleert, Tilemaps kollidieren miteinander). - e2e Modul 40, Schritt 40l (+4 Checks): Solid im Inspector einschalten, Umrandung `[{0,0,3,3},{4,0,1,1}]`, eine Kiste hält im Spiel bei x = −170, ohne Solid läuft sie durch. - Befund beim Testen (mein Prüfcode): 40l nutzte zuerst die Kiste `o1`, die in der vollen Kette schon entfernt ist. 40l legt jetzt eine eigene Kiste an und räumt sie weg. ## Doku Wiki: neuer Abschnitt *Collision* in `Tilemaps.md`, Solid-Schalter in `Tiles.md`, `tilemapCollision` in `Architecture.md`. Check-Summe **1021** in README und `Development.md` (gerechnet, CI steht noch aus). ## Folge-Issues - #140 Kollisionsgröße pro Tile, an den Seiten zu blockierenden Nachbarn bündig (v2) - #141 Kollisions-Polygon pro Frame im Texture-Tab, genutzt von der Tilemap (v3) - Trigger-Tiles bleiben offen, dafür gibt es noch kein Issue.
admin moved this to Done in Development on 2026-09-18 09:51:12 +02:00
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: meleki/feuerwerk#132