wenn ich diese collision drinnen hab, dann fängt es an zu ruckeln (20 fps oder sogar weniger) - das ist aber erst vor kurzem so, das hatte ich vor ein paar commits davor noch nicht das problem. Es ruckelt aber auch nur dann wenn sich ein objekt per move tatsächlich bewegt. wenn man still stehen bleibt wieder volle 60fps
<img width="648" alt="image.png" src="attachments/981fe645-0bfb-4396-84ae-000031fcf146">
wenn ich diese collision drinnen hab, dann fängt es an zu ruckeln (20 fps oder sogar weniger) - das ist aber erst vor kurzem so, das hatte ich vor ein paar commits davor noch nicht das problem. Es ruckelt aber auch nur dann wenn sich ein objekt per move tatsächlich bewegt. wenn man still stehen bleibt wieder volle 60fps
Das Bild im Issue zeigt Kollisions-Umrisse im Texture-Tab — also Kacheln, die mit einem Polygon blockieren. Ein Umriss ist pro gemalter Zelle ein eigenes Polygon, jedes in konvexe Teile zerlegt. Wo dieselbe Karte aus schlichten soliden Tiles ein paar gemergte Rechtecke ist, sind es hier hunderte Solids unter einem Entity.
Dazu kam: Move hat jedes dieser Solids in Weltkoordinaten umgerechnet und exakt geprüft — auch das auf der anderen Seite der Karte. Gemessen an einer Karte mit 20 × 20 Umriss-Zellen (Node-Benchmark, kein Browser):
Karte
vorher
nachher
20 × 20, Umriss mit 16 Ecken
148 ms je Move
0,26 ms
20 × 20, Umriss mit 8 Ecken
34 ms
0,27 ms
60 × 60, Umriss mit 8 Ecken
20 ms
3,5 ms
20 × 20, schlichte solide Tiles
0,4 ms
0,4 ms
Das passt auf das Symptom: im Stillstand ruft niemand Move auf, also 60 fps; sobald sich etwas bewegt, kostet ein einziger Aufruf mehr als ein ganzer Frame.
Keine Regression der letzten Commits. Derselbe Benchmark in Worktrees gegen 947a6b1 (#141), 6a31f5e, 4f55f85 (#152) und ae3aa7d gibt überall dieselben 85–148 ms. Die Kosten stecken seit den Umriss-Kacheln (#141) drin und fallen auf, sobald man Umrisse zeichnet und etwas bewegt.
Was gebaut ist
Eine Broad Phase in zwei Stufen, die nichts entscheidet — sie lässt nur weg, was gar nicht erreichbar ist:
Reichweite eines Moves: Ein Move kommt nie weiter von seinem Startpunkt weg, als er lang ist (Gleiten kürzt den Rest immer). Also: Rechteck um jeden Collider des Movers, gewachsen um die Move-Länge — was das nicht berührt, wird nicht geprüft. Ein Strahl nimmt das Rechteck um sich selbst.
Zellen statt Shapes: Das Rechteck wird per invert(m) in die eigenen Einheiten der Karte zurückgerechnet und in Zellkoordinaten umgerechnet (cellsWithin), eine gedrehte Karte durch ihre eigenen Achsen. Eine ferne Zelle wird damit gar nicht erst in Weltform gebracht — solidShapesOf war O(Zellen) pro Aufruf, das war der zweite Kostenblock und der Grund, warum der 60 × 60-Fall noch wuchs.
Auch die Erkennung:CollisionSystem.overlaps (Editor-Run, jeder Frame, jedes Paar) fragt eine Karte nur nach den Zellen in Reichweite der Gegenseite. Bei zwei Karten engt nichts ein.
Nebenbei fällt im Move ein Hindernis schon beim Aufbau weg, wenn seine Ebene die des Movers nicht trifft (#152) — vorher erst im Inneren der Schleife.
Neue reine Funktionen: shapeBounds / grownBounds / boundsMeet in collider.ts, cellsWithin / cellsMeet in tilemapCollision.ts.
11 neue Unit-Tests in tests/core/broadphase.test.ts: die Rechtecke der vier Formen (gedrehte Box misst ihren Schrägzug), Berühren zählt als Treffen, cellsWithin gerade und um 90° gedreht, eine Karte gibt nur die erwarteten Zellen heraus (400 ohne Bereich, < 10 mit), ein 800 Einheiten langer Move aus der Ferne stoppt weiterhin an der ersten Zelle, eine gedrehte Karte blockt dort, wohin ihre eine bemalte Zelle gedreht ist, ein Strahl über die ganze Karte trifft, und der Editor-Run erkennt einen Collider auf einer fernen Zelle.
Vier Gegenproben, alle greifen: Zellfilter aus → der Zähltest fällt; Padding ohne Move-Länge → 4 Move-Tests fallen; Drehung in cellsWithin ignoriert → 3 Tests fallen; falscher Bereich in der Erkennung → der Touching-Test fällt. Originale per cmp zurückgeprüft.
Ein stumpfer Test unterwegs: Die erste gedrehte Karte war vollflächig bemalt — da blockt auch ein falsch berechneter Zellbereich, die Gegenprobe blieb grün. Jetzt ist genau eine Zelle bemalt, und der Test misst, wohin sie sich gedreht hat.
Kein e2e-Check, Check-Summe bleibt 1157. Ein Zeitcheck im Browser wäre unter CI-Last wackelig; die Korrektheit decken die Module 40 und 42 ohnehin ab. Der Geschwindigkeitsbeleg ist der Node-Benchmark oben.
Zwei Dinge, die bleiben
Der Geschwindigkeitsbeleg ist gemessen, aber nicht am echten Projekt. Bitte im eigenen Projekt nachprüfen — das ist die einzige Messung am realen Fall.
Was man berührt, kostet weiter. Ein Objekt, das in vielen Umriss-Zellen steht, wird gegen jede davon getestet, und ein Umriss mit 30 Ecken pro Zelle ist teurer als einer mit acht. Für einen Seeuferverlauf ist das in Ordnung, für jede Zelle eines vollen Sees wäre es Verschwendung; der Hinweis steht jetzt im Wiki unter Tilemaps.
Wiki: Colliders (Reichweite von Move und Strahl), Tilemaps (neuer Abschnitt The cost of outlined cells), Architecture (solids, cellsWithin, shapeBounds).
Umgesetzt in [`eac50e4`](http://localhost:3000/meleki/game-maker/commit/eac50e4), Wiki in [`64a527a`](http://localhost:3000/meleki/game-maker.wiki/commit/64a527a).
## Was die Ursache war
Das Bild im Issue zeigt **Kollisions-Umrisse** im Texture-Tab — also Kacheln, die mit einem Polygon blockieren. Ein Umriss ist **pro gemalter Zelle ein eigenes Polygon**, jedes in konvexe Teile zerlegt. Wo dieselbe Karte aus schlichten soliden Tiles ein paar gemergte Rechtecke ist, sind es hier hunderte Solids unter einem Entity.
Dazu kam: `Move` hat **jedes** dieser Solids in Weltkoordinaten umgerechnet und exakt geprüft — auch das auf der anderen Seite der Karte. Gemessen an einer Karte mit 20 × 20 Umriss-Zellen (Node-Benchmark, kein Browser):
| Karte | vorher | nachher |
|---|---|---|
| 20 × 20, Umriss mit 16 Ecken | **148 ms** je `Move` | **0,26 ms** |
| 20 × 20, Umriss mit 8 Ecken | 34 ms | 0,27 ms |
| 60 × 60, Umriss mit 8 Ecken | 20 ms | 3,5 ms |
| 20 × 20, schlichte solide Tiles | 0,4 ms | 0,4 ms |
Das passt auf das Symptom: im Stillstand ruft niemand `Move` auf, also 60 fps; sobald sich etwas bewegt, kostet ein einziger Aufruf mehr als ein ganzer Frame.
**Keine Regression der letzten Commits.** Derselbe Benchmark in Worktrees gegen `947a6b1` (#141), `6a31f5e`, `4f55f85` (#152) und `ae3aa7d` gibt überall dieselben 85–148 ms. Die Kosten stecken seit den Umriss-Kacheln (#141) drin und fallen auf, sobald man Umrisse zeichnet und etwas bewegt.
## Was gebaut ist
Eine **Broad Phase** in zwei Stufen, die nichts entscheidet — sie lässt nur weg, was gar nicht erreichbar ist:
- **Reichweite eines Moves:** Ein Move kommt nie weiter von seinem Startpunkt weg, als er lang ist (Gleiten kürzt den Rest immer). Also: Rechteck um jeden Collider des Movers, gewachsen um die Move-Länge — was das nicht berührt, wird nicht geprüft. Ein Strahl nimmt das Rechteck um sich selbst.
- **Zellen statt Shapes:** Das Rechteck wird per `invert(m)` in die eigenen Einheiten der Karte zurückgerechnet und in **Zellkoordinaten** umgerechnet (`cellsWithin`), eine gedrehte Karte durch ihre eigenen Achsen. Eine ferne Zelle wird damit gar nicht erst in Weltform gebracht — `solidShapesOf` war O(Zellen) pro Aufruf, das war der zweite Kostenblock und der Grund, warum der 60 × 60-Fall noch wuchs.
- **Auch die Erkennung:** `CollisionSystem.overlaps` (Editor-Run, jeder Frame, jedes Paar) fragt eine Karte nur nach den Zellen in Reichweite der Gegenseite. Bei zwei Karten engt nichts ein.
- Nebenbei fällt im Move ein Hindernis schon beim Aufbau weg, wenn seine **Ebene** die des Movers nicht trifft (#152) — vorher erst im Inneren der Schleife.
Neue reine Funktionen: `shapeBounds` / `grownBounds` / `boundsMeet` in `collider.ts`, `cellsWithin` / `cellsMeet` in `tilemapCollision.ts`.
## Tests
```
./check.sh → 122 Files, 1799 Tests, CHECK EXIT 0 (vorher 121 / 1788)
npm run build → BUILD EXIT 0
GM_STEPS=1-12,17,19,39-43 → ALL PASSED – 546 checks
```
- **11 neue Unit-Tests** in `tests/core/broadphase.test.ts`: die Rechtecke der vier Formen (gedrehte Box misst ihren Schrägzug), Berühren zählt als Treffen, `cellsWithin` gerade und um 90° gedreht, eine Karte gibt nur die erwarteten Zellen heraus (400 ohne Bereich, < 10 mit), ein 800 Einheiten langer Move aus der Ferne stoppt weiterhin an der ersten Zelle, eine gedrehte Karte blockt dort, wohin ihre eine bemalte Zelle gedreht ist, ein Strahl über die ganze Karte trifft, und der Editor-Run erkennt einen Collider auf einer fernen Zelle.
- **Vier Gegenproben, alle greifen:** Zellfilter aus → der Zähltest fällt; Padding ohne Move-Länge → 4 Move-Tests fallen; Drehung in `cellsWithin` ignoriert → 3 Tests fallen; falscher Bereich in der Erkennung → der Touching-Test fällt. Originale per `cmp` zurückgeprüft.
- **Ein stumpfer Test unterwegs:** Die erste gedrehte Karte war vollflächig bemalt — da blockt auch ein falsch berechneter Zellbereich, die Gegenprobe blieb grün. Jetzt ist genau **eine** Zelle bemalt, und der Test misst, wohin sie sich gedreht hat.
- **Kein e2e-Check, Check-Summe bleibt 1157.** Ein Zeitcheck im Browser wäre unter CI-Last wackelig; die Korrektheit decken die Module 40 und 42 ohnehin ab. Der Geschwindigkeitsbeleg ist der Node-Benchmark oben.
## Zwei Dinge, die bleiben
- **Der Geschwindigkeitsbeleg ist gemessen, aber nicht am echten Projekt.** Bitte im eigenen Projekt nachprüfen — das ist die einzige Messung am realen Fall.
- **Was man berührt, kostet weiter.** Ein Objekt, das *in* vielen Umriss-Zellen steht, wird gegen jede davon getestet, und ein Umriss mit 30 Ecken pro Zelle ist teurer als einer mit acht. Für einen Seeuferverlauf ist das in Ordnung, für jede Zelle eines vollen Sees wäre es Verschwendung; der Hinweis steht jetzt im Wiki unter Tilemaps.
Wiki: Colliders (Reichweite von Move und Strahl), Tilemaps (neuer Abschnitt *The cost of outlined cells*), Architecture (`solids`, `cellsWithin`, `shapeBounds`).
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
wenn ich diese collision drinnen hab, dann fängt es an zu ruckeln (20 fps oder sogar weniger) - das ist aber erst vor kurzem so, das hatte ich vor ein paar commits davor noch nicht das problem. Es ruckelt aber auch nur dann wenn sich ein objekt per move tatsächlich bewegt. wenn man still stehen bleibt wieder volle 60fps
Umgesetzt in
eac50e4, Wiki in64a527a.Was die Ursache war
Das Bild im Issue zeigt Kollisions-Umrisse im Texture-Tab — also Kacheln, die mit einem Polygon blockieren. Ein Umriss ist pro gemalter Zelle ein eigenes Polygon, jedes in konvexe Teile zerlegt. Wo dieselbe Karte aus schlichten soliden Tiles ein paar gemergte Rechtecke ist, sind es hier hunderte Solids unter einem Entity.
Dazu kam:
Movehat jedes dieser Solids in Weltkoordinaten umgerechnet und exakt geprüft — auch das auf der anderen Seite der Karte. Gemessen an einer Karte mit 20 × 20 Umriss-Zellen (Node-Benchmark, kein Browser):MoveDas passt auf das Symptom: im Stillstand ruft niemand
Moveauf, also 60 fps; sobald sich etwas bewegt, kostet ein einziger Aufruf mehr als ein ganzer Frame.Keine Regression der letzten Commits. Derselbe Benchmark in Worktrees gegen
947a6b1(#141),6a31f5e,4f55f85(#152) undae3aa7dgibt überall dieselben 85–148 ms. Die Kosten stecken seit den Umriss-Kacheln (#141) drin und fallen auf, sobald man Umrisse zeichnet und etwas bewegt.Was gebaut ist
Eine Broad Phase in zwei Stufen, die nichts entscheidet — sie lässt nur weg, was gar nicht erreichbar ist:
invert(m)in die eigenen Einheiten der Karte zurückgerechnet und in Zellkoordinaten umgerechnet (cellsWithin), eine gedrehte Karte durch ihre eigenen Achsen. Eine ferne Zelle wird damit gar nicht erst in Weltform gebracht —solidShapesOfwar O(Zellen) pro Aufruf, das war der zweite Kostenblock und der Grund, warum der 60 × 60-Fall noch wuchs.CollisionSystem.overlaps(Editor-Run, jeder Frame, jedes Paar) fragt eine Karte nur nach den Zellen in Reichweite der Gegenseite. Bei zwei Karten engt nichts ein.Neue reine Funktionen:
shapeBounds/grownBounds/boundsMeetincollider.ts,cellsWithin/cellsMeetintilemapCollision.ts.Tests
tests/core/broadphase.test.ts: die Rechtecke der vier Formen (gedrehte Box misst ihren Schrägzug), Berühren zählt als Treffen,cellsWithingerade und um 90° gedreht, eine Karte gibt nur die erwarteten Zellen heraus (400 ohne Bereich, < 10 mit), ein 800 Einheiten langer Move aus der Ferne stoppt weiterhin an der ersten Zelle, eine gedrehte Karte blockt dort, wohin ihre eine bemalte Zelle gedreht ist, ein Strahl über die ganze Karte trifft, und der Editor-Run erkennt einen Collider auf einer fernen Zelle.cellsWithinignoriert → 3 Tests fallen; falscher Bereich in der Erkennung → der Touching-Test fällt. Originale percmpzurückgeprüft.Zwei Dinge, die bleiben
Wiki: Colliders (Reichweite von Move und Strahl), Tilemaps (neuer Abschnitt The cost of outlined cells), Architecture (
solids,cellsWithin,shapeBounds).