Tilemap: Kollisionsgröße pro Tile, mit den Nachbarn verschmolzen (Kollision v2) #140

Closed
opened 2026-09-18 09:48:24 +02:00 by meleki · 2 comments
Owner

Folge-Issue zu #132 (Stufe 2 der Tilemap-Kollision, Stufe 3, das Polygon pro Frame, steht in #141).

Befund

  • Seit #132 blockiert eine Zelle mit einem Tile, das Solid hat, immer mit ihrer vollen Zellfläche. Die blockierenden Zellen werden zeilenweise zu Rechtecken zusammengefasst (mergeCells, src/core/model/tilemapCollision.ts:60) und als box an das CollisionSystem gegeben (cellRectShape, ebd. :115).
  • Das Zusammenfassen setzt voraus, dass jede Zelle ihre Fläche ganz füllt: Nur dann stoßen zwei Nachbarn lückenlos aneinander. Eine Größe pro Tile gibt es noch nicht.

Zu bauen

  • Neue Property am Tile: Collision scale (Vorgabe 1.0). Sie bestimmt, wie groß die Kollisionsbox einer Zelle im Verhältnis zur Zelle ist. Kleiner als 1 lässt Figuren näher heran (z. B. ein Busch, eine schmale Säule), größer als 1 hält sie weiter weg.
  • Verschmelzen mit den Nachbarn, vor allem bei < 1.0: Eingezogen wird nur an den Seiten, an denen kein blockierender Nachbar liegt. Zwischen zwei blockierenden Zellen bleibt die Seite bündig, es entsteht also keine Lücke, durch die eine Figur schlüpft. Eine Wand aus zwanzig Busch-Zellen ist dann ein schmaleres Rechteck, keine Perlenkette aus zwanzig Einzelboxen.
  • Das Zusammenfassen berücksichtigt den Einzug: Zu einem Rechteck verschmolzen wird nur, was denselben Einzug an denselben Seiten hat. Die Formen bleiben so wenige wie möglich, und das Gleiten an Nahtstellen (Test aus #132) bleibt heil.
  • Die Umrandung im Scene-View zeigt die eingezogenen Formen.
  • Tests: Unit-Tests für Einzug und Verschmelzen (einzelne Zelle, Reihe, L-Form mit Innenecke, zwei Tiles mit verschiedenem Wert nebeneinander, Wert > 1) und für das Gleiten ohne Hängenbleiben. e2e: eine Figur kommt an einer verkleinerten Zelle näher heran als an einer vollen.

Offen (mit Vorschlag)

  • Ein Wert oder Breite/Höhe getrennt? Vorschlag: ein Wert, zentriert in der Zelle. Getrennte Achsen und ein Anker (z. B. unten bündig für Wände in der Draufsicht) erst bei Bedarf.
  • Nachbarn mit anderem Wert: Liegen zwei blockierende Tiles mit verschiedenem Wert nebeneinander, bleibt die gemeinsame Seite trotzdem bündig? Vorschlag: ja, jede blockierende Nachbarzelle zählt als „dicht", egal welcher Wert. Sonst entsteht genau die Lücke, die das Issue vermeiden will.
  • Innenecken einer L-Form: Wird dort die Ecke der eingezogenen Form sauber geschlossen? Vorschlag: ja, über die Zerlegung in Rechtecke. Das kann ein Rechteck mehr ergeben als bei 1.0, das ist in Ordnung.
  • Diagonale Nachbarn (nur über die Ecke verbunden): Vorschlag: zählen nicht als verbunden, die Ecke bleibt eingezogen. Sonst entsteht ein Durchgang, der optisch zu ist, oder umgekehrt eine Wand, die optisch offen ist.
Folge-Issue zu #132 (Stufe 2 der Tilemap-Kollision, Stufe 3, das Polygon pro Frame, steht in #141). ## Befund - Seit #132 blockiert eine Zelle mit einem Tile, das **Solid** hat, immer mit ihrer **vollen Zellfläche**. Die blockierenden Zellen werden zeilenweise zu Rechtecken zusammengefasst (`mergeCells`, `src/core/model/tilemapCollision.ts:60`) und als `box` an das `CollisionSystem` gegeben (`cellRectShape`, ebd. `:115`). - Das Zusammenfassen setzt voraus, dass jede Zelle ihre Fläche ganz füllt: Nur dann stoßen zwei Nachbarn lückenlos aneinander. Eine Größe pro Tile gibt es noch nicht. ## Zu bauen - Neue Property am Tile: **Collision scale** (Vorgabe `1.0`). Sie bestimmt, wie groß die Kollisionsbox einer Zelle im Verhältnis zur Zelle ist. Kleiner als 1 lässt Figuren näher heran (z. B. ein Busch, eine schmale Säule), größer als 1 hält sie weiter weg. - **Verschmelzen mit den Nachbarn**, vor allem bei `< 1.0`: Eingezogen wird nur an den Seiten, an denen **kein** blockierender Nachbar liegt. Zwischen zwei blockierenden Zellen bleibt die Seite bündig, es entsteht also keine Lücke, durch die eine Figur schlüpft. Eine Wand aus zwanzig Busch-Zellen ist dann ein schmaleres Rechteck, keine Perlenkette aus zwanzig Einzelboxen. - Das Zusammenfassen berücksichtigt den Einzug: Zu einem Rechteck verschmolzen wird nur, was denselben Einzug an denselben Seiten hat. Die Formen bleiben so wenige wie möglich, und das Gleiten an Nahtstellen (Test aus #132) bleibt heil. - Die Umrandung im Scene-View zeigt die eingezogenen Formen. - Tests: Unit-Tests für Einzug und Verschmelzen (einzelne Zelle, Reihe, L-Form mit Innenecke, zwei Tiles mit verschiedenem Wert nebeneinander, Wert `> 1`) und für das Gleiten ohne Hängenbleiben. e2e: eine Figur kommt an einer verkleinerten Zelle näher heran als an einer vollen. ## Offen (mit Vorschlag) - **Ein Wert oder Breite/Höhe getrennt?** Vorschlag: **ein Wert**, zentriert in der Zelle. Getrennte Achsen und ein Anker (z. B. unten bündig für Wände in der Draufsicht) erst bei Bedarf. - **Nachbarn mit anderem Wert:** Liegen zwei blockierende Tiles mit verschiedenem Wert nebeneinander, bleibt die gemeinsame Seite trotzdem bündig? Vorschlag: **ja**, jede blockierende Nachbarzelle zählt als „dicht", egal welcher Wert. Sonst entsteht genau die Lücke, die das Issue vermeiden will. - **Innenecken einer L-Form:** Wird dort die Ecke der eingezogenen Form sauber geschlossen? Vorschlag: ja, über die Zerlegung in Rechtecke. Das kann ein Rechteck mehr ergeben als bei `1.0`, das ist in Ordnung. - **Diagonale Nachbarn** (nur über die Ecke verbunden): Vorschlag: zählen **nicht** als verbunden, die Ecke bleibt eingezogen. Sonst entsteht ein Durchgang, der optisch zu ist, oder umgekehrt eine Wand, die optisch offen ist.
admin added this to the Development project 2026-09-18 09:49:51 +02:00
admin moved this to Next in Development on 2026-09-18 12:20:18 +02:00
  • Ein Wert oder Breite/Höhe getrennt? Vorschlag: ein Wert - passt
  • Nachbarn mit anderem Wert: gemeinsame Seite trotzdem bündig? Vorschlag: ja - passt
  • Innenecken einer L-Form: Vorschlag, ja - passt
  • Diagonale Nachbarn: Vorschlag: zählen nicht als verbunden - passt
- Ein Wert oder Breite/Höhe getrennt? Vorschlag: ein Wert - passt - Nachbarn mit anderem Wert: gemeinsame Seite trotzdem bündig? Vorschlag: ja - passt - Innenecken einer L-Form: Vorschlag, ja - passt - Diagonale Nachbarn: Vorschlag: zählen nicht als verbunden - passt
Author
Owner

Umgesetzt in bffa508 (Wiki e5e9f65), nach den Antworten in #issuecomment-1083 — alle vier Vorschläge gelten.

Was sich ändert

  • Neue Tile-Property „Collision scale“ unter Solid: Vorgabe 1, Bereich 0.1–2, Schritt 0.05. Normale Property mit Undo, Gold-Markierung und Speichern; ein Wert außerhalb des Bereichs in der Datei wird beim Lesen begrenzt.
  • Kleiner als 1: Jede blockierende Zelle wird in ein 3 × 3-Raster geteilt (src/core/model/tilemapCollision.ts):
    • Die Mitte blockiert immer.
    • Ein Randstreifen blockiert nur, wenn auf dieser Seite ein blockierender Nachbar liegt, egal mit welchem Wert.
    • Eine Ecke blockiert nur, wenn beide Seiten und die Zelle über Eck blockieren.
    • Ergebnis: eine Wand aus Büschen wird schmaler, aber ohne Lücken; die Innenecke eines L schließt sauber; Zellen, die sich nur diagonal berühren, bleiben getrennt.
  • Größer als 1: Die Zelle wächst an ihren offenen Seiten.
  • Mehrere Layer mit blockierendem Tile auf einer Zelle: Es gilt der größte Wert (eigene Entscheidung, das Issue sagte dazu nichts).
  • Zusammenfassen geht jetzt über Bruchteile von Zellen. Bei 1 kommt exakt dasselbe heraus wie vorher, die 16 alten Tests laufen unverändert.
  • Die Umrandung im Scene-View zeigt die eingezogenen bzw. vergrößerten Formen, ohne Änderung am Zeichencode.

Tests

  • npm run check: 102 Files, 1380 Tests (+9), CHECK EXIT 0. Build grün.
  • e2e lokal: GM_STEPS=39,40 → 68 Checks, GM_STEPS=1-16,24,25,33,36,38,39,40 → 612 Checks, beide ALL PASSED.
  • 9 neue Unit-Tests in tests/core/tilemap-collision.test.ts: Einzelzelle, Reihe, Block, L mit Innenecke, gemischte Werte, Wert > 1, Diagonale, die Property (Vorgabe, Grenzen, Speichern/Laden, größter Wert über Layer), Anhalten und Gleiten an einer eingezogenen Wand ohne Hängenbleiben an Nahtstellen, Abstand bei vergrößerter Wand. Dazu ein Vergleich über 20 Zufallsverteilungen gegen eine punktgenaue Vorgabe (Fläche und keine Doppelabdeckung).
  • Gegenprobe mit 4 absichtlichen Fehlern (Diagonale ignoriert, Einzug auch zu Nachbarn, kleinster statt größter Wert, Grenzen nicht angewandt): alle gefangen, Originale per cmp zurück.
  • 2 neue e2e-Checks in Modul 40 (40l): 0.5 im echten Feld eingetippt → Umrandung [{0.25,0.25,2.5,2.5},{4.25,0.25,0.5,0.5}]; die Kiste hält im Spiel bei x = −158 statt −170.
  • Nicht per Screenshot angesehen, belegt über die Rechteckdaten im Check.

Doku

Wiki: Tilemaps.md (Unterabschnitt Collision scale), Tiles.md, Architecture.md, Development.md. Check-Summe 1042 (1040 + 2) in README und Wiki — gerechnet, CI-Bestätigung steht noch aus.

Weiter geht es mit #141 (Kollisions-Polygon pro Frame, v3).

Umgesetzt in bffa508 (Wiki e5e9f65), nach den Antworten in #issuecomment-1083 — alle vier Vorschläge gelten. ## Was sich ändert - **Neue Tile-Property „Collision scale“** unter Solid: Vorgabe 1, Bereich 0.1–2, Schritt 0.05. Normale Property mit Undo, Gold-Markierung und Speichern; ein Wert außerhalb des Bereichs in der Datei wird beim Lesen begrenzt. - **Kleiner als 1:** Jede blockierende Zelle wird in ein 3 × 3-Raster geteilt (`src/core/model/tilemapCollision.ts`): - Die Mitte blockiert immer. - Ein Randstreifen blockiert nur, wenn auf dieser Seite ein blockierender Nachbar liegt, egal mit welchem Wert. - Eine Ecke blockiert nur, wenn beide Seiten **und** die Zelle über Eck blockieren. - Ergebnis: eine Wand aus Büschen wird schmaler, aber ohne Lücken; die Innenecke eines L schließt sauber; Zellen, die sich nur diagonal berühren, bleiben getrennt. - **Größer als 1:** Die Zelle wächst an ihren offenen Seiten. - **Mehrere Layer mit blockierendem Tile auf einer Zelle:** Es gilt der größte Wert (eigene Entscheidung, das Issue sagte dazu nichts). - **Zusammenfassen** geht jetzt über Bruchteile von Zellen. Bei 1 kommt exakt dasselbe heraus wie vorher, die 16 alten Tests laufen unverändert. - Die Umrandung im Scene-View zeigt die eingezogenen bzw. vergrößerten Formen, ohne Änderung am Zeichencode. ## Tests - `npm run check`: 102 Files, 1380 Tests (+9), CHECK EXIT 0. Build grün. - e2e lokal: `GM_STEPS=39,40` → 68 Checks, `GM_STEPS=1-16,24,25,33,36,38,39,40` → 612 Checks, beide ALL PASSED. - **9 neue Unit-Tests** in `tests/core/tilemap-collision.test.ts`: Einzelzelle, Reihe, Block, L mit Innenecke, gemischte Werte, Wert > 1, Diagonale, die Property (Vorgabe, Grenzen, Speichern/Laden, größter Wert über Layer), Anhalten und Gleiten an einer eingezogenen Wand ohne Hängenbleiben an Nahtstellen, Abstand bei vergrößerter Wand. Dazu ein Vergleich über 20 Zufallsverteilungen gegen eine punktgenaue Vorgabe (Fläche und keine Doppelabdeckung). - **Gegenprobe** mit 4 absichtlichen Fehlern (Diagonale ignoriert, Einzug auch zu Nachbarn, kleinster statt größter Wert, Grenzen nicht angewandt): alle gefangen, Originale per `cmp` zurück. - **2 neue e2e-Checks in Modul 40 (40l):** 0.5 im echten Feld eingetippt → Umrandung `[{0.25,0.25,2.5,2.5},{4.25,0.25,0.5,0.5}]`; die Kiste hält im Spiel bei x = −158 statt −170. - Nicht per Screenshot angesehen, belegt über die Rechteckdaten im Check. ## Doku Wiki: `Tilemaps.md` (Unterabschnitt *Collision scale*), `Tiles.md`, `Architecture.md`, `Development.md`. Check-Summe **1042** (1040 + 2) in README und Wiki — gerechnet, CI-Bestätigung steht noch aus. Weiter geht es mit #141 (Kollisions-Polygon pro Frame, v3).
admin moved this to Persistent in Development on 2026-09-18 15:01:28 +02:00
admin moved this to Done in Development on 2026-09-18 15:01:34 +02:00
Sign in to join this conversation.
2 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: meleki/feuerwerk#140