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

5.7 KiB
Raw Blame History

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

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).