Files
atomic-engine/AGENTS.md
T
nico 2c23216152 v0.3: Large world 4096x3072 + momentum waves + zoom-scaled lighting
- World expanded to 4096x3072 (16x area, tiled 4x4 copies)
- Active region margin increased to 200px
- Liquid momentum system (FLAG_MOMENTUM) for wave sloshing
- Zoom-at-cursor support (camera.zoomAt)
- Smoother zoom steps (0.2 per scroll tick)
- Light propagation scaled by zoom (passes & falloff)
- AGENTS.md updated with all current features
2026-07-09 12:15:52 +02:00

228 lines
10 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.
# AtomicEngine
> Eine 2D-Platformer-Gameengine bei der jeder Pixel wie ein Atom mit eigenen
> Eigenschaften und Anziehungskräften zu seinen Nachbarn agiert.
> Browser-basiert mit Rust/WASM für die Physik und WebGL2 fürs Rendering.
## Architektur-Übersicht
```
engine/ (Rust → WASM) web/ (TypeScript + Vite)
┌─────────────────────┐ ┌──────────────────────┐
│ grid.rs │ │ main.ts │
│ material.rs │ │ renderer.ts (WebGL2) │
│ physics.rs │ │ input.ts │
│ render_buffer.rs │ │ camera.ts │
│ lib.rs (wasm API) │ │ │
└─────────────────────┘ └──────────────────────┘
│ │
└── SharedArrayBuffer ──────┘
```
## Tech-Stack
| Schicht | Technologie | Warum |
|---------------|--------------------------|------------------------------------|
| Physik | Rust → WASM | Performance, Sicherheit |
| WASM Bridge | wasm-bindgen, wasm-pack | Auto JS-Bindings |
| Renderer | WebGL2 (Canvas) | GPU-beschleunigt, 60fps |
| Web Shell | TypeScript, Vite | Build, Dev-Server, HMR |
| Speicher | SharedArrayBuffer | Zero-copy WASM ↔ JS |
## Welt-Modell
- **Große Welt**: 4096×3072 Pixel (16×12 Chunks à 256×256, tiled 4×4 Szenen)
- Nur Zellen im Sichtfeld + 200px Margin werden simuliert
- Rest eingefroren — keine CPU-Kosten für unsichtbare Bereiche
- Grid wächst auf 4096×3072 = 12.5M Zellen (~48 MB Speicher)
### Datenstruktur Cell (pro Pixel)
```rust
struct Cell {
material: u8, // Material-ID (0 = Luft)
health: u8, // Lebenspunkte/Dichte
temp: u8, // Temperatur
flags: u8, // Bitflags (aktiv, Player, Flüssig, ...)
}
```
Grid ist SoA (Structure of Arrays):
- `Vec<u8>` für materials, healths, temps, flags
- Breite × Höhe = Chunk-Größe
## Material-System
16 Materialien (0=Luft, 1=Stein, 2=Erde, 3=Sand, 4=Wasser, 5=Holz, 6=Lava,
7=Player, 8=Dampf, 9=Feuer, 10=Glas, 11=Eis, 12=Öl, 13=Säure, 14=Funken, 15=Rauch).
Properties pro Material: `MaterialProps` in `engine/src/material.rs`:
- `color`, `density`, Bewegung-Typ (`is_powder/is_liquid/is_gas/is_solid/is_static`)
- `flammability`, `melt_temp`, `boil_temp`, `heat_conduct`, `acid_resist`
- `light_emit` (0255, wie viel Licht das Material abstrahlt)
- `light_block` (0255, wie stark das Material Licht blockiert)
### Interaktions-System
**Heat-Transfer**: Temperatur-Diffusion über Moore-Nachbarn (`heat_conduct`-abhängig).
**Phase-Transformationen** (`material::transform`):
- Eis (11) → Wasser (4) bei temp ≥ 5°C
- Wasser (4) → Dampf (8) bei temp ≥ 100°C
- Dampf (8) → Wasser (4) bei temp ≤ 80°C
- Lava (6) → Stein (1) bei temp ≤ 5°C
- Holz (5) → Feuer (9) bei temp ≥ 240°C
- Öl (12) → Feuer (9) bei temp ≥ 120°C
**Reaktionen** (`physics::react`):
- Wasser + Lava → Dampf + Stein
- Wasser + Feuer → Dampf + Luft (löscht Feuer)
- Lava + Holz/Öl → entzündet Holz/Öl sofort zu Feuer
- Säure + Holz/Sand → zerstört Holz/Sand
- Säure + Stein/Erde → zerstört Stein/Erde (langsamer)
**Feuer-System**:
- Braucht Brennstoff (Holz, Öl) um zu überleben — prüft direkt unter sich + Moore-Nachbarn
- Feuer-Säule: Hat eine Zelle Feuer unter sich, gilt sie als versorgt (Ketten-Propagation)
- Mit Brennstoff: keine Health-Decay
- Ohne Brennstoff: 6hp/Frame → erlischt in ~7 Frames (~0.1s)
- Brenn-Rate `flammability`-abhängig: `300/flam` Frames zwischen 1hp-Konsum
- Holz (30): ~10 Frames/Konsum → ~42s pro Block
- Öl (80): ~3 Frames/Konsum → ~13s pro Block
- Ausbreitung: 1/250 Chance auf brennbare Nachbarn
- Wasser/Eis in Nachbarschaft → Feuer erlischt sofort
- Erzeugt Funken (14) und Rauch (15) beim Brennen
- **Flammen-Züngeln**: 1/5 Chance pro Zelle, Funken-Partikel nach oben zu feuern (412 Frames Lebensdauer)
- **Liquid-Momentum**: FLAG_MOMENTUM auf fallendem/sloshendem Wasser, waves klettern an Schalenwänden hoch
- **Flammen-Züngeln**: 1/5 Chance pro Zelle, Funken-Partikel nach oben zu feuern (412 Frames Lebensdauer)
**Ephemere Partikel** (Funken + Rauch):
- `update_ephemeral`: dekrementiert Health, bei 0 → Luft
- Flammen-Funken: 412 Frames, 1/5 Emission, kurzlebig für Flammenspitzen
- Funken: 3080 Frames Lebensdauer, orange, 1/16 Emission/Frame
- Rauch: 140220 Frames, hellgrau transparent, 1/6 Emission/Frame
- Gas-Pass separat (alle 2 Frames, top→bottom = max 1px/Frame Aufstieg)
## Physik-Loop (pro Frame)
Reihenfolge in `physics::update(cam_x, cam_y, rw, rh, zoom)`:
Nur Zellen innerhalb **Kamera-Sichtfeld + 200px Margin** werden simuliert.
Rest der Welt ist eingefroren (keine CPU-Kosten).
1. **update_fire** — Feuer-Update: Brennstoff-Verbrauch, Ausbreitung, Funken/Rauch-Emission, Flammen-Züngeln
2. **Haupt-Loop** (bottom→top, alternierende Spalten, nur aktive Region):
- Heat-Transfer über Moore-Nachbarn
- Material-Reaktionen (Water+Lava, Säure+Holz, ...)
- Phasen-Transformation (Temp-basiert)
- Bewegung: Powders ↓, Liquids ↓↔, Solids ↓ (kein Gas!)
3. **update_gases** (top→bottom, nur jedes 2. Frame, nur aktive Region): Gas + Flammen-Funken steigen max 1px/Frame
4. **update_ephemeral** (nur aktive Region): Spark/Smoke Health dekrementieren, bei 0 → Luft
## Level-Format
RGBA-PNG, jeder Pixel = 1 Atom:
- R = Material-ID (0=Luft)
- G = Dichte/Gesundheit
- B = Temperatur
- A = Flags
Levels in Aseprite/Photoshop malbar. Große Welten = Raster von PNG-Dateien.
## Rendering
1. WASM schreibt sichtbaren Bereich als RGBA-Buffer
2. Buffer als Uint8Array aus WASM-Speicher gelesen
3. WebGL2 lädt als Textur → Fullscreen-Quad
4. Kamera-Matrix für Scroll/Zoom
### Textur-System
Jedes Material bekommt deterministische Pixel-Variation basierend auf Grid-Koordinaten:
- **Holz**: Vertikale Maserung (dünne Fasern alle ~5px, ±1px Welle)
- **Erde**: Grobkörnige Flecken (niederfrequentes Rauschen, ±20 RGB)
- **Stein/Glas**: Subtiles Rauschen (±10 RGB)
- **Sand**: Feines Granulat (±6 RGB)
- **Eis**: Minimales Rauschen (±5 RGB)
- **Flüssigkeiten/Gase**: Keine Textur
- **Feuer/Lava**: Kanal-getrennte Varianz (R ±6, G ±12, B ±8) — natürliche Gelb-/Orange-Mischung
### Beleuchtungs-System
**Light-Propagation** in `render_buffer.rs`:
1. Emissions-Pass: Jedes Pixel mit `light_emit > 0` strahlt Licht
2. Flood-Fill (max `16/zoom` Passes, early-termination): 8-Richtungs-Propagation
- Falloff: `(2|3)/zoom` (kardinal/diagonal) + `light_block` (gecapped bei 200)
- Opaque Pixel blocken Weitergabe, werden aber selbst beleuchtet
3. Abwechselnde Scan-Richtung für gleichmäßige Verteilung
4. Zoom-skaliert: Passes + Falloff passen sich an Kamera-Zoom an
**Lichtquellen**: Feuer (255), Lava (200), Funken (180)
**Glow-Effekt**: Luft-Pixel mit Licht blenden von dunklem Hintergrund zu warmem Orange — quadratische Kurve (`t = lvl²/255`) für natürlichen Abfall. Nur direkt an der Quelle stark sichtbar.
**Reflexion**: Semi-transparente Materialien (Glas, Wasser, Eis — block 1179) werfen 50% des empfangenen Lichts an Nachbar-Pixel zurück → indirekte Beleuchtung.
**Beleuchtungs-Formel** (Material-Pixel): Additiv: `r = min(255, r × 150/256 + light)` — Ambient-Basis 59% + Licht obendrauf.
### UI
- **Material-Palette**: Leiste am unteren Bildschirmrand, Farb-Swatch + Name + Taste
- **FPS-Anzeige**: Oben rechts, grün, alle 500ms aktualisiert
## Build & Entwicklung
**WICHTIG**: Native Debug-Binary und WASM/Web-Build müssen immer identisch laufen.
Der Debug-Build nutzt dieselben Module (`grid`, `material`, `physics`, `render_buffer`)
und dieselben Dimensionen (256×192 Grid, 320×180 Render-Buffer). Performance-Unterschiede
sind WASM-Overhead, nicht Logik-Abweichungen. Bei Änderungen an der Engine immer beide
Builds testen.
```bash
# Rust → WASM bauen
cd engine && wasm-pack build --target web --out-dir ../web/pkg
# Native Debug-Binary (x86, ohne WASM)
cd engine && cargo run --bin atomic-debug --release
# Web Dev-Server
cd web && npm run dev
# Produktion
cd web && npm run build
```
## Datei-Index (was findet man wo)
| Was | Datei |
|----------------------------------|-----------------------------------|
| WASM öffentliche API | `engine/src/lib.rs` |
| Native Debug Binary | `engine/src/main.rs` |
| Cell + Chunk + Grid DS | `engine/src/grid.rs` |
| Material-Definitionen | `engine/src/material.rs` |
| Physik: Kräfte, Sand, Fluide | `engine/src/physics.rs` |
| RGBA-Buffer Export | `engine/src/render_buffer.rs` |
| Rust-Abhängigkeiten | `engine/Cargo.toml` |
| WebGL2 Renderer | `web/src/renderer.ts` |
| WASM-Bridge + Material-Konstanten| `web/src/engine.ts` |
| Game-Loop, Wiring | `web/src/main.ts` |
| Input (Keyboard, Maus) | `web/src/input.ts` |
| Kamera (Scroll, Zoom) | `web/src/camera.ts` |
| HTML Einstieg | `web/index.html` |
| Vite Konfiguration | `web/vite.config.ts` |
| TypeScript Konfiguration | `web/tsconfig.json` |
## Aktueller Status
- [x] Phase 0: Projekt-Struktur + WASM/WebGL End-to-End ← **fertig**
- [x] Phase 1: Sand-Physik (Pixel fällt nach unten) ← **fertig** (Sand + Flüssigkeiten)
- [x] Phase 2: Mehrere Materialien + Kräfte-Tabelle ← **fertig**
- [x] Phase 3: Chunk-System + große Welt + Kamera ← **fertig**
- [ ] Phase 4: Spieler als Atom-Cluster + Input
- [ ] Phase 5: Erweiterungen (Items, Player-Interaktion, Editor, ...)
## Namenskonventionen
- Rust: snake_case, English
- TypeScript: camelCase, English
- Keine Kommentare im Code (nur wenn explizit gewünscht)