Files
Laby/docs/06_Refactoring_Game.md
T
Lila-Kuh ea2a2f2cbf
Aegis CI / audit (push) Failing after 1m7s
Aegis CI / selfplay (push) Skipped
Aegis CI / perf (push) Failing after 3s
Aegis CI / report (push) Failing after 2s
Initial project version
2026-08-29 01:45:19 +02:00

123 lines
5.7 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 06 Refactoring & Performance (Advisory für `Aegis-Labyrinth.html`)
> **Advisory-Dokumentation** Empfehlungen, `Aegis-Labyrinth.html` (~707 KB, Single-File-Game)
> sauberer, schneller und wartbarer zu machen, **ohne** das sichtbare Spielverhalten zu ändern.
>
> ⚠️ Diese Datei gehört **nicht** zur Test-Suite selbst sie ist ein **Beratungs-Backlog** für die
> extern eingelegte Game-Datei. Die Suite (`tests/*`) prüft nur, dass Änderungen daran die 3 Audits
> nicht brechen (→ `02_Audits.md`) und die Performance nicht einbricht (→ `04_Resultate_Report.md`).
---
## 1. Ist-Zustand (kurz)
| Aspekt | Status |
|---|---|
| Struktur | Eine HTML-Datei: CSS + ~4.500 Zeilen JS + UI |
| Rendering | Canvas 2D, `requestAnimationFrame`-Loop, Partikel-/Projectile-Arrays |
| State | Globale `Game`-Objekte + freie Funktionen im Global Scope |
| Persistenz | `localStorage` (Skilltree, Audio, Unlocks, Custom Levels) |
| Debug | `window.__AEGIS_DEBUG__` (Audits, State, Audio, Levels) → `01_Debug_API.md` |
| Tests | 3 eingebauten Audits + externe Suite (`tests/`) |
| Bundle | Base64-PNG-Logo (ca. 350 KB) inline bereits optimiert |
---
## 2. Priorisierte Maßnahmen (P0 = mach heute, P2 = Backlog)
### P0 Hohe Wirkung, geringes Risiko
| # | Maßnahme | Wo | Erwarteter Effekt |
|---|---|---|---|
| 1 | **Partikel-/Projectile-Pooling** statt `push`/`filter` pro Frame | `updateProjectiles`, `updateEnemies`, Partikel-Arrays | 3050 % GC-Pressure |
| 2 | **Spatial Hash** (Grid 32 px) für `towerFire`-Zielwahl (heute O(N·M) alle Paare) | `towerFire(t,dt)` | 5080 % CPU bei 50+ Türmen |
| 3 | **Canvas-Layer-Trennung**: `bgCanvas`, `midCanvas`, `fxCanvas` + `ctx.imageSmoothingEnabled=false` | `render()` | Weniger Re-Draws, stabilerer 60 fps |
| 4 | **Offscreen-Canvas für statische Route** (1× pro Level zeichnen, dann `drawImage`) | Route-Drawing | 1020 % Framezeit |
| 5 | **Delta-Clamp**: `dt = Math.min(dt, 1/30)` am Loop-Start | Game-Loop | Keine Explosionen nach Tab-Wechsel |
| 6 | **`willReadFrequently:true`** nur bei `getImageData`-Puffern, `desynchronized:true` bei FX-Canvas | Canvas-Kontexte | Geringere GPU-Roundtrips |
| 7 | **Event-Debounce** auf `mousemove` (Tooltip) & `resize` | HUD/Tooltip | Weniger Layout-Thrashing |
### P1 Mittlere Wirkung
| # | Maßnahme |
|---|---|
| 8 | **Modularisierung** in IIFE/Module: `core/loop`, `core/state`, `units/tower`, `units/enemy`, `systems/pathflow`, `systems/aura`, `systems/skills`, `ui/*`, `audio`, `persistence`, `debug` |
| 9 | **State-Reduktion**: `Game`-Objekt durch `store`-Muster mit `subscribe/get` ersetzen (~60 Zeilen) |
| 10 | **Pure-Function-Tests für Pfadlogik**: `flowField`, `diagnoseRouteState`, `computePath` extrahieren → unit-testbar |
| 11 | **Konstanten-Block** (`CELL`, `MAP_W`, `NANITE_LIMIT`, `PARTICLE_MAX`, …) in `constants` |
| 12 | **Magic-Number-Audit**: `1/60`, `0.05`, `180` etc. in Konstanten umbenennen |
| 13 | **`Symbol()`-Keys** für interne `Game.towers[i]._internal*` statt String-Props |
### P2 Backlog
| # | Maßnahme |
|---|---|
| 14 | Web Worker für Flow-Field-Berechnung bei großen Maps |
| 15 | WebGL2-Option (`<canvas>`-Fallback bleibt) für Partikel > 5.000 |
| 16 | `requestIdleCallback` für UI-Rebuilds (Tower-Panel, Skilltree) |
| 17 | Code-Splitting via `import()` hinter „Menu" |
| 18 | `Intl.NumberFormat` für Zahlen im HUD |
| 19 | i18n-Datei (DE/EN) statt inline-Strings |
| 20 | PWA-Shell (`manifest.json`, `sw.js`) für Offline |
---
## 3. Rendering-Optimierung (Details)
### 3.1 Frame-Pipeline (empfohlen)
```
[1] input → pointer events, key state
[2] update(dt) → enemies → towers → projectiles → nanites → particles
[3] dirty check → bgDirty / midDirty / fxDirty (Boolean pro Layer)
[4] render() → if bgDirty: drawImage(bgCanvas);
if midDirty: draw towers+enemies;
fxCanvas always (additive)
```
### 3.2 Konkrete Canvas-Techniken
```js
const fx = document.createElement('canvas');
const fctx = fx.getContext('2d', { alpha: true, desynchronized: true });
fctx.globalCompositeOperation = 'lighter'; // Partikel additiv
// pro Frame:
fctx.clearRect(0, 0, fx.width, fx.height); // oder 'destination-out' Fade für Trails
```
- **Batching**: Alle Partikel gleicher Farbe in einen `beginPath()` → eine `fill()`.
- **`roundRect`** nativ (Chrome 99+) statt Hand-Rotation.
- **`createPattern`** für Tile-Boden (1× erzeugen).
- **Sprite-Atlas** für die 9 Türme: 1 Atlas 512×512, `drawImage(atlas, sx,sy,sw,sh, x,y,w,h)`.
### 3.3 Zielwerte
| Metrik | Vorher (Messung in DevTools) | Nachher (Ziel) |
|---|---|---|
| Framezeit @ 4K, 80 Türme, 150 Feinde | 1824 ms | ≤ 12 ms |
| GC-Pausen/Minute | ~12 | ≤ 4 |
| Heap-Wachstum (10 Min) | ~40 MB | ≤ 10 MB |
---
## 4. Refactoring-Checkliste (Definition of Done)
- [ ] Alle drei Audits (`self`, `extension`, `redesign`) vor & nach Refactoring grün
- [ ] `window.__AEGIS_DEBUG__.getState()` liefert identisches Schema (nur interne Keys dürfen sich ändern)
- [ ] Kein sichtbarer Frame-Drop bei 60 fps Test-Level
- [ ] `localStorage`-Schema unverändert (oder Migration in `persistence.js`)
- [ ] Editor-Map & Custom-Level import/export byte-identisch
- [ ] Lighthouse-Performance ≥ 95 (mobile emulation)
---
## 5. Vorschlag: Refactoring in 3 Sprints
| Sprint | Inhalt | Aufwand |
|---|---|---|
| S1 (Tag 12) | P0 17 (Pooling, Spatial Hash, Layer-Cache, dt-Clamp) | ~6 h |
| S2 (Tag 34) | P1 811 (Modularisierung + Konstanten + Store) | ~8 h |
| S3 (Tag 5) | P1 1213 + P2 14 (Worker) + Lighthouse-Polish | ~5 h |
**Regressionsschutz:** Vor jedem Sprint einen „Golden Frame" (Screenshot + `getState()`-Dump) mit der
Test-Suite erzeugen (`tests/audit.mjs` + `tests/perf-test.mjs`, siehe `03_Automatisierung_CI.md`).