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 |
−30–50 % GC-Pressure |
| 2 |
Spatial Hash (Grid 32 px) für towerFire-Zielwahl (heute O(N·M) alle Paare) |
towerFire(t,dt) |
−50–80 % 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 |
−10–20 % 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)
3.2 Konkrete Canvas-Techniken
- 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 |
18–24 ms |
≤ 12 ms |
| GC-Pausen/Minute |
~12 |
≤ 4 |
| Heap-Wachstum (10 Min) |
~40 MB |
≤ 10 MB |
4. Refactoring-Checkliste (Definition of Done)
5. Vorschlag: Refactoring in 3 Sprints
| Sprint |
Inhalt |
Aufwand |
| S1 (Tag 1–2) |
P0 1–7 (Pooling, Spatial Hash, Layer-Cache, dt-Clamp) |
~6 h |
| S2 (Tag 3–4) |
P1 8–11 (Modularisierung + Konstanten + Store) |
~8 h |
| S3 (Tag 5) |
P1 12–13 + 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).