Plan 0 from Phase 1: Architektur-Weichenstellung & Datengetriebene Szenen-Pipeline
Status: Genehmigter Masterplan für den Übergang von imperativen TypeScript-Szenen zu datengetriebenen Level-Deskriptoren und smarter Kit-Metadaten-Auswertung. Umgesetzt und abgelöst durch ADR 0020 — dieses Dokument bleibt als historischer Schnappschuss der ursprünglichen Q&A-Herleitung stehen.
Datum: 2026-09-18 (verschoben aus.agents/notes/am 2026-09-26, Inhalt unverändert)
Referenzen: ADR 0010 (Maker), ADR 0011 (Asset-Kits), ADR 0016 (Stage Zones), ADR 0017 (glTF Extension Registry), ADR 0019 (Environment Texturing).
1. Kontext & Ausgangsdiagnose
Die initiale Implementierung des Prologs (Koje 42, apps/sample-apps/the-whisper/scenes/prologue/prologue.ts, 1772 Zeilen) wurde als imperativer Monolith aufgebaut:
- 14 Kit-Props werden im TypeScript-Code mit hardkodierten Pfaden, Positionen, Skalierungen und Rotationen geladen.
- Sockets und Lichtquellen (
FlameGlow,BulbLight,CyanideColorkeySpot) werden manuell im Code erzeugt und verdrahtet. - Material-PBR-Werte (
roughness,metallic) werden pertraverse-Schleifen überschrieben. - Die Spielerbewegung läuft über ein isoliertes, handgeschriebenes
MathUtils.clamp()-AABB-System statt überStageMovementBehavior/StageZone. - Hotspots, deutsche Monologe und Kamera-Fahrten stehen als Code-Literale in derselben Datei.
Die drei identifizierten Brüche
- Metadaten-Bruch: Das
meta.json-Schema (ADR 0011) definiert bereitsrecommendedScaleundsockets[].recommendedLight(z. B. Laterne: Scale 0.35, SocketFlameGlowbei[0, 0.16, 0], Farbe#d49a3d, Intensity 3.5).prologue.tstippte diese Werte bisher erneut von Hand ein. - Szenen-Bruch: Szene ist aktuell 100 % Code statt Daten-Asset. Jede Änderung am Raum-Layout erfordert TS-Code-Edits und Re-Kompilierung.
- Bewegungs-Bruch: Die Koje nutzt ein improvisiertes AABB-Clamp statt der etablierten
StageZone-Engine-Infrastruktur.
2. Kritische Einsprüche & Grundsatzentscheidungen
Im Architektur-Sparring wurden drei fundamentale Einsprüche und Leitprinzipien festgehalten:
Einwand 1: Bewegung & Projektions-Falle ($v \to Y$ vs. $v \to Z$)
StageProjection("flat-plane"aus ADR 0016) projiziert $v \to Y$ auf eine vertikale 2.5D-Kulissenwand (Backdrop).- Koje 42 ist jedoch ein echter 3D-Boden ($X/Z$-Ebene bei $Y = 0$). Eine naive Umstellung auf
"flat-plane"würde die Achsen vertikal spiegeln. - Entscheidung: Bewegung wird in diesem Meilenstein nicht überstürzt, sondern in Phase 3 als eigene, saubere Erweiterung gelöst: Einführung von
{ mode: "ground-plane", width, depth, y }inStageProjectionfür Koje 42,character-dioramaund künftige Isometrie-/3D-Böden.
Einwand 2: Realistische Zeilentrennung (Dressing vs. Gameplay/Story)
- Die Aufteilung trennt Dressing (Raum, Props, Lichter, PBR $\to$ Daten/JSON) von Story & Gameplay (Timeline, Verzweigungen, Dialoge, Modals $\to$ schlanker TypeScript-Controller).
- Der TypeScript-Controller schrumpft von 1772 Zeilen auf einen fokussierten, lesbaren Gameplay-Controller (~600–800 Zeilen State-Machine- & Story-Code).
Einwand 3: Prefab-Defaults vs. Level-Instance-Overrides (Unity/Unreal-Standard)
meta.jsonfungiert als Default-Quelle (Typ/Prefab).- Das Level-Config (
koje42.level.json) besitzt explizites Override-Recht (z. B. Lichtintensität 2.5 statt 3.5 für düstere Bunker-Atmosphäre). - Narrative, raumspezifische Lichter (wie der smaragdgrüne
CyanideColorkeySpotauf der Leichenschublade) leben in einer eigenständigenlights[]-Sektion im Level-Config.
3. Architektur-Grundgesetze für Small World
- Instanzbasierte
KitRegistry(Kein Singleton):- Gemäß Kern-Architekturregel von Small World gibt es keine globalen Singletons.
KitRegistrywird via Constructor Injection oder Scene-Context bereitgestellt.
- Standardisierte Schemas & Validierung:
- Jedes Level-Config folgt einem formalen JSON-Schema (
public/schemas/level.schema.json). - Unit-Tests validieren alle Level-Configs automatisch per Ajv (analog zu
AssetKitValidation.test.ts).
- Jedes Level-Config folgt einem formalen JSON-Schema (
- Zweistufige Prop-Auflösung:
registry.loadProp("bunker/kerosene_lantern")lädt das.glb, liestmeta.json, skaliert automatisch aufrecommendedScaleund instanziiert deklarierte Sockets/Lichter.- Das Level-Descriptor-System überschreibt bei Bedarf gezielte Attribute (
position,rotation,scale,lightOverrides).
4. Phasen- & Umsetzungsplan
flowchart TD
subgraph Phase 1: Engine Kit-Runtime & Level-Dressing
P1A["1.1 Engine KitRegistry<br/>(Instanzbasiert, meta.json-Parsing, Socket-Auto-Mounting)"]
P1B["1.2 Level-Schema & Tests<br/>(public/schemas/level.schema.json, LevelValidation.test.ts)"]
P1C["1.3 Deklarative Koje 42<br/>(koje42.level.json)"]
P1D["1.4 Controller-Refactoring<br/>(prologue.ts entkernen, Dressing auslagern)"]
P1A --> P1B --> P1C --> P1D
end
subgraph Phase 2: QA & ADR-Dokumentation
P2A["2.1 Testsuite & Typenprüfung (146+ Testsuiten)"]
P2B["2.2 ADR 0020: Declarative Level Descriptors & Kit Runtime"]
P1D --> P2A --> P2B
end
subgraph Phase 3: 3D Ground Movement
P3A["3.1 StageProjection ground-plane Modus"]
P3B["3.2 StageZone-Polygon-Migration für Koje 42"]
P2B --> P3A --> P3B
end
subgraph Phase 4: Maker-Editor-Pipeline
P4A["4.1 Maker Kit-Katalog-Browser"]
P4B["4.2 Visueller Levelbau & Export"]
P3B --> P4A --> P4B
endDetaillierte Arbeitspakete für Phase 1 (Aktueller Fokus)
Schritt 1.1: KitRegistry im @small-world/engine
- Pfad:
packages/engine/src/loaders/kit/KitRegistry.ts - Verantwortlichkeit:
- Auflösen von Kit-IDs (
<kit>/<prop>) zumodel.glbundmeta.json. - Automatisches Anwenden von
recommendedScale. - Automatisches Erzeugen von
PointLight/SpotLightan deklariertensockets[]. - Caching von
meta.jsonund geladenen Geometrien. - Vollständige Entkopplung (kein globaler Singleton, reine DI).
- Auflösen von Kit-IDs (
- Unit-Tests:
packages/engine/tests/loaders/KitRegistry.test.ts.
Schritt 1.2: level.schema.json & Validierung
- Pfad:
public/schemas/level.schema.json - Struktur:
id,name,version,environment(Wände, Boden, Decke, Texturzuweisungen).props: Array aus{ id, kitProp, position, rotation, scale, lightOverrides, materialOverrides }.lights: Array aus eigenständigen Szenenlichtern (SpotLight,PointLight,DirectionalLight).hotspots: Array aus Interaktionspunkten (id,name,position,radius,promptText,monologue).stageZone: Optionale 2.5D/3D-Zonendefinition.
- Validierung:
packages/engine/tests/loaders/LevelValidation.test.ts.
Schritt 1.3: koje42.level.json
- Pfad:
apps/sample-apps/the-whisper/scenes/prologue/koje42.level.json - Extraktion aller 14 Props, PBR-Material-Bindings (
concrete_board,brick_aged,steel_corroded, etc.), Decken-/Kerosin-Lichter, Hotspots und Monologe in die deklarative JSON-Datei.
Schritt 1.4: Refactoring von prologue.ts
- Ersetzen von
_buildBunkerRoom(),_loadPropKits()und_applyKitTextures()durch einen schlanken Level-Loader:typescriptconst level = await this.kitRegistry.loadLevel("/scenes/prologue/koje42.level.json", this.scene); - Fokussierung der Klasse auf Gameplay, Cinematic-Timeline, Dialoge, Modals und Area-Transitions.
5. Abnahmekriterien & Qualitätsstandards
- 100 % Strict TypeScript: Kein
any, saubere Schnittstellen und explizite Typen. - 100 % Testabdeckung: Alle bestehenden 835 Tests bleiben grün, neue Tests für
KitRegistryundLevelValidationhinzugefügt. - Visuelle & Funktionale Parität: Koje 42 sieht im Browser absolut identisch aus und verhält sich exakt wie vor dem Refactoring.
- Dokumentation: Aktualisierung des Dev-Logs (
apps/sample-apps/the-whisper/docs/log.md) und Erstellung vondocs/adr/0020-declarative-level-descriptors-and-kit-runtime.md.
6. Kompendium: „Vom 2D-Konzeptbild zur fertig spielbaren 3D-Szene“ (Q&A-Referenz)
Dieses Kompendium hält alle während der Entstehung von Koje 42 getroffenen Grundsatzfragen, Diskussionen und Antworten schriftlich und dauerhaft fest.
flowchart LR
A["1. 2D-Konzeptgrafik<br/>(4 Kardinal-Ansichten)"] --> B["2. Freisteller-Extraktion<br/>(Isolierte Requisiten)"]
B --> C["3. Tripo3D Ingest<br/>(Game-Ready .glb + PBR)"]
C --> D["4. Kit-Packaging<br/>(meta.json, kit.json, Sockets)"]
D --> E["5. Modularer Shell-Bau<br/>(ADR 0019 PBR-Kachelsystem)"]
E --> F["6. PBR-Decals & Aufputz<br/>(Tiefen, Reliefs, Emissive)"]
F --> G["7. Level-Deskriptor<br/>(koje42.level.json)"]
G --> H["8. Story-Controller<br/>(TypeScript State-Machine)"]Frage 1: Warum kann eine KI nicht einfach das 2D-Raumbild komplett in eine 3D-Szene umwandeln?
Antwort & Begründung:
- Matsch-Geometrie: Wird ein ganzer Raum mit allen Wänden, Möbeln und Böden in ein 3D-Netzwerk (z. B. Tripo3D) geworfen, entsteht eine einzige zusammenhängende, geschmolzene "Gummizelle". Tische verschmelzen mit dem Boden, Stühle mit den Wänden.
- Eingebranntes Licht: Schatten und Lichtreflexionen der Konzeptgrafik werden fest in die Textur gebacken. Ein dynamisches Bewegen von Lichtquellen (z. B. Novotnys Laterne oder flackernde Deckenlampen) ist unmöglich.
- Fehlende Interaktivität: Man kann keine Schublade öffnen, kein Lüftungsgitter abschrauben und keine Kollisionsboxen für einzelne Objekte definieren.
- Die Small-World-Doktrin: $$\text{2D-Raum} \implies \text{Modularer Raum-Shell (PBR-Kacheln)} + \text{Isolierte Requisiten (Tripo3D)} + \text{In-Engine-Assemblierung}.$$
Frage 2: Wie werden Wände und modulare Räume strukturiert, damit sie nicht monoton wirken?
Antwort & Begründung:
- Räume variieren stark in ihrer Größe (von der $7 \times 7,\text{m}$ Koje 42 bis zu $30,\text{m}$ langen Fluren und Katakomben). Reine Textur-Kachelung führt unweigerlich zu sichtbaren Wiederholungsmustern (Tiling-Artefakte).
- Die 4-Säulen-Skalierungsdoktrin (ADR 0019):
- Struktureller Rhythmus: Wandflächen werden im festen $3{,}5,\text{m}$-Raster durch vertikale Stahlbeton-Pilaster/Säulen (
FlakturmKit.createPillar) unterbrochen. - Horizontale Zonierung (Baseboard): Der untere Wandmeter erhält eine separate Sockelleiste mit Feuchtigkeits-, Schimmel- und Salzausblühungs-Texturen (
concrete_damp_efflorescence). Der obere Bereich nutzt Schalungsbeton (concrete_panel_smoothoderconcrete_board). - Decken-Kassetten & Unterzüge: Decken werden durch Quer- und Längsträger (
createBeam) in Kassetten aufgeteilt. - Aufputz-Infrastruktur: Leitungsrohre (
createConduitRun), Verteilerdosen und Schalter brechen die glatten Flächen organisch auf.
- Struktureller Rhythmus: Wandflächen werden im festen $3{,}5,\text{m}$-Raster durch vertikale Stahlbeton-Pilaster/Säulen (
Frage 3: Sind Decals in Small World nur flache „Aufkleber“ oder haben sie echte Materialeigenschaften?
Antwort & Begründung:
- Decals in Small World sind keine unbeleuchteten, flachen 2D-Sticker.
- Sie werden als vollwertige
StandardMaterial-Instanzen auf Polygon-Trägern gerendert und besitzen:- Diffuse Map: Albedo-Farbkanal mit Alphatransparenz für nahtlose Kanten.
- Normal Map: Echte Oberflächennormalen für Risse, abgeplatzten Putz, Nieten und Kantenabplatzungen (wirft bei flachem Lichteinfall plastische Schatten).
- Roughness & Metallic: Nasser Beton glänzt speckig, korrodierter Stahl reflektiert metallisch, während Ziegel rau und matt bleiben.
- Emissive Map: Phosphoreszierende Leuchtstreifen (
guide_stripe_glow) und Warnmarkierungen strahlen eigenes Licht in den Bloom-Pass der Engine ab.
Frage 4: Haben wir nach den 3D-Meshes alles, um einen Raum aufzubauen, oder braucht es noch Code?
Antwort & Begründung:
- Ja, es braucht Code — aber an der richtigen Stelle:
- Daten (JSON / glTF): Wo steht welcher Tisch? Welcher Hocker gehört davor? Welche Skalierung hat die Lampe? Welche PBR-Textur liegt auf welcher Wand? $\implies$ Reine Daten-Ebene (
koje42.level.json). - Code (TypeScript): Was passiert, wenn Novotny die Kaffeemühle anklickt? Wie läuft das Verhör mit Hawelka ab? Welche Wahlmöglichkeiten hat der Spieler im Dialog? Wann schaltet die Kamera in die Kältekammer um? $\implies$ Interaktive Logik-Ebene (
prologue.ts).
- Daten (JSON / glTF): Wo steht welcher Tisch? Welcher Hocker gehört davor? Welche Skalierung hat die Lampe? Welche PBR-Textur liegt auf welcher Wand? $\implies$ Reine Daten-Ebene (
Frage 5: In welcher Sprache wird die Logik implementiert?
Antwort & Begründung:
- Strict TypeScript: 100 % typisiert, strikte Null-Prüfung, striktes Verbot von
any. - Keine globalen Singletons: Alle Systeme (
KitRegistry,HotspotManager,TerminalModal) werden per Dependency Injection oder Szenen-Kontext übergeben. Das garantiert, dass mehrere Engine-Instanzen auf einer Seite koexistieren können.
Frage 6: Wie verhält sich meta.json zur Spielszene? (Defaults vs. Overrides)
Antwort & Begründung:
meta.json= Prefab-Default: Jedes Prop definiert seine physikalische Grundkonfiguration (recommendedScale, Sockets wieFlameGlowan der Kerosinlampe, Standard-Lichtfarbe#d49a3d, Standard-Intensität $3{,}5$).level.json= Szenen-Instanz-Override: Die konkrete Szene kann diese Werte gezielt überschreiben (z. B.intensity: 2.5für eine dunklere, düstere Bunker-Koje).- Narrative Lichter: Lichter, die an keinem Prop hängen (wie der smaragdgrüne Blausäure-Fokus-Spot
CyanideColorkeySpotauf Františeks Hals), leben in einer eigenständigenlights[]-Sektion im Level-Deskriptor.
Frage 7: Wie unterscheidet sich die Spielerbewegung in 2.5D-Bühnen von echten 3D-Räumen?
Antwort & Begründung:
- 2.5D-Backdrop (
flat-plane): Die Spielfigur bewegt sich auf einer vertikal gemalten Kulissenwand ($v \to Y$). Die Skalierung der Figur wird künstlich mit der Bildhöhe verkleinert. - Echter 3D-Boden (
ground-plane): Die Spielfigur bewegt sich auf einer horizontalen Ebene ($X/Z$-Koordinaten bei $Y = 0$). Die Tiefenskalierung entsteht natürlich durch die 3D-Perspektivkamera der Engine. - Architektur-Entscheidung: Koje 42 bleibt im aktuellen Meilenstein ein echter 3D-Boden. In Phase 3 wird
StageProjectionum{ mode: "ground-plane" }erweitert, um beide Welten in einem einheitlichenStageZone-Polygon-System zu vereinen.