mirror of
https://github.com/Memo-2023/mana-monorepo.git
synced 2026-05-14 18:01:09 +02:00
feat(manavoxel): scaffold 2D pixel platform MVP (Phase 0)
Add ManaVoxel — a 2D top-down pixel platform for creating and programming miniature worlds in the browser. This commit includes: - SvelteKit + PixiJS 8 web app with chunk-based tilemap renderer - Game engine: camera (scroll/zoom), input (keyboard/mouse/touch), player with AABB collision, editor/play mode toggle - Pixel editor tools: brush, eraser, flood fill, pipette, box fill, line (Bresenham), undo/redo stack - Shared types package: materials, areas, items, network protocol, inventory - Demo world generator with terrain, buildings, trees - Material palette UI with 15 materials, keyboard shortcuts - Comprehensive design documents (Roblox analysis, tech stack options, voxel resolution analysis, 2D alternative comparison, full project plan) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
f4599d1fd9
commit
5589765180
30 changed files with 8969 additions and 0 deletions
826
NewAppIdeas/Roblox Reimagined/2D-Alternative.md
Normal file
826
NewAppIdeas/Roblox Reimagined/2D-Alternative.md
Normal file
|
|
@ -0,0 +1,826 @@
|
|||
# ManaVoxel 2D: Die Pixel-Variante
|
||||
|
||||
## Gleiche Vision, eine Dimension weniger -- was aendert sich?
|
||||
|
||||
---
|
||||
|
||||
## 1. Die 2D-Vision
|
||||
|
||||
```
|
||||
3D-Version (GAME-PLAN.md):
|
||||
Bevy + wgpu + WebGPU + Rust + 10cm Voxel
|
||||
→ 18-24 Monate, Rust lernen, komplexe Engine
|
||||
|
||||
2D-Version:
|
||||
PixiJS/Phaser + Canvas/WebGL + TypeScript + Pixel-Grid
|
||||
→ 3-6 Monate, bestehende Sprachen, simpler Stack
|
||||
→ GLEICHE Kern-Features: Bauen, Programmieren, Teilen, Spielen
|
||||
```
|
||||
|
||||
### Was bleibt gleich?
|
||||
|
||||
```
|
||||
✓ Alles entsteht im Spiel (Pixel-Editor statt Voxel-Editor)
|
||||
✓ Items sind programmierbar (3 Ebenen: Slider → Trigger → Code)
|
||||
✓ Browser-first, kein Download
|
||||
✓ Local-First, Offline-Editor
|
||||
✓ Multiplayer
|
||||
✓ Self-Hosted
|
||||
✓ Creator Economy (70% Revenue)
|
||||
✓ AI-Integration (NPCs, Asset-Gen)
|
||||
✓ Cross-Game Items
|
||||
✓ Guest Mode
|
||||
```
|
||||
|
||||
### Was aendert sich?
|
||||
|
||||
```
|
||||
3D Voxel: 2D Pixel:
|
||||
Perspektive von allen Seiten Top-Down oder Side-Scroller
|
||||
Kamera: Orbit, Fly, First-Person Kamera: Scroll, Zoom
|
||||
3D Physik (Rapier, Gravitation) 2D Physik (Rapier2D oder planck.js)
|
||||
3D Meshing (Greedy, LOD) Tilemap oder Sprite-Rendering
|
||||
Beleuchtung (Schatten, AO) 2D Lighting (Raycasting, einfach)
|
||||
GPU-intensiv (WebGPU noetig) CPU-freundlich (Canvas/WebGL reicht)
|
||||
Rust/Bevy (neu lernen) TypeScript (bereits beherrscht)
|
||||
Komplexe Kamera-Steuerung Simple Kamera (Pfeiltasten + Maus)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 2. Zwei 2D-Ansaetze
|
||||
|
||||
### Option A: Top-Down (wie Zelda / Pokemon / Stardew Valley)
|
||||
|
||||
```
|
||||
┌────────────────────────────────┐
|
||||
│ ████████████████████████████ │
|
||||
│ █ █ │
|
||||
│ █ ┌──┐ ┌────────┐ █ │
|
||||
│ █ │🪑│ │ Tisch │ █ │
|
||||
│ █ └──┘ │ ▫▫▫▫ │ █ │
|
||||
│ █ └────────┘ █ │
|
||||
│ █ █ │
|
||||
│ █ 🧑 (Spieler) █ │
|
||||
│ █ █ │
|
||||
│ █ 🗡️ (Item am Boden) █ │
|
||||
│ █ █ │
|
||||
│ ████████░░░░████████████████ │
|
||||
│ (Tuer) │
|
||||
└────────────────────────────────┘
|
||||
|
||||
Ansicht: Von oben, leicht schraeg (RPG Maker / Zelda-Style)
|
||||
Bewegung: 8 Richtungen
|
||||
Welt: 2D Grid aus Pixeln/Tiles
|
||||
```
|
||||
|
||||
**Vorteile Top-Down:**
|
||||
|
||||
- Raeume und Gebaeude natuerlich darstellbar
|
||||
- Moebel, Items, NPCs klar erkennbar
|
||||
- Klassisches Genre, Spieler kennen die Steuerung
|
||||
- Einfachste Physik (nur X/Y, keine Gravitation)
|
||||
- Perfekt fuer: Dungeons, Haeuser, Doerfer, Overworld
|
||||
|
||||
**Nachteile:**
|
||||
|
||||
- Keine Hoehe (kein Springen, keine Ebenen ohne Tricks)
|
||||
- Weniger "wow" als 3D
|
||||
- Gebaeude von oben = kein Innenraum sichtbar (braucht Layer-System)
|
||||
|
||||
### Option B: Side-Scroller (wie Terraria / Starbound / Noita)
|
||||
|
||||
```
|
||||
┌────────────────────────────────────────┐
|
||||
│ ☀️ ☁️ │
|
||||
│ │
|
||||
│ 🧑 ← Spieler │
|
||||
│ ████ │
|
||||
│ ██████ ┌──────┐ │
|
||||
│ ████████████████ │ Haus │ ████████ │
|
||||
│ ████████████████ │ │ ████████ │
|
||||
│ ████████████████ └──────┘ ████████ │
|
||||
│ ██████████████████████████████████ │
|
||||
│ ██████████████████████████████████ │
|
||||
│ ██████████████████ 💎 ███████████ │
|
||||
│ ██████████████████████████████████ │
|
||||
└────────────────────────────────────────┘
|
||||
|
||||
Ansicht: Seite (wie ein Querschnitt)
|
||||
Bewegung: Links/Rechts + Springen
|
||||
Welt: 2D Grid, mit Gravitation
|
||||
```
|
||||
|
||||
**Vorteile Side-Scroller:**
|
||||
|
||||
- Gravitation und Springen → dynamischeres Gameplay
|
||||
- Terraria hat bewiesen: 2D + Bauen + Kaempfen = Mega-Erfolg
|
||||
- Hoehlen, Untergrund, Himmel → vertikale Exploration
|
||||
- Gebaeude sieht man im Querschnitt (Inneneinrichtung natuerlich sichtbar!)
|
||||
- Zerstoerung sieht besser aus (Pixel fallen runter)
|
||||
|
||||
**Nachteile:**
|
||||
|
||||
- Nur eine "Tiefe" (kein Hinter/Vor, ausser mit Layern)
|
||||
- Weniger geeignet fuer Top-Down-Genres (RPG, Strategie)
|
||||
|
||||
### Empfehlung: Side-Scroller (Terraria-Ansatz)
|
||||
|
||||
```
|
||||
Warum Side-Scroller > Top-Down fuer unsere Vision:
|
||||
|
||||
1. INNENRAEUME: Im Side-View sieht man automatisch in Gebaeude rein
|
||||
→ Moebel, Items, Details sofort sichtbar
|
||||
→ Bei Top-Down muss man Daecher entfernen oder Layer wechseln
|
||||
|
||||
2. PHYSIK: Gravitation macht Items und Zerstoerung spannender
|
||||
→ Bloecke fallen, Schutt rieselt, Wasser fliesst
|
||||
→ Items fliegen durch die Luft beim Kampf
|
||||
|
||||
3. BEWIESENES KONZEPT: Terraria hat >58 Millionen Kopien verkauft
|
||||
→ Das Genre funktioniert, die Zielgruppe ist riesig
|
||||
|
||||
4. DETAIL: Seitenansicht zeigt mehr Detail pro Bildschirm
|
||||
→ Ein Schwert ist als Sprite viel erkennbarer als top-down
|
||||
|
||||
5. BAUEN: Haeuser bauen in Side-View ist intuitiver
|
||||
→ Wand, Boden, Dach -- wie ein Puppenhaus
|
||||
→ Top-Down: Man sieht nur den Grundriss
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 3. Das Pixel-Grid-System
|
||||
|
||||
### Dreistufig (wie 3D, aber in 2D)
|
||||
|
||||
```
|
||||
STUFE 1: Welt-Pixel (10cm = 10 Pixel/Meter) ← MVP
|
||||
Terrain, Gebaeude, grosse Strukturen
|
||||
Max Welt: 500m × 200m = 5.000 × 2.000 Pixel = 10M Pixel
|
||||
Das ist RIESIG fuer 2D (Terraria: ~8.400 × 2.400)
|
||||
|
||||
STUFE 2: Detail-Zonen (5cm = 20 Pixel/Meter) ← Post-MVP
|
||||
Altaere, Vitrinen, Mechanismen
|
||||
Max 5 Zonen, je 10m × 10m
|
||||
|
||||
STUFE 3: Sprite-Editor (1-2 Pixel/cm = frei) ← Post-MVP
|
||||
Items, Charaktere, Dekorationen
|
||||
64×64 oder 128×128 Canvas
|
||||
Wie ein Mini-Aseprite im Spiel
|
||||
```
|
||||
|
||||
### Resolution-Vergleich (2D vs 3D)
|
||||
|
||||
```
|
||||
Gleiche physische Groesse: 10cm
|
||||
|
||||
3D bei 10cm:
|
||||
Ein Wuerfel: 1 Voxel = u16 = 2 Bytes
|
||||
Ein Raum (3m × 4m × 3m): 30 × 40 × 30 = 36.000 Voxel = 72 KB
|
||||
|
||||
2D bei 10cm:
|
||||
Ein Quadrat: 1 Pixel = u16 = 2 Bytes
|
||||
Ein Raum (3m × 4m): 30 × 40 = 1.200 Pixel = 2.4 KB
|
||||
|
||||
→ 2D braucht ~30× WENIGER Speicher als 3D
|
||||
→ Bei gleichem Budget: 30× groessere Welten moeglich!
|
||||
```
|
||||
|
||||
### Welten-Groessen in 2D
|
||||
|
||||
| Welt | Pixel-Groesse | Speicher (roh) | Speicher (kompr.) | Vergleich |
|
||||
| -------------------------------- | ----------------- | -------------- | ----------------- | ----------- |
|
||||
| Zimmer (5m × 4m) | 50 × 40 | 4 KB | <1 KB | Trivial |
|
||||
| Haus (20m × 15m) | 200 × 150 | 60 KB | ~5 KB | Trivial |
|
||||
| Dungeon (100m × 50m) | 1.000 × 500 | 1 MB | ~50 KB | Winzig |
|
||||
| Dorf (200m × 100m) | 2.000 × 1.000 | 4 MB | ~200 KB | Klein |
|
||||
| **Terraria-Gross (500m × 200m)** | **5.000 × 2.000** | **20 MB** | **~1 MB** | **Locker** |
|
||||
| Mega-Welt (1.000m × 300m) | 10.000 × 3.000 | 60 MB | ~3 MB | Moeglich |
|
||||
| Ultra (2.000m × 500m) | 20.000 × 5.000 | 200 MB | ~10 MB | Grenzwertig |
|
||||
|
||||
**In 2D koennen Welten 10-30× groesser sein als in 3D bei gleichem Budget!**
|
||||
|
||||
Eine Terraria-grosse Welt (500m × 200m) braucht komprimiert ~1 MB. Das ist nichts.
|
||||
|
||||
---
|
||||
|
||||
## 4. Der Pixel-Editor (In-Game)
|
||||
|
||||
### Welt-Editor (Side-Scroller)
|
||||
|
||||
```
|
||||
┌────────────────────────────────────────────────────┐
|
||||
│ [Pinsel][Radierer][Fuellen][Pipette][Box][Linie] │
|
||||
│ [Auswahl][Kopieren][Einfuegen][Undo][Redo] │
|
||||
├────────────────────────────────────────────────────┤
|
||||
│ │
|
||||
│ ☀️ │
|
||||
│ │
|
||||
│ ┌─────────────┐ Cursor: ▓ │
|
||||
│ │ │ (zeigt wo Pixel gesetzt │
|
||||
│ │ HAUS │ wird) │
|
||||
│ │ │ │
|
||||
│ ███████│ ┌──┐ ┌──┐│██████████████ │
|
||||
│ ███████│ │ │ │ ││██████████████ │
|
||||
│ ███████└──┘──└──┘──┘██████████████ │
|
||||
│ ██████████████████████████████████ │
|
||||
│ ██████████████████████████████████ │
|
||||
│ │
|
||||
├────────────────────────────────────────────────────┤
|
||||
│ Palette: [█ Stein][█ Holz][█ Gras][█ Sand][...] │
|
||||
│ Layer: [Hintergrund ▼] [Vordergrund ▼] │
|
||||
│ Zoom: [−] ████████████ [+] 100% │
|
||||
└────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
### Sprite-Editor (Items + Charaktere)
|
||||
|
||||
```
|
||||
┌───────────────────────────────────────────────┐
|
||||
│ SPRITE EDITOR: "Flammenklinge" │
|
||||
├───────────────────────────────────────────────┤
|
||||
│ │
|
||||
│ ┌──────────────────────┐ ┌───────────────┐ │
|
||||
│ │ ░░░░░░░▓░░░░░░░░░░░ │ │ Vorschau: │ │
|
||||
│ │ ░░░░░░▓▓▓░░░░░░░░░░ │ │ │ │
|
||||
│ │ ░░░░░▓███▓░░░░░░░░░ │ │ 🗡️ │ │
|
||||
│ │ ░░░░▓█████▓░░░░░░░░ │ │ (animiert) │ │
|
||||
│ │ ░░░░░▓███▓░░░░░░░░░ │ │ │ │
|
||||
│ │ ░░░░░░▓█▓░░░░░░░░░░ │ │ Frames: 1/4 │ │
|
||||
│ │ ░░░░░░░▓░░░░░░░░░░░ │ │ [▶][⏸][⏹] │ │
|
||||
│ │ ░░░░░░░▓░░░░░░░░░░░ │ └───────────────┘ │
|
||||
│ │ ░░░░░░░▓░░░░░░░░░░░ │ │
|
||||
│ │ ░░░░░░▓▓▓░░░░░░░░░░ │ Werkzeuge: │
|
||||
│ │ ░░░░░▓▓▓▓▓░░░░░░░░░ │ [Pinsel][Radierer] │
|
||||
│ │ ░░░░░░░▓░░░░░░░░░░░ │ [Fuellen][Spiegeln]│
|
||||
│ │ ░░░░░░░▓░░░░░░░░░░░ │ [Frame +][Frame -] │
|
||||
│ └──────────────────────┘ │
|
||||
│ Canvas: 16 × 32 Pixel │
|
||||
│ Palette: [█][█][█][█][█][█][█][█][+Custom] │
|
||||
│ Anchor: [Mitte-Unten ▼] │
|
||||
│ Hitbox: [Auto ▼] │
|
||||
├───────────────────────────────────────────────┤
|
||||
│ [Testen] [Properties] [Speichern] │
|
||||
└───────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
**Sprite-Groessen:**
|
||||
|
||||
| Item-Typ | Canvas | Beispiel |
|
||||
| ----------------------------- | ---------------------- | ------------------- |
|
||||
| Klein (Ring, Muenze) | 8 × 8 | 64 Pixel, 128 Bytes |
|
||||
| Mittel (Schwert, Trank) | 16 × 32 | 512 Pixel, 1 KB |
|
||||
| Gross (Schild, Ruestung) | 32 × 32 | 1.024 Pixel, 2 KB |
|
||||
| Charakter | 32 × 48 | 1.536 Pixel, 3 KB |
|
||||
| Charakter animiert (4 Frames) | 128 × 48 (Spritesheet) | 6.144 Pixel, 12 KB |
|
||||
| Boss / Grosses Objekt | 64 × 64 | 4.096 Pixel, 8 KB |
|
||||
|
||||
Items sind in 2D winzig. Hunderte Items = wenige KB. Kein Performance-Problem, nie.
|
||||
|
||||
---
|
||||
|
||||
## 5. Programmierbare Items (identisch zu 3D)
|
||||
|
||||
Die drei Programmier-Ebenen funktionieren exakt gleich:
|
||||
|
||||
### Ebene 1: Sliders
|
||||
|
||||
```
|
||||
Gleiches UI wie in 3D.
|
||||
Schaden, Element, Haltbarkeit, Reichweite, Sound, Partikel, Seltenheit.
|
||||
```
|
||||
|
||||
### Ebene 2: Trigger-Actions
|
||||
|
||||
```
|
||||
Gleiche Trigger und Actions, aber 2D-spezifisch:
|
||||
|
||||
TRIGGER (2D):
|
||||
Spieler beruehrt, Item benutzt, Timer, HP unter X,
|
||||
Spieler springt auf Item, Spieler steht drauf,
|
||||
Pixel darunter zerstoert, Regen, Nacht...
|
||||
|
||||
ACTIONS (2D):
|
||||
Schaden, Partikel (2D), Sound, Teleportieren,
|
||||
Pixel setzen/loeschen, Gravitation an/aus,
|
||||
Licht erzeugen, Wasser fliessen lassen,
|
||||
Explosion (Pixel-Krater), Nachricht anzeigen...
|
||||
```
|
||||
|
||||
### Ebene 3: TypeScript
|
||||
|
||||
```typescript
|
||||
import { onUse, damage, particles, voxel } from '@manavoxel/api';
|
||||
|
||||
onUse((event) => {
|
||||
// Schwert-Hieb: Bogen von Pixeln zerstoeren
|
||||
const arc = getArc(player.position, player.facing, 3);
|
||||
for (const pos of arc) {
|
||||
voxel.destroy(pos);
|
||||
particles.spawn('spark', pos);
|
||||
}
|
||||
damage.area(arc, 15);
|
||||
});
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 6. Technischer Stack (2D)
|
||||
|
||||
### Architektur
|
||||
|
||||
```
|
||||
╔═══════════════════════════════════════════════════╗
|
||||
║ BROWSER (Zero Install, PWA) ║
|
||||
║ ║
|
||||
║ ┌──────────────────┐ ┌──────────────────────┐ ║
|
||||
║ │ PixiJS 8 │ │ Svelte 5 UI │ ║
|
||||
║ │ (WebGL/WebGPU) │ │ │ ║
|
||||
║ │ Tilemap Renderer │ │ Pixel-Editor │ ║
|
||||
║ │ Sprite Renderer │ │ Sprite-Editor │ ║
|
||||
║ │ 2D Lighting │ │ Property Sliders │ ║
|
||||
║ │ Particle System │ │ Trigger-Actions │ ║
|
||||
║ │ Planck.js (Physik)│ │ Code Editor │ ║
|
||||
║ └────────┬─────────┘ └──────────┬───────────┘ ║
|
||||
║ │ │ ║
|
||||
║ ┌────────▼────────────────────────▼────────────┐║
|
||||
║ │ Dexie.js -- Local-First ║║
|
||||
║ └──────────────────┬───────────────────────────┘║
|
||||
╚═════════════════════╪═════════════════════════════╝
|
||||
│ WebSocket + REST
|
||||
╔═════════════════════╪═════════════════════════════╗
|
||||
║ GAME SERVER (Go oder Bun/Hono) ║
|
||||
║ Welt-State, Physik-Validierung, Script-Sandbox ║
|
||||
╠═══════════════════════════════════════════════════╣
|
||||
║ BACKEND (bestehende Services, unveraendert) ║
|
||||
╠═══════════════════════════════════════════════════╣
|
||||
║ DATA: PostgreSQL │ TigerBeetle │ Redis │ MinIO ║
|
||||
╚═══════════════════════════════════════════════════╝
|
||||
```
|
||||
|
||||
### Stack-Vergleich: 2D vs 3D
|
||||
|
||||
| Schicht | 3D Stack | 2D Stack | Aenderung |
|
||||
| ------------------ | ------------------------ | ----------------------- | --------------------- |
|
||||
| **Sprache Engine** | Rust (Bevy) | TypeScript (PixiJS) | **Kein Rust noetig!** |
|
||||
| **Rendering** | wgpu → WebGPU | PixiJS → WebGL/Canvas | Viel simpler |
|
||||
| **Physik** | Rapier (Rust) | Planck.js oder Rapier2D | JS-nativ |
|
||||
| **Meshing** | Greedy Meshing (komplex) | Tilemap (trivial) | Entfaellt fast |
|
||||
| **Lighting** | Shadow Maps, SSAO | 2D Raycast Lighting | Viel simpler |
|
||||
| **Networking** | WebTransport (QUIC) | WebSocket (reicht!) | Simpler |
|
||||
| **Game Server** | Rust (Bevy headless) | **Go oder Bun** | Kein Rust! |
|
||||
| **Scripting** | WASM (Wasmtime) | WASM oder V8 Isolates | Flexibler |
|
||||
| **UI** | Svelte 5 | Svelte 5 | Gleich |
|
||||
| **Local-First** | Dexie.js | Dexie.js | Gleich |
|
||||
| **Auth** | mana-auth | mana-auth | Gleich |
|
||||
| **Economy** | TigerBeetle | TigerBeetle | Gleich |
|
||||
| **AI** | mana-llm, etc. | mana-llm, etc. | Gleich |
|
||||
|
||||
**Der groesste Unterschied: Kein Rust.** Der gesamte Engine-Layer wird TypeScript -- eine Sprache die wir bereits perfekt beherrschen.
|
||||
|
||||
### Game Server in 2D: Go oder Bun?
|
||||
|
||||
**Option 1: Go (empfohlen)**
|
||||
|
||||
```
|
||||
mana-game-server (Go):
|
||||
├── 1 Goroutine pro Welt-Instanz
|
||||
├── Welt-State als 2D-Array im Memory
|
||||
├── Physik: Einfache 2D-Kollision (eigene Impl, ~200 Zeilen)
|
||||
├── Scripting: wazero (WASM in Go) ODER
|
||||
├── Scripting: Deno-Subprocess (V8 Isolate)
|
||||
├── Networking: gorilla/websocket (bestehend)
|
||||
└── Persistenz: PostgreSQL + mana-sync
|
||||
|
||||
Vorteile: Bestehende Expertise, goroutines perfekt fuer viele Instanzen,
|
||||
kein GC-Problem bei 20-30 Ticks/sec (2D braucht keine 60Hz Server)
|
||||
```
|
||||
|
||||
**Option 2: Bun/Hono**
|
||||
|
||||
```
|
||||
mana-game-server (Bun + Hono):
|
||||
├── Worker pro Welt-Instanz
|
||||
├── Gleiche Sprache wie Client (TypeScript)
|
||||
├── Physik: Planck.js (gleicher Code Client+Server!)
|
||||
├── Scripting: V8 Isolate (nativ in Bun)
|
||||
└── Networking: Bun WebSocket (eingebaut, sehr schnell)
|
||||
|
||||
Vorteile: Ein Language fuer alles, Code-Sharing Client/Server,
|
||||
Bun-WebSocket ist extrem performant
|
||||
```
|
||||
|
||||
**Empfehlung: Go fuer Server** -- konsistent mit unserer Architektur, goroutines skalieren besser als Bun-Worker, bewaehertes Pattern aus mana-sync.
|
||||
|
||||
---
|
||||
|
||||
## 7. Performance-Vergleich: 2D vs 3D
|
||||
|
||||
### Rendering
|
||||
|
||||
```
|
||||
3D (Bevy + WebGPU):
|
||||
500 Chunks × Greedy Mesh = ~1.8M Dreiecke
|
||||
GPU-intensiv, WebGPU noetig
|
||||
60fps Desktop, 30fps Mobile
|
||||
|
||||
2D (PixiJS + WebGL):
|
||||
5.000 × 2.000 Tilemap = 10M Tiles (aber nur ~2.000 sichtbar)
|
||||
GPU-trivial, WebGL reicht (laeuft ueberall)
|
||||
60fps auf ALLEM (sogar alte Handys)
|
||||
```
|
||||
|
||||
### Memory
|
||||
|
||||
```
|
||||
3D Welt (32m × 32m × 16m bei 10cm):
|
||||
50M Voxel × 2 Bytes = ~100 MB roh, ~5-30 MB komprimiert
|
||||
+ Meshes: ~50 MB GPU
|
||||
= ~80 MB total
|
||||
|
||||
2D Welt (500m × 200m bei 10cm):
|
||||
5.000 × 2.000 Pixel × 2 Bytes = 20 MB roh, ~1 MB komprimiert
|
||||
+ Sprites: ~5 MB GPU
|
||||
= ~6 MB total
|
||||
|
||||
→ 2D braucht ~13× weniger Speicher
|
||||
```
|
||||
|
||||
### Netzwerk
|
||||
|
||||
```
|
||||
3D: 500 Chunks initial = ~2.5 MB komprimiert
|
||||
2D: Ganze Welt initial = ~1 MB komprimiert (oder Chunk-weise ~100 KB)
|
||||
|
||||
2D ist ~2-3× kleiner ueber die Leitung
|
||||
```
|
||||
|
||||
### Physik
|
||||
|
||||
```
|
||||
3D: Rapier 3D, 240Hz moeglich aber teuer
|
||||
2D: Planck.js oder simple AABB, 60Hz trivial
|
||||
|
||||
2D Physik ist ~10-50× billiger
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 8. Entwicklungszeit-Vergleich
|
||||
|
||||
### 3D (aus GAME-PLAN.md)
|
||||
|
||||
```
|
||||
Phase 0: Proof of Concept 6-8 Wochen Bevy + WebGPU + Greedy Meshing
|
||||
Phase 1: Item-System 6-8 Wochen Editor, Properties, WASM
|
||||
Phase 2: Multiplayer 4-6 Wochen Rust Server, WebTransport
|
||||
Phase 3: Platform 4-6 Wochen Auth, Economy, Discovery
|
||||
─────────────
|
||||
20-28 Wochen (5-7 Monate)
|
||||
|
||||
+ Rust lernen: 8-12 Wochen
|
||||
─────────────
|
||||
TOTAL: 28-40 Wochen (7-10 Monate)
|
||||
```
|
||||
|
||||
### 2D
|
||||
|
||||
```
|
||||
Phase 0: Proof of Concept 2-3 Wochen PixiJS + Tilemap + Editor
|
||||
Phase 1: Item-System 3-4 Wochen Sprite-Editor, Properties, WASM
|
||||
Phase 2: Multiplayer 2-3 Wochen Go Server, WebSocket
|
||||
Phase 3: Platform 3-4 Wochen Auth, Economy, Discovery
|
||||
─────────────
|
||||
10-14 Wochen (2.5-3.5 Monate)
|
||||
|
||||
+ Rust lernen: 0 Wochen (nicht noetig!)
|
||||
─────────────
|
||||
TOTAL: 10-14 Wochen (2.5-3.5 Monate)
|
||||
```
|
||||
|
||||
### Warum 2D so viel schneller ist
|
||||
|
||||
| Aufgabe | 3D Aufwand | 2D Aufwand | Faktor |
|
||||
| ------------------ | ------------------------------------ | ---------------------------- | ------ |
|
||||
| Rendering Engine | Bevy+wgpu+WebGPU+Meshing (Wochen) | PixiJS Tilemap (Tage) | ~5× |
|
||||
| Physik | Rapier 3D Collision (komplex) | AABB 2D (200 Zeilen) | ~10× |
|
||||
| Kamera | Orbit+Fly+FPS (3 Modi) | Scroll+Zoom (trivial) | ~5× |
|
||||
| Voxel/Pixel Editor | 3D Raycast+Cursor (schwer) | 2D Mausklick→Pixel (einfach) | ~3× |
|
||||
| Level of Detail | Octree LOD, Chunk Streaming | Nicht noetig (2D ist billig) | ~∞ |
|
||||
| Beleuchtung | Shadow Maps, SSAO, Future Is Bright | 2D Raycast (optional) | ~5× |
|
||||
| Sprite-Editor | MagicaVoxel-aehnlich, 3D Rotation | Aseprite-aehnlich, flat | ~2× |
|
||||
| Server | Rust/Bevy headless + Go Orchestrator | Go Server (einfach) | ~3× |
|
||||
| Cross-Platform | WebGPU-Kompatibilitaet (Safari...) | WebGL (ueberall, sofort) | ~3× |
|
||||
| **Rust lernen** | **8-12 Wochen** | **0** | **∞** |
|
||||
|
||||
---
|
||||
|
||||
## 9. Vor- und Nachteile: Die ehrliche Tabelle
|
||||
|
||||
### 2D Vorteile
|
||||
|
||||
| Vorteil | Detail |
|
||||
| ----------------------------------- | ------------------------------------------------- |
|
||||
| **3× schnellere Entwicklung** | 2.5-3.5 Monate statt 7-10 |
|
||||
| **Kein Rust lernen** | Alles in TypeScript + Go (beherrscht) |
|
||||
| **Laeuft ueberall** | WebGL auf jedem Browser, jedem Handy, sogar alten |
|
||||
| **Viel groessere Welten** | 500m × 200m statt 32m × 32m bei gleichem Budget |
|
||||
| **Simpelere Physik** | 2D Collision in 200 Zeilen, keine 3D-Ecken |
|
||||
| **Einfachere Kamera** | Scroll + Zoom statt Orbit + Fly + FPS |
|
||||
| **Kein LOD noetig** | 2D ist billig genug ohne Optimierung |
|
||||
| **Bewiesenes Genre** | Terraria: 58M Kopien, Stardew Valley: 30M+ |
|
||||
| **Geringere Barriere fuer Creator** | Pixel Art ist zugaenglicher als 3D-Modelling |
|
||||
| **Sprite-Editor simpler** | 2D-Zeichnen ist einfacher als 3D-Sculpting |
|
||||
| **Server in Go** | Keine zweite Systemsprache, bestehende Expertise |
|
||||
| **Code-Sharing** | TypeScript auf Client UND (optional) Server |
|
||||
| **Mobile-Performance** | 60fps auf 5 Jahre alten Handys |
|
||||
| **Barrierefreiheit** | Pixel Art ist leichter zu erlernen als 3D |
|
||||
|
||||
### 2D Nachteile
|
||||
|
||||
| Nachteil | Detail |
|
||||
| ------------------------------- | ----------------------------------------------------- |
|
||||
| **Weniger "wow"** | 3D beeindruckt mehr beim ersten Sehen |
|
||||
| **Kein Tiefengefuehl** | Keine Perspektive, kein "in die Welt eintauchen" |
|
||||
| **Weniger immersiv** | First-Person-Erlebnis nicht moeglich |
|
||||
| **"Nur ein Terraria-Klon"** | Vergleich ist unvermeidlich |
|
||||
| **Weniger Bau-Freiheit** | In 3D kann man um Objekte herumgehen |
|
||||
| **Limitierte Kamera** | Immer gleiche Perspektive |
|
||||
| **Items weniger beeindruckend** | Pixel-Schwert vs. 3D-Voxel-Schwert |
|
||||
| **Weniger Innovation** | 2D+Pixel ist bekannt, 3D+Voxel+Programmierbar ist neu |
|
||||
| **Zielgruppe kleiner?** | Gen Alpha erwartet 3D (Roblox/Minecraft) |
|
||||
| **Kein VR/AR** | 2D in VR macht keinen Sinn |
|
||||
|
||||
### 3D Vorteile (was man aufgibt)
|
||||
|
||||
| Was 3D kann, 2D nicht | Impact |
|
||||
| ------------------------ | ------------------------- |
|
||||
| Um Objekte herumgehen | Hoch -- immersiver |
|
||||
| First-Person-Perspektive | Hoch -- "drin sein" |
|
||||
| 3D Voxel-Skulpturen | Mittel -- beeindruckender |
|
||||
| VR/AR-faehig | Niedrig (kleiner Markt) |
|
||||
| Technisch beeindruckend | Marketing-Vorteil |
|
||||
|
||||
### 3D Nachteile (was man sich spart)
|
||||
|
||||
| Was 3D schwer macht | Impact |
|
||||
| ----------------------- | -------------------------------- |
|
||||
| Rust lernen | 8-12 Wochen, signifikante Huerde |
|
||||
| WebGPU-Kompatibilitaet | Safari, alte Browser, Mobile |
|
||||
| 3D-Physik Komplexitaet | Monate an Edge Cases |
|
||||
| Greedy Meshing + LOD | Komplexer Rendering-Code |
|
||||
| 3D-Kamera-Steuerung | UX-Challenge (besonders Mobile) |
|
||||
| Performance-Optimierung | Endloser Aufwand |
|
||||
|
||||
---
|
||||
|
||||
## 10. Der Hybrid-Weg: 2D starten, 3D spaeter?
|
||||
|
||||
### Ist ein Uebergang moeglich?
|
||||
|
||||
```
|
||||
Strategie: 2D MVP → 3D spaeter
|
||||
|
||||
Phase 1 (Monat 1-4): 2D-Plattform (PixiJS + Go)
|
||||
→ Proof of Concept, Community aufbauen, Feedback sammeln
|
||||
→ Creator-Economy validieren
|
||||
→ Gameplay-Loop testen (Bauen → Programmieren → Teilen)
|
||||
|
||||
Phase 2 (Monat 5-12): Parallel 3D-Engine entwickeln
|
||||
→ Rust/Bevy lernen waehrend 2D-Version lebt
|
||||
→ 3D-Engine als separates Projekt
|
||||
→ 2D-Welten → 3D-Konverter (Pixel → Voxel-Scheibe)
|
||||
|
||||
Phase 3 (Monat 12+): 3D-Version launchen
|
||||
→ Bestehende Accounts, Economy, Items migrieren
|
||||
→ 2D-Version bleibt als "Classic" / "Lite" erhalten
|
||||
→ Items: 2D-Sprites werden zu 3D-Voxel extrudiert
|
||||
```
|
||||
|
||||
**Problem: Item-Migration**
|
||||
|
||||
```
|
||||
2D Schwert (16 × 32 Pixel): 3D Schwert (3 × 1 × 15 Voxel):
|
||||
▓ Komplett anderes Format.
|
||||
▓▓▓ Man kann 2D → 3D extrudieren
|
||||
▓███▓ (Pixel → 1 Voxel tief),
|
||||
▓█████▓ aber es sieht "flach" aus.
|
||||
▓███▓
|
||||
▓█▓ Creator muessten Items
|
||||
▓ manuell in 3D neu bauen.
|
||||
▓
|
||||
▓▓▓
|
||||
```
|
||||
|
||||
**Ehrliche Einschaetzung:** Der Uebergang ist **moeglich aber schmerzhaft**. Backend (Auth, Economy, Social) migriert trivial. Content (Welten, Items) muss neu erstellt oder automatisch konvertiert werden. Automatische Konvertierung (2D→3D) erzeugt nur maessige Qualitaet.
|
||||
|
||||
### Besser: Sich fuer einen Weg entscheiden
|
||||
|
||||
```
|
||||
WENN das Ziel ist: Schnell launchen, Community validieren, iterieren
|
||||
→ 2D. In 3 Monaten spielbar, in 6 Monaten polished.
|
||||
|
||||
WENN das Ziel ist: Langfristig die beeindruckendste UGC-Plattform
|
||||
→ 3D. In 7-10 Monaten spielbar, aber technisch ueberlegener.
|
||||
|
||||
WENN das Ziel ist: Beides testen
|
||||
→ 2D zuerst (3 Monate), dann entscheiden ob 3D-Investment lohnt.
|
||||
Die Backend-Services (Auth, Economy, AI) sind identisch.
|
||||
Nur Engine + Editor muessen neu.
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 11. 2D MVP Build-Plan
|
||||
|
||||
### Phase 0: Engine + Editor (3 Wochen)
|
||||
|
||||
```
|
||||
Woche 1: PixiJS Setup + Tilemap
|
||||
□ PixiJS 8 + SvelteKit-Integration
|
||||
□ Tilemap-Renderer (Chunk-basiert, 32×32 Tiles pro Chunk)
|
||||
□ Kamera: Scroll + Zoom
|
||||
□ 10 Basis-Materialien
|
||||
|
||||
Woche 2: Pixel-Editor
|
||||
□ Mausklick → Pixel setzen/loeschen
|
||||
□ Pinsel, Radierer, Fuellen, Pipette
|
||||
□ Material-Palette
|
||||
□ Undo/Redo
|
||||
□ Hintergrund/Vordergrund Layer
|
||||
|
||||
Woche 3: Spieler + Physik
|
||||
□ Charakter-Sprite (Standard)
|
||||
□ 2D-Kollision mit Tilemap
|
||||
□ Gravitation, Laufen, Springen
|
||||
□ Simple Beleuchtung (optional)
|
||||
|
||||
Ergebnis: Terraria-Creative im Browser, 60fps
|
||||
```
|
||||
|
||||
### Phase 1: Items (4 Wochen)
|
||||
|
||||
```
|
||||
Woche 4-5: Sprite-Editor + Items
|
||||
□ In-Game Sprite-Editor (16×32, 32×32, 64×64)
|
||||
□ Pixel-fuer-Pixel zeichnen
|
||||
□ Animation Frames (Spritesheet)
|
||||
□ "Als Item speichern"
|
||||
□ Item im Inventar, in der Hand halten
|
||||
|
||||
Woche 6: Properties + Trigger-Actions
|
||||
□ Property Sliders (Ebene 1)
|
||||
□ Trigger-Action Editor (Ebene 2)
|
||||
□ Trigger → WASM Kompilierung
|
||||
□ WASM Sandbox (wazero in Go oder V8 in Bun)
|
||||
|
||||
Woche 7: Items in der Welt
|
||||
□ Item benutzen (Primary/Secondary Action)
|
||||
□ Items ablegen / aufheben
|
||||
□ Partikel-System (2D)
|
||||
□ Sound-System
|
||||
□ Pixel-Zerstoerung (Explosion → Krater)
|
||||
|
||||
Ergebnis: Schwert zeichnen → Feuer-Schaden geben → benutzen → Pixel splittern
|
||||
```
|
||||
|
||||
### Phase 2: Multiplayer (3 Wochen)
|
||||
|
||||
```
|
||||
Woche 8: Go Game Server
|
||||
□ mana-game-server (Go)
|
||||
□ Welt-State im Memory (2D-Array)
|
||||
□ WebSocket (coder/websocket, wie mana-sync)
|
||||
□ Pixel-Sync (Delta-Kompression)
|
||||
|
||||
Woche 9: Multiplayer-Sync
|
||||
□ Spieler-Positionen synchronisieren
|
||||
□ Voxel-Aenderungen broadcasten
|
||||
□ Server-autoritativ (Validierung)
|
||||
□ Andere Spieler sehen
|
||||
|
||||
Woche 10: Multiplayer-Features
|
||||
□ Item-Sync + Inventar
|
||||
□ PvP (Items benutzen gegen andere)
|
||||
□ Chat
|
||||
□ Welt-Link teilen
|
||||
|
||||
Ergebnis: 2-20 Spieler bauen und kaempfen zusammen
|
||||
```
|
||||
|
||||
### Phase 3: Platform (4 Wochen)
|
||||
|
||||
```
|
||||
Woche 11-12: Auth + Persistence
|
||||
□ mana-auth Integration
|
||||
□ PostgreSQL Persistence
|
||||
□ Local-First (Dexie.js ↔ mana-sync)
|
||||
□ Guest Mode
|
||||
|
||||
Woche 13: Discovery + Economy
|
||||
□ Welt veroeffentlichen
|
||||
□ Welt-Suche (Meilisearch)
|
||||
□ TigerBeetle Economy
|
||||
□ Item Marketplace
|
||||
|
||||
Woche 14: Polish
|
||||
□ Onboarding Tutorial
|
||||
□ Welt-Templates (Hoehle, Insel, Haus, Arena)
|
||||
□ Mobile-Optimierung
|
||||
□ Bug Fixes
|
||||
|
||||
Ergebnis: MVP! Spielbare 2D-Pixel-Plattform im Browser.
|
||||
```
|
||||
|
||||
### Timeline-Vergleich
|
||||
|
||||
```
|
||||
Monat 1 Monat 2 Monat 3 Monat 4 Monat 5-7 Monat 8-10
|
||||
|
||||
2D: [Engine ] [Items ] [Multi] [Platform] Post-MVP Post-MVP
|
||||
[Editor ] [Props ] [player] [Polish ] (Detail, (3D??)
|
||||
[Trigger ] AI, etc.)
|
||||
|
||||
3D: [Rust lernen ] [Engine ] [Engine ] [Items ] [Multi ]
|
||||
[Chunks ] [Meshing ] [Editor ] [Platf.]
|
||||
[Physik ] [Camera ] [Trigger ] [Polish]
|
||||
|
||||
2D spielbar nach: ~10 Wochen (2.5 Monate)
|
||||
3D spielbar nach: ~28 Wochen (7 Monate)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 12. Entscheidungshilfe
|
||||
|
||||
### Wann 2D die richtige Wahl ist
|
||||
|
||||
```
|
||||
✓ Du willst in 3 Monaten etwas Spielbares
|
||||
✓ Du willst kein Rust lernen (jetzt)
|
||||
✓ Du willst das Konzept validieren bevor du 10 Monate investierst
|
||||
✓ Deine Zielgruppe sind Creator die Pixel Art moegen
|
||||
✓ Du willst auf JEDEM Geraet laufen (auch alte Handys)
|
||||
✓ Du willst grosse Welten (500m+) bei niedrigen Kosten
|
||||
✓ Du planst eventuell spaeter eine 3D-Version
|
||||
```
|
||||
|
||||
### Wann 3D die richtige Wahl ist
|
||||
|
||||
```
|
||||
✓ Du willst die langfristig beeindruckendste Plattform
|
||||
✓ Du bist bereit, Rust zu lernen
|
||||
✓ Deine Zielgruppe erwartet 3D (Gen Alpha, Roblox-Spieler)
|
||||
✓ Du willst VR/AR-ready sein
|
||||
✓ Du willst dich maximal differenzieren (3D+Voxel+Programmierbar ist einzigartig)
|
||||
✓ Du hast 7-10 Monate Zeit bis zum MVP
|
||||
```
|
||||
|
||||
### Die pragmatische Empfehlung
|
||||
|
||||
```
|
||||
OPTION: 2D zuerst, 3D evaluieren
|
||||
|
||||
Monat 1-3.5: 2D MVP bauen und launchen
|
||||
Monat 4-6: Community aufbauen, Feedback, iterieren
|
||||
Monat 5: Rust-Lernphase starten (parallel)
|
||||
Monat 7: Entscheidung: Lohnt sich 3D-Investment?
|
||||
JA → 3D-Engine parallel entwickeln
|
||||
NEIN → 2D weiter ausbauen
|
||||
|
||||
Das Backend (Auth, Economy, AI, Sync) ist IDENTISCH.
|
||||
Nur Engine + Editor aendern sich.
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 13. Zusammenfassung
|
||||
|
||||
```
|
||||
2D (PixiJS) 3D (Bevy)
|
||||
────────────────────── ─────────────────── ──────────────────
|
||||
Entwicklungszeit 2.5-3.5 Monate 7-10 Monate
|
||||
Neue Sprachen Keine Rust
|
||||
Performance 60fps ueberall 60fps Desktop
|
||||
Browser-Support Alle (WebGL) Modern (WebGPU)
|
||||
Weltgroesse 500m × 200m 64m × 64m × 32m
|
||||
Speicher ~6 MB ~80 MB
|
||||
Komplexitaet Niedrig Hoch
|
||||
Wow-Faktor Mittel Hoch
|
||||
Genre-Vergleich Terraria+Roblox Minecraft+Roblox
|
||||
Zielgruppe Pixel-Art-Fans 3D-Voxel-Fans
|
||||
Items 2D Sprites 3D Voxel-Skulpturen
|
||||
Programmierung Identisch Identisch
|
||||
Economy Identisch Identisch
|
||||
AI Identisch Identisch
|
||||
Self-Hosting Identisch Identisch
|
||||
Mobile Perfekt Eingeschraenkt
|
||||
Risiko Niedrig Mittel-Hoch
|
||||
```
|
||||
|
||||
**Kern-Erkenntnis:** Das was ManaVoxel einzigartig macht -- programmierbare Items, Creator Economy, In-Game-Editor, Local-First, Browser-nativ -- funktioniert in 2D genauso gut wie in 3D. Die dritte Dimension macht es beeindruckender, aber nicht besser.
|
||||
|
||||
---
|
||||
|
||||
_Erstellt: 28. Maerz 2026_
|
||||
1019
NewAppIdeas/Roblox Reimagined/GAME-PLAN.md
Normal file
1019
NewAppIdeas/Roblox Reimagined/GAME-PLAN.md
Normal file
File diff suppressed because it is too large
Load diff
1138
NewAppIdeas/Roblox Reimagined/MVP-Voxel-Platform.md
Normal file
1138
NewAppIdeas/Roblox Reimagined/MVP-Voxel-Platform.md
Normal file
File diff suppressed because it is too large
Load diff
677
NewAppIdeas/Roblox Reimagined/Optimaler-Stack.md
Normal file
677
NewAppIdeas/Roblox Reimagined/Optimaler-Stack.md
Normal file
|
|
@ -0,0 +1,677 @@
|
|||
# Roblox Reimagined: Der optimale Stack
|
||||
|
||||
## Keine Kompromisse -- das technisch beste Tool fuer jeden Job
|
||||
|
||||
---
|
||||
|
||||
## Die Leitprinzipien
|
||||
|
||||
1. **Browser-First** -- Zero Install ist der groesste Wettbewerbsvorteil gegenueber Roblox
|
||||
2. **Self-Hosted-First** -- Volle Kontrolle, keine Vendor-Abhaengigkeit, DSGVO-konform
|
||||
3. **Local-First** -- Offline-Editor, Guest Mode, Instant UI (unser bestehendes Differenzierungsmerkmal)
|
||||
4. **Beste Sprache pro Domaene** -- Rust fuer Engine, Go fuer Infrastruktur, TypeScript fuer UI, Python fuer AI
|
||||
5. **Maximale Sicherheit** -- WASM-Sandbox, kein User-Code ausserhalb der Sandbox
|
||||
|
||||
---
|
||||
|
||||
## Der Stack
|
||||
|
||||
```
|
||||
╔══════════════════════════════════════════════════════════════════╗
|
||||
║ BROWSER CLIENT ║
|
||||
║ ║
|
||||
║ ┌─────────────────────────────────┐ ┌────────────────────────┐ ║
|
||||
║ │ Bevy Engine (Rust → WASM) │ │ Svelte 5 UI Layer │ ║
|
||||
║ │ │ │ │ ║
|
||||
║ │ wgpu → WebGPU (Rendering) │ │ Editor (3D + Visual │ ║
|
||||
║ │ Rapier (Physik, nativ Rust) │ │ + Code) │ ║
|
||||
║ │ bevy_ecs (State Management) │ │ Marketplace │ ║
|
||||
║ │ Quinn → WebTransport (Netz) │ │ Social / Chat │ ║
|
||||
║ │ Wasmtime (User-Script-Sandbox) │ │ Profile / Avatar │ ║
|
||||
║ │ │ │ Monaco (Code Editor) │ ║
|
||||
║ └──────────┬───────────────────────┘ └───────────┬────────────┘ ║
|
||||
║ │ │ ║
|
||||
║ ┌──────────▼──────────────────────────────────────▼────────────┐║
|
||||
║ │ Dexie.js (IndexedDB) -- Local-First Layer ║║
|
||||
║ │ Games, Assets, Fortschritt, Avatar, Settings (alles offline) ║║
|
||||
║ └───────────────────────────┬──────────────────────────────────┘║
|
||||
╚══════════════════════════════╪═══════════════════════════════════╝
|
||||
│ WebTransport (QUIC) + REST + WS
|
||||
│
|
||||
╔══════════════════════════════╪═══════════════════════════════════╗
|
||||
║ GAME SERVER TIER ║
|
||||
║ │ ║
|
||||
║ ┌───────────────────────────▼──────────────────────────────┐ ║
|
||||
║ │ Game Server (Rust / Bevy headless) │ ║
|
||||
║ │ │ ║
|
||||
║ │ bevy_ecs ── gleicher Code wie Client │ ║
|
||||
║ │ Rapier ──── gleiche Physik wie Client │ ║
|
||||
║ │ Wasmtime ── User-Scripts in WASM-Sandbox │ ║
|
||||
║ │ Quinn ───── WebTransport (QUIC, unreliable datagrams) │ ║
|
||||
║ │ │ ║
|
||||
║ │ Pro Instanz: 1 Prozess, 100-500 MB RAM, 1-2 CPU Cores │ ║
|
||||
║ └───────────────────────────────────────────────────────────┘ ║
|
||||
║ ║
|
||||
║ ┌────────────────────────────────────────────────────────────┐ ║
|
||||
║ │ mana-game-orchestrator (Go) │ ║
|
||||
║ │ - Spawnt/stoppt Rust-Game-Server als Subprozesse │ ║
|
||||
║ │ - Health Checks, Auto-Restart, Ressourcen-Limits │ ║
|
||||
║ │ - Latenz-basiertes Routing │ ║
|
||||
║ │ - Cloud-Burst zu Fly.io bei Spitzenlast │ ║
|
||||
║ └────────────────────────────────────────────────────────────┘ ║
|
||||
║ ║
|
||||
║ ┌────────────────────────────────────────────────────────────┐ ║
|
||||
║ │ mana-matchmaker (Go) │ ║
|
||||
║ │ - Freunde-zuerst, dann Region, dann Fuellstand │ ║
|
||||
║ │ - Redis Sorted Sets fuer Warteschlangen │ ║
|
||||
║ │ - Latenz-Messung via QUIC Ping │ ║
|
||||
║ └────────────────────────────────────────────────────────────┘ ║
|
||||
╚══════════════════════════════════════════════════════════════════╝
|
||||
│
|
||||
╔══════════════════════════════╪═══════════════════════════════════╗
|
||||
║ PLATFORM SERVICES ║
|
||||
║ ║
|
||||
║ BESTEHEND (unveraendert oder minimal erweitert): ║
|
||||
║ ┌──────────┐ ┌───────────┐ ┌──────────┐ ┌───────────────────┐ ║
|
||||
║ │mana-auth │ │mana-credit│ │mana-sub │ │mana-user │ ║
|
||||
║ │Hono+Bun │ │Hono+Bun │ │Hono+Bun │ │Hono+Bun │ ║
|
||||
║ │Port 3001 │ │Port 3061 │ │Port 3063 │ │Port 3062 │ ║
|
||||
║ └──────────┘ └───────────┘ └──────────┘ └───────────────────┘ ║
|
||||
║ ┌──────────┐ ┌───────────┐ ┌──────────┐ ┌───────────────────┐ ║
|
||||
║ │mana-sync │ │mana-notify│ │mana- │ │mana-search │ ║
|
||||
║ │Go │ │Go │ │analytics │ │Go │ ║
|
||||
║ │Port 3050 │ │Port 3040 │ │Hono │ │Port 3021 │ ║
|
||||
║ └──────────┘ └───────────┘ └──────────┘ └───────────────────┘ ║
|
||||
║ ║
|
||||
║ NEU: ║
|
||||
║ ┌──────────────────┐ ┌──────────────────┐ ║
|
||||
║ │mana-asset-server │ │mana-moderation │ ║
|
||||
║ │Go │ │Go + Python ML │ ║
|
||||
║ │Asset Pipeline, │ │Text + Voice + │ ║
|
||||
║ │CDN Origin, │ │Content + Behavior│ ║
|
||||
║ │Auto-LOD, Hashing │ │ │ ║
|
||||
║ └──────────────────┘ └──────────────────┘ ║
|
||||
╚══════════════════════════════════════════════════════════════════╝
|
||||
│
|
||||
╔══════════════════════════════╪═══════════════════════════════════╗
|
||||
║ AI SERVICES (bestehend) ║
|
||||
║ ║
|
||||
║ ┌──────────┐ ┌───────────┐ ┌──────────┐ ┌───────────────────┐ ║
|
||||
║ │mana-llm │ │mana-image │ │mana-stt │ │mana-tts │ ║
|
||||
║ │Python │ │-gen Python│ │Python │ │Python │ ║
|
||||
║ │NPC Dialog│ │Texturen │ │Voice Mod │ │NPC Stimme │ ║
|
||||
║ │Quest Gen │ │+ 3D (neu) │ │Voice Chat│ │ │ ║
|
||||
║ └──────────┘ └───────────┘ └──────────┘ └───────────────────┘ ║
|
||||
╚══════════════════════════════════════════════════════════════════╝
|
||||
│
|
||||
╔══════════════════════════════╪═══════════════════════════════════╗
|
||||
║ DATA LAYER ║
|
||||
║ ║
|
||||
║ ┌──────────────┐ ┌────────────┐ ┌───────────┐ ┌──────────────┐ ║
|
||||
║ │ PostgreSQL 16│ │ TigerBeetle│ │ Dragonfly │ │ MinIO │ ║
|
||||
║ │ │ │ │ │ (Redis- │ │ (S3) │ ║
|
||||
║ │ Users, Games │ │ Economy: │ │ kompatibel│ │ 3D Assets │ ║
|
||||
║ │ Social Graph │ │ Wallets │ │ Multi- │ │ Texturen │ ║
|
||||
║ │ Moderation │ │ Transfers │ │ threaded) │ │ Audio │ ║
|
||||
║ │ Game Meta │ │ Revenue │ │ Sessions │ │ WASM Scripts │ ║
|
||||
║ │ │ │ Split │ │ Presence │ │ │ ║
|
||||
║ │ BESTEHT │ │ NEU │ │ Matchmake │ │ BESTEHT │ ║
|
||||
║ │ │ │ │ │ Leaderb. │ │ │ ║
|
||||
║ └──────────────┘ └────────────┘ └───────────┘ └──────────────┘ ║
|
||||
║ ║
|
||||
║ ┌──────────────┐ ┌────────────┐ ┌────────────────────────────┐ ║
|
||||
║ │ Dexie.js │ │ Meilisearch│ │ ClickHouse │ ║
|
||||
║ │ (Client) │ │ │ │ │ ║
|
||||
║ │ Local-First │ │ Game-Suche │ │ Analytics, Telemetrie, │ ║
|
||||
║ │ Offline Data │ │ Asset-Suche│ │ Play-Metriken, Retention │ ║
|
||||
║ │ │ │ Creator │ │ │ ║
|
||||
║ │ BESTEHT │ │ NEU │ │ NEU │ ║
|
||||
║ └──────────────┘ └────────────┘ └────────────────────────────┘ ║
|
||||
╚══════════════════════════════════════════════════════════════════╝
|
||||
│
|
||||
╔══════════════════════════════╪═══════════════════════════════════╗
|
||||
║ INFRASTRUCTURE ║
|
||||
║ ║
|
||||
║ ┌────────────┐ ┌──────────┐ ┌────────────┐ ┌─────────────────┐ ║
|
||||
║ │ Forgejo │ │ Grafana │ │ Victoria │ │ GlitchTip │ ║
|
||||
║ │ Git + CI │ │ Dashb. │ │ Metrics │ │ Error Track. │ ║
|
||||
║ │ BESTEHT │ │ BESTEHT │ │ + Loki │ │ BESTEHT │ ║
|
||||
║ │ │ │ │ │ BESTEHT │ │ │ ║
|
||||
║ └────────────┘ └──────────┘ └────────────┘ └─────────────────┘ ║
|
||||
║ ║
|
||||
║ Hosting: Mac Mini M4 (Basis) + Fly.io (Burst) ║
|
||||
║ CDN: Cloudflare R2 (Assets) + Tunnel (Routing) ║
|
||||
║ Domain: mana.how ║
|
||||
╚══════════════════════════════════════════════════════════════════╝
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Warum genau diese Technologie pro Schicht
|
||||
|
||||
### Rendering: Bevy + wgpu (Rust → WASM → WebGPU)
|
||||
|
||||
**Warum nicht Three.js / Babylon.js:**
|
||||
|
||||
| Kriterium | Three.js | Bevy + wgpu |
|
||||
| -------------- | -------------------------------- | ------------------------------------- |
|
||||
| Sprache | JavaScript | Rust (→ WASM) |
|
||||
| Performance | Gut, aber JS-GC-Pausen | Nah an nativ, kein GC |
|
||||
| ECS | Nicht eingebaut (muss extern) | Kern-Architektur |
|
||||
| Physik | Extern (Rapier.js = WASM-Bridge) | Rapier nativ in Rust (zero overhead) |
|
||||
| Server-Sharing | Nicht moeglich (JS != Headless) | **Gleicher Code auf Client + Server** |
|
||||
| WebGPU | Seit r163, aber JS-Overhead | wgpu ist die Referenz-Impl von WebGPU |
|
||||
| Sandboxing | Schwierig in JS | WASM-Isolation nativ |
|
||||
| Multithreading | Web Workers (umstaendlich) | Bevy Parallel Systems (automatisch) |
|
||||
|
||||
**Der Killer-Vorteil:** Mit Bevy laeuft **identischer Rust-Code** auf Client (WASM) und Server (nativ). Die Physik-Simulation ist bit-identisch. Das eliminiert eine ganze Klasse von Client-Server-Desync-Bugs, die bei separaten Client/Server-Stacks auftreten.
|
||||
|
||||
```
|
||||
bevy_game_logic.rs
|
||||
│
|
||||
├──→ cargo build --target wasm32-unknown-unknown → Browser (WASM)
|
||||
│
|
||||
└──→ cargo build --release → Server (nativ, 10x schneller)
|
||||
```
|
||||
|
||||
### Physik: Rapier (Rust, nativ in Bevy)
|
||||
|
||||
- Rapier ist in Rust geschrieben und integriert nativ mit Bevy (`bevy_rapier3d`)
|
||||
- **Deterministisch** -- gleiche Inputs erzeugen gleiche Outputs auf Client und Server
|
||||
- 2D + 3D in einer Library
|
||||
- Kein WASM-Bridge-Overhead (im Gegensatz zu Rapier.js, das ueber die JS-WASM-Grenze kommuniziert)
|
||||
- Aktiv entwickelt von Dimforge (das gleiche Team hinter der Rust-Numerics-Szene)
|
||||
|
||||
### Scripting: WASM via Wasmtime (User-Code-Sandbox)
|
||||
|
||||
**Warum TypeScript → WASM statt Lua:**
|
||||
|
||||
| Kriterium | Lua/Luau (wie Roblox) | TypeScript → WASM |
|
||||
| ------------------- | ----------------------------------------- | --------------------------------- |
|
||||
| Sprach-Popularitaet | Nische | #4 weltweit |
|
||||
| Entwickler-Pool | Klein | Riesig |
|
||||
| Tooling | Custom IDE | VS Code, jede IDE |
|
||||
| Performance | Interpreter (~10-20x langsamer als nativ) | WASM (~1.2x langsamer als nativ) |
|
||||
| Type Safety | Optional (Luau) | Stark (TypeScript) |
|
||||
| Sandbox-Sicherheit | Software-basiert (VM-Escapes moeglich) | Hardware-Level (Linear Memory) |
|
||||
| Ecosystem | Roblox-spezifisch | npm, jede JS-Library (compiliert) |
|
||||
|
||||
**Der Workflow:**
|
||||
|
||||
```
|
||||
Creator schreibt TypeScript
|
||||
│
|
||||
▼
|
||||
AssemblyScript Compiler (oder Javy fuer volles TS)
|
||||
│
|
||||
▼
|
||||
WASM Binary (.wasm)
|
||||
│
|
||||
├──→ Browser: Native WASM-Engine (V8/SpiderMonkey)
|
||||
│
|
||||
└──→ Server: Wasmtime (Rust) mit:
|
||||
- Fuel Metering (max Instructions/Tick)
|
||||
- Memory Limit (z.B. 64 MB pro Script)
|
||||
- Capability-based API (deklarierte Permissions)
|
||||
- Kein Filesystem, kein Network, kein OS-Zugriff
|
||||
```
|
||||
|
||||
**Wasmtime** (nicht wazero) weil:
|
||||
|
||||
- JIT-Compilation → 3-5x schneller als wazero (Interpreter)
|
||||
- Cranelift Compiler Backend (schnelle Compilation + guter Code)
|
||||
- WASI Component Model Support (typisierte Interfaces)
|
||||
- Fuel Metering eingebaut
|
||||
- Von der Bytecode Alliance (Mozilla, Fastly, Intel) maintained
|
||||
|
||||
### Networking: Quinn (Rust QUIC) → WebTransport
|
||||
|
||||
**Warum WebTransport statt WebSocket:**
|
||||
|
||||
```
|
||||
WebSocket (TCP):
|
||||
Client → Server: Position Update (10 Bytes)
|
||||
[Wenn Paket 47 verloren geht, blockieren Pakete 48-100 bis Retransmit]
|
||||
→ Head-of-Line Blocking → Lag-Spikes
|
||||
|
||||
WebTransport (QUIC):
|
||||
Client → Server: Position Update (Unreliable Datagram, 10 Bytes)
|
||||
[Paket 47 verloren? Egal, Paket 48-100 kommen sofort durch]
|
||||
→ Kein Blocking → Fluessig
|
||||
|
||||
PLUS: Reliable Streams fuer Chat, Inventar, Economy (parallel, unabhaengig)
|
||||
```
|
||||
|
||||
Quinn ist die Rust-QUIC-Library und integriert direkt mit dem Rust-Server.
|
||||
|
||||
### Orchestrierung: Go
|
||||
|
||||
Go ist hier perfekt -- es geht nicht um Game-Ticks bei 60Hz, sondern um:
|
||||
|
||||
- Prozesse spawnen und ueberwachen
|
||||
- Health Checks (HTTP)
|
||||
- Latenz messen
|
||||
- Load Balancing
|
||||
- Fly.io API aufrufen fuer Cloud-Burst
|
||||
- Redis-Queries fuer Matchmaking
|
||||
|
||||
Das ist exakt was unsere Go-Services (mana-sync, mana-notify, mana-gateway) bereits machen.
|
||||
|
||||
### Economy: TigerBeetle
|
||||
|
||||
**Warum nicht einfach PostgreSQL:**
|
||||
|
||||
| Kriterium | PostgreSQL | TigerBeetle |
|
||||
| ----------------- | ---------------------------------- | --------------------------------- |
|
||||
| Transfers/Sekunde | ~10.000 (SERIALIZABLE) | 1.000.000+ |
|
||||
| Double-Entry | Manuell implementieren | Eingebaut |
|
||||
| Konsistenz | SERIALIZABLE moeglich aber langsam | Strikte Serialisierbarkeit, immer |
|
||||
| Race Conditions | Moeglich bei falscher Isolation | Unmoeglich (by Design) |
|
||||
| Negative Balances | Manuell pruefen | Unmoeglich (by Design) |
|
||||
| Audit Trail | Manuell | Jeder Transfer ist immutable |
|
||||
|
||||
Fuer eine Plattform mit virtueller Waehrung, In-Game-Purchases, Creator-Revenue-Splits und Marketplace-Trades ist TigerBeetle nicht Overkill -- es ist die richtige Abstraktionsebene.
|
||||
|
||||
**Account-Hierarchie:**
|
||||
|
||||
```
|
||||
Platform Treasury
|
||||
├── User:alice
|
||||
│ ├── Wallet:purchased (Mana gekauft)
|
||||
│ └── Wallet:earned (Mana verdient)
|
||||
├── User:bob
|
||||
│ ├── Wallet:purchased
|
||||
│ └── Wallet:earned
|
||||
├── Game:bobs-adventure
|
||||
│ └── Revenue (Einnahmen aus dem Game)
|
||||
├── Marketplace:escrow (Treuhand fuer Trades)
|
||||
└── CreatorPayout:pending (Auszahlungs-Queue)
|
||||
|
||||
Transaktion: Alice kauft Item in Bobs Game fuer 100 Mana
|
||||
Debit: alice/wallet:purchased -100
|
||||
Credit: bobs-adventure/revenue +70 (70% Creator)
|
||||
Credit: platform/treasury +30 (30% Plattform)
|
||||
|
||||
→ Automatisch, atomar, unmoeglich falsch zu buchen
|
||||
```
|
||||
|
||||
### Suche: Meilisearch
|
||||
|
||||
- Instant Search (<50ms) mit Typo-Toleranz
|
||||
- Faceted Search (nach Genre, Rating, Spielerzahl filtern)
|
||||
- Self-hostable, Rust-basiert, ~50 MB RAM
|
||||
- REST API, trivial zu integrieren
|
||||
- Ersetzt keine Datenbank -- indiziert Games, Assets, Creators fuer Discovery
|
||||
|
||||
### Analytics: ClickHouse
|
||||
|
||||
- Spaltenorientiert -- perfekt fuer "wie oft wurde Game X in den letzten 7 Tagen gespielt?"
|
||||
- Milliarden Events, Echtzeit-Aggregation
|
||||
- Self-hostable, ~200 MB RAM fuer kleine Instanzen
|
||||
- Materialized Views fuer Dashboards (Retention, DAU, Revenue pro Game)
|
||||
|
||||
### Cache: Dragonfly statt Redis
|
||||
|
||||
- Drop-in Redis-Ersatz (gleiches Protokoll)
|
||||
- **Multi-threaded** (Redis ist single-threaded)
|
||||
- 2-5x speichereffizienter
|
||||
- Hoehere Throughput bei gleicher Hardware
|
||||
- Self-hostable, ein Binary
|
||||
|
||||
---
|
||||
|
||||
## Sprach-Verteilung
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────┐
|
||||
│ │
|
||||
│ Rust (30%) │
|
||||
│ ├── Game Engine (Bevy + wgpu + Rapier) │
|
||||
│ ├── Game Server (Bevy headless) │
|
||||
│ ├── WASM Runtime (Wasmtime) │
|
||||
│ └── Networking (Quinn/QUIC) │
|
||||
│ │
|
||||
│ Go (25%) │
|
||||
│ ├── Game Orchestrator (NEU) │
|
||||
│ ├── Matchmaker (NEU) │
|
||||
│ ├── Asset Server (NEU) │
|
||||
│ ├── mana-sync (BESTEHT) │
|
||||
│ ├── mana-notify (BESTEHT) │
|
||||
│ ├── mana-search (BESTEHT) │
|
||||
│ ├── mana-crawler (BESTEHT) │
|
||||
│ ├── mana-api-gateway (BESTEHT) │
|
||||
│ └── mana-matrix-bot (BESTEHT) │
|
||||
│ │
|
||||
│ TypeScript (30%) │
|
||||
│ ├── Svelte 5 UI (Editor, Marketplace, Social) │
|
||||
│ ├── Hono Services (auth, credits, user, subs) │
|
||||
│ ├── Shared Packages (@manacore/*) │
|
||||
│ ├── User Game Scripts (→ WASM kompiliert) │
|
||||
│ └── Local-First Layer (Dexie.js) │
|
||||
│ │
|
||||
│ Python (15%) │
|
||||
│ ├── mana-llm (NPC-Dialoge, Quest-Generierung) │
|
||||
│ ├── mana-image-gen (Texturen, Assets) │
|
||||
│ ├── mana-stt (Voice Chat Transkription) │
|
||||
│ ├── mana-tts (NPC-Sprachausgabe) │
|
||||
│ └── ML-Moderation (Content + Behavior) │
|
||||
│ │
|
||||
└─────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
**Jede Sprache in ihrer Staerke:**
|
||||
|
||||
| Sprache | Domaene | Warum genau diese |
|
||||
| -------------- | -------------------------------------- | --------------------------------------------------------------------------------- |
|
||||
| **Rust** | Alles mit Echtzeit-Anforderung (60Hz+) | Zero-Cost Abstractions, kein GC, WASM-Output, gleicher Code Client+Server |
|
||||
| **Go** | Alles mit I/O + Orchestrierung | Goroutines fuer tausende Connections, schnelle Binaries, unser bestehendes Wissen |
|
||||
| **TypeScript** | Alles was User sehen/schreiben | Svelte-Frontend, Creator-Scripting, groesstes Oekosystem |
|
||||
| **Python** | Alles mit ML/AI | PyTorch/MLX, FastAPI, unser bestehender AI-Stack |
|
||||
|
||||
---
|
||||
|
||||
## Self-Hosting: Komplett-Uebersicht
|
||||
|
||||
### Hardware-Plan
|
||||
|
||||
**Phase 1 (jetzt): Mac Mini M4 16 GB**
|
||||
|
||||
- Alles laeuft hier: Backend, 10-50 Game-Instanzen, AI-Services
|
||||
- ~200-500 gleichzeitige Spieler
|
||||
- Kosten: Bereits vorhanden
|
||||
|
||||
**Phase 2 (bei Traktion): Mac Mini M4 Pro 48 GB (zusaetzlich)**
|
||||
|
||||
- Dediziert fuer Game-Server
|
||||
- 50-200 Instanzen
|
||||
- ~$1.200 einmalig
|
||||
|
||||
**Phase 3 (Wachstum): Mac Studio M4 Ultra 192 GB**
|
||||
|
||||
- Game-Server + AI auf einem Geraet
|
||||
- 200-500 Instanzen + LLM-Inference
|
||||
- ~$4.000 einmalig
|
||||
|
||||
**Phase X (Skalierung): Hybrid**
|
||||
|
||||
- Apple Silicon lokal fuer Basis-Last
|
||||
- Fly.io/Hetzner fuer Burst (Go-Orchestrator steuert automatisch)
|
||||
|
||||
### Jede Komponente: Self-Hosted-Status
|
||||
|
||||
| Komponente | Self-Hosted | Binary/Docker | RAM | Anmerkung |
|
||||
| ----------------------- | ----------- | -------------- | ---------------- | ----------------------- |
|
||||
| PostgreSQL 16 | BESTEHT | Docker | 512 MB | Backbone |
|
||||
| Redis/Dragonfly | UPGRADE | Single Binary | 100 MB | Dragonfly ersetzt Redis |
|
||||
| MinIO | BESTEHT | Docker | 256 MB | Asset Storage |
|
||||
| TigerBeetle | NEU | Single Binary | 100 MB | Economy |
|
||||
| Meilisearch | NEU | Single Binary | 50 MB | Game/Asset-Suche |
|
||||
| ClickHouse | NEU | Docker | 200 MB | Analytics |
|
||||
| Forgejo | BESTEHT | Docker | 200 MB | Git + CI |
|
||||
| Grafana Stack | BESTEHT | Docker | 300 MB | Monitoring |
|
||||
| GlitchTip | BESTEHT | Docker | 200 MB | Error Tracking |
|
||||
| SearXNG | BESTEHT | Docker | 100 MB | Meta-Suche |
|
||||
| Matrix Synapse | BESTEHT | Docker | 300 MB | Chat |
|
||||
| Ollama | BESTEHT | Binary | 4-8 GB | LLM (bei Bedarf) |
|
||||
| Bevy Game Server | NEU | Native Binary | 100-200 MB/Inst. | Game-Instanzen |
|
||||
| Wasmtime | NEU | In Game Server | (enthalten) | Script-Sandbox |
|
||||
| mana-\* Services | BESTEHT | Docker | je 30-100 MB | ~12 Services |
|
||||
| **Gesamt Basis** | | | **~8-10 GB** | Ohne Game-Instanzen |
|
||||
| **+ 50 Game-Instanzen** | | | **+5-10 GB** | |
|
||||
|
||||
**Passt auf den Mac Mini M4 mit 16 GB.** Eng, aber machbar fuer Alpha/Beta.
|
||||
|
||||
---
|
||||
|
||||
## Datenfluss: Eine komplette User-Journey
|
||||
|
||||
### Creator baut ein Game
|
||||
|
||||
```
|
||||
1. Creator oeffnet Browser → mana.how/create
|
||||
└─ SvelteKit laedt, Bevy Engine (WASM) initialisiert
|
||||
|
||||
2. Offline-faehiger 3D-Editor startet
|
||||
└─ Dexie.js hat vorherige Session → sofortiges Laden
|
||||
|
||||
3. Creator platziert Objekte, schreibt TypeScript-Script
|
||||
┌─ 3D: Bevy Renderer (wgpu → WebGPU)
|
||||
├─ Code: Monaco Editor mit AI-Autocomplete (Claude API)
|
||||
└─ Visual: Node-Editor → kompiliert zu WASM
|
||||
|
||||
4. "Test" Button → lokaler Game-Loop startet
|
||||
├─ Bevy + Rapier laufen komplett im Browser
|
||||
├─ WASM-Script wird in Wasmtime-Sandbox ausgefuehrt
|
||||
└─ Kein Server noetig fuer Singleplayer-Test!
|
||||
|
||||
5. "Publish" Button
|
||||
├─ Game-Daten: Dexie.js → mana-sync → PostgreSQL
|
||||
├─ Assets: Upload → mana-asset-server → MinIO (+ CDN)
|
||||
├─ WASM-Script: Validiert + gespeichert
|
||||
└─ Meilisearch: Game wird indizierbar
|
||||
```
|
||||
|
||||
### Spieler spielt ein Game
|
||||
|
||||
```
|
||||
1. Spieler klickt Link → mana.how/play/bobs-adventure
|
||||
└─ SvelteKit laedt, Bevy Engine (WASM) initialisiert
|
||||
|
||||
2. Matchmaker findet/erstellt Server-Instanz
|
||||
├─ mana-matchmaker (Go): Prueft existierende Instanzen
|
||||
├─ Keine frei? → mana-game-orchestrator spawnt Bevy-Server
|
||||
└─ Spieler erhaelt Server-Adresse
|
||||
|
||||
3. WebTransport-Verbindung zum Game Server
|
||||
├─ Unreliable Datagrams: Position, Rotation (20Hz)
|
||||
├─ Reliable Stream 1: Chat
|
||||
├─ Reliable Stream 2: Inventar, Economy
|
||||
└─ Reliable Stream 3: Game Events
|
||||
|
||||
4. Game laeuft
|
||||
├─ Server: Bevy headless + Rapier + Wasmtime (autoritativ)
|
||||
├─ Client: Bevy WASM + Rapier (Prediction) + Rendering
|
||||
├─ Physik: Identisch auf Client + Server (Rust → Rust)
|
||||
└─ Scripts: Identischer WASM-Code auf Client + Server
|
||||
|
||||
5. In-Game-Kauf: 50 Mana fuer Schwert
|
||||
├─ Client → Server: "Kaufe Item X" (Reliable Stream)
|
||||
├─ Server → mana-credits: Validierung
|
||||
├─ Server → TigerBeetle: Atomarer Transfer
|
||||
│ Debit: spieler/wallet -50
|
||||
│ Credit: game/revenue +35 (70%)
|
||||
│ Credit: platform/treasury +15 (30%)
|
||||
└─ Server → Client: "Kauf bestaetigt" + Item im Inventar
|
||||
|
||||
6. Spieler geht offline
|
||||
├─ Spielfortschritt: In Dexie.js gespeichert
|
||||
├─ Naechster Login: Sync via mana-sync
|
||||
└─ Game-Instanz: Wird nach Timeout vom Orchestrator gestoppt
|
||||
```
|
||||
|
||||
### AI-Interaktion mit NPC
|
||||
|
||||
```
|
||||
1. Spieler spricht NPC an (Text oder Voice)
|
||||
├─ Text: Direkt an Server
|
||||
└─ Voice: mana-stt (Whisper) → Text
|
||||
|
||||
2. Server → mana-llm (Ollama, Gemma 3 4B)
|
||||
├─ System Prompt: NPC-Persoenlichkeit + Game-Lore
|
||||
├─ Context: Bisherige Konversation (Redis)
|
||||
└─ Structured Output: {dialog, emotion, action}
|
||||
|
||||
3. Response
|
||||
├─ Dialog-Text → Client UI
|
||||
├─ Emotion → Avatar-Animation (Bevy)
|
||||
├─ Action → ECS-Command (z.B. Item geben, Quest starten)
|
||||
└─ Optional: mana-tts → Sprachausgabe
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Creator-Economy: Fairer als Roblox
|
||||
|
||||
### Revenue Split
|
||||
|
||||
```
|
||||
Spieler kauft 100 Mana fuer €1,00
|
||||
|
||||
Roblox: Creator erhaelt ~€0,25 (24.5%)
|
||||
Wir (Self-H): Creator erhaelt ~€0,70 (70%)
|
||||
|
||||
Warum 70% moeglich ist:
|
||||
├─ Kein App Store Fee (Browser → 0% statt 30%)
|
||||
├─ Self-Hosted (Server-Kosten: ~€0,001 pro Spielstunde)
|
||||
├─ TigerBeetle (keine Payment-Processing-Overhead)
|
||||
└─ Stripe-Fee: ~2.9% (direkt, kein Zwischenhändler)
|
||||
|
||||
Tatsaechliche Kosten pro €1,00 Kauf:
|
||||
├─ Stripe: €0,03 (2.9%)
|
||||
├─ Server: €0,01 (geschaetzt)
|
||||
├─ CDN/Infra: €0,01 (geschaetzt)
|
||||
├─ Moderation: €0,01 (geschaetzt)
|
||||
├─ Platform: €0,24 (Marge)
|
||||
└─ Creator: €0,70
|
||||
Summe: €1,00
|
||||
```
|
||||
|
||||
### Auszahlungs-Modell
|
||||
|
||||
| Tier | Voraussetzung | Auszahlung |
|
||||
| ----------- | --------------------- | -------------------------------------------------- |
|
||||
| **Starter** | 0 Mana verdient | Mana nur in-platform nutzbar |
|
||||
| **Creator** | 1.000 Mana verdient | Monatliche Auszahlung via Stripe |
|
||||
| **Pro** | 10.000 Mana verdient | Woechentliche Auszahlung, Analytics-Dashboard |
|
||||
| **Studio** | 100.000 Mana verdient | Taegliche Auszahlung, Priority Support, API-Zugang |
|
||||
|
||||
---
|
||||
|
||||
## Sicherheitsarchitektur
|
||||
|
||||
### Defense in Depth
|
||||
|
||||
```
|
||||
Layer 1: WASM Sandbox
|
||||
├─ Linear Memory Isolation (Hardware-Level)
|
||||
├─ Fuel Metering (max. Instructions/Tick)
|
||||
├─ Memory Limit (64 MB pro Script)
|
||||
├─ Capability-based API (deklarierte Permissions)
|
||||
└─ Kein FS/Network/OS-Zugang
|
||||
|
||||
Layer 2: Server-Autoritaet
|
||||
├─ Alle Economy-Operationen nur serverseitig
|
||||
├─ Physik-Validierung auf dem Server
|
||||
├─ Input-Validation (Rate Limits, Bounds Checks)
|
||||
└─ Client-State wird nie vertraut
|
||||
|
||||
Layer 3: Network Security
|
||||
├─ WebTransport (TLS 1.3 mandatory)
|
||||
├─ EdDSA JWT (bestehendes Auth-System)
|
||||
├─ Per-Connection Rate Limiting
|
||||
└─ DDoS: Cloudflare Tunnel (besteht)
|
||||
|
||||
Layer 4: Content Moderation
|
||||
├─ Text: BERT-Classifier (wie Roblox, aber kleiner)
|
||||
├─ Voice: Whisper → Text-Classifier
|
||||
├─ 3D Content: Render-to-Image → Vision Model
|
||||
├─ Behavior: Pattern Detection (ML)
|
||||
└─ Human Review Queue fuer Grenzfaelle
|
||||
|
||||
Layer 5: Economy
|
||||
├─ TigerBeetle: Double-Entry, keine Race Conditions
|
||||
├─ Negative Balances physisch unmoeglich
|
||||
├─ Velocity Checks (>N Transaktionen/Minute → Flag)
|
||||
└─ Fraud Detection Pipeline
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Vergleich: Unser optimaler Stack vs. Roblox
|
||||
|
||||
| Dimension | Roblox (2025) | Unser Stack |
|
||||
| -------------- | ----------------------------- | ----------------------------------- |
|
||||
| Engine | Custom C++ (20 Jahre alt) | Bevy/Rust (modern, ECS-native) |
|
||||
| Rendering | D3D11/Metal/Vulkan (nativ) | wgpu → WebGPU (Browser + nativ) |
|
||||
| Client | Nativer Download erforderlich | **Zero Install (Browser)** |
|
||||
| Scripting | Luau (Lua-Fork, Nische) | TypeScript → WASM (Mainstream) |
|
||||
| Sandbox | Software-VM (Escape-Risiko) | WASM Linear Memory (Hardware-Level) |
|
||||
| Physik | Custom PGS 240Hz | Rapier (Rust, deterministisch) |
|
||||
| Networking | Custom UDP | WebTransport (QUIC, Standard) |
|
||||
| Server | Custom C++ headless | Bevy headless (**gleicher Code**) |
|
||||
| Orchestrierung | Nomad (135.000 Server) | Go + Fly.io (Self-Hosted + Burst) |
|
||||
| Economy | Proprietaer (24.5% Creator) | TigerBeetle (**70% Creator**) |
|
||||
| Moderation | Massive AI + Humans | Kleiner Scale, Self-Hosted ML |
|
||||
| Offline | Nicht moeglich | **Local-First (Dexie.js)** |
|
||||
| Editor Offline | Nicht moeglich | **Komplett offline-faehig** |
|
||||
| Guest Mode | Nicht moeglich | **Ja (bestehendes Pattern)** |
|
||||
| Self-Hosted | Unmoeglich | **Komplett auf Mac Mini** |
|
||||
| Open Source | Nein (nur Luau) | Moeglich (Rust + Go + TS) |
|
||||
| AI NPCs | Experimentell | **Bestehende Pipeline** |
|
||||
| Voice Chat | Proprietaer | **Self-Hosted (Whisper + Kokoro)** |
|
||||
| DSGVO | Fragwuerdig | **100% Self-Hosted-konform** |
|
||||
|
||||
---
|
||||
|
||||
## Risiken und Mitigationen
|
||||
|
||||
| Risiko | Impact | Mitigation |
|
||||
| ------------------------------- | --------- | ------------------------------------------------------------------------------------------ |
|
||||
| Rust-Lernkurve | Hoch | 3 Monate dediziertes Lernen, dann Pair Programming. Bevy hat exzellente Docs und Community |
|
||||
| Bevy pre-1.0 API-Churn | Mittel | Auf stable Features fokussieren, Engine-Abstraktionsschicht bauen |
|
||||
| WebGPU nicht auf allen Browsern | Mittel | WebGL2-Fallback via wgpu (automatisch). Safari 18.2+ hat WebGPU |
|
||||
| Performance auf Low-End Mobile | Hoch | Quality Tiers wie Roblox. LOD, Culling, Resolution Scaling. PWA statt App |
|
||||
| Moderation bei Scale | Sehr hoch | Von Anfang an ML-Pipeline bauen. Content Rating. Creator-Verantwortung |
|
||||
| Mac Mini reicht nicht | Mittel | Fly.io Cloud-Burst ist ein Go-Funktionsaufruf entfernt |
|
||||
| Kein Team | Kritisch | Solo: Phase 0-1 machbar. Ab Phase 2 braucht es mindestens 2-3 Leute |
|
||||
|
||||
---
|
||||
|
||||
## Timeline (realistisch, 1 Person)
|
||||
|
||||
| Phase | Dauer | Ergebnis |
|
||||
| ----------------------------- | ------------- | ------------------------------------------------------------------ |
|
||||
| **Rust lernen** | 2-3 Monate | Ownership, Lifetimes, Bevy-Basics sicher |
|
||||
| **Phase 0: Proof of Concept** | 2-3 Monate | Bevy-Szene im Browser, WASM-Script bewegt Objekt, Go-Server sync't |
|
||||
| **Phase 1: Editor Alpha** | 4-6 Monate | 3D-Editor, Visual Scripting, Singleplayer, Offline-faehig |
|
||||
| **Phase 2: Multiplayer** | 3-4 Monate | WebTransport, 2-20 Spieler, Server-Autoritative Physik |
|
||||
| **Phase 3: Platform** | 3-4 Monate | Auth, Economy (TigerBeetle), Marketplace, Game Discovery |
|
||||
| **Phase 4: Social + AI** | 3-4 Monate | Friends, Voice Chat, LLM-NPCs, Moderation |
|
||||
| **Phase 5: Polish** | Laufend | Performance, Mobile, Scale, Community |
|
||||
| **Gesamt bis spielbar** | ~18-24 Monate | |
|
||||
|
||||
---
|
||||
|
||||
## Zusammenfassung: Eine Seite
|
||||
|
||||
**Engine:** Bevy (Rust) + wgpu → WebGPU im Browser, nativ auf Server. Gleicher Code beidseitig.
|
||||
|
||||
**Scripting:** TypeScript → WASM. Wasmtime-Sandbox mit Fuel Metering. Sicherer als Roblox (Hardware-Level Isolation statt Software-VM).
|
||||
|
||||
**Networking:** WebTransport (QUIC) via Quinn. Unreliable Datagrams fuer Positionen, Reliable Streams fuer Economy/Chat.
|
||||
|
||||
**Backend:** Go fuer Orchestrierung, Matchmaking, bestehende Services. Hono/Bun fuer Auth, Credits, User.
|
||||
|
||||
**AI:** Bestehende Python-Services (LLM, Image Gen, STT, TTS). NPCs sprechen, Texturen werden generiert, Voice wird moderiert.
|
||||
|
||||
**Economy:** TigerBeetle. Double-Entry-Bookkeeping. 70% Creator Revenue Share (vs. Roblox 24.5%).
|
||||
|
||||
**Daten:** PostgreSQL + TigerBeetle + Dragonfly + MinIO + Dexie.js. Alles self-hosted.
|
||||
|
||||
**Local-First:** Offline-Editor, Guest Mode, Asset-Caching, Spielfortschritt -- alles in IndexedDB, sync bei Verbindung.
|
||||
|
||||
**Self-Hosted:** Komplett auf Mac Mini. Cloudflare R2 nur fuer CDN. Fly.io nur fuer Peak-Burst.
|
||||
|
||||
**Differenzierung gegenueber Roblox:**
|
||||
|
||||
1. Zero Install (Browser)
|
||||
2. 70% Creator Revenue (statt 24.5%)
|
||||
3. TypeScript statt Lua (100x groesserer Entwickler-Pool)
|
||||
4. Offline-faehig (Editor + Singleplayer)
|
||||
5. Self-Hosted / DSGVO-konform
|
||||
6. AI-native (NPCs, Asset-Generierung, Voice)
|
||||
7. Open-Source-moeglich
|
||||
|
||||
---
|
||||
|
||||
_Erstellt: 28. Maerz 2026_
|
||||
930
NewAppIdeas/Roblox Reimagined/PROJEKT-PLAN-2D.md
Normal file
930
NewAppIdeas/Roblox Reimagined/PROJEKT-PLAN-2D.md
Normal file
|
|
@ -0,0 +1,930 @@
|
|||
# ManaVoxel 2D: Der Projektplan
|
||||
|
||||
## Top-Down Pixel-Plattform mit Zoom-Stufen -- von der Weltkarte bis zum Item-Detail
|
||||
|
||||
---
|
||||
|
||||
## 1. Die Vision: Fraktaler Zoom
|
||||
|
||||
Das Spiel funktioniert wie Google Maps fuer Spielwelten: Je naeher man zoomt, desto mehr Detail erscheint. Jede Zoom-Stufe hat ihre eigene Pixel-Resolution und ihren eigenen Gameplay-Fokus.
|
||||
|
||||
```
|
||||
ZOOM OUT ──────────────────────────────────────────── ZOOM IN
|
||||
|
||||
Weltkarte Region Stadt/Dorf Strasse Innenraum Detail
|
||||
(spaeter) (spaeter) (spaeter) (MVP) (MVP) (MVP)
|
||||
|
||||
🗺️ 🏔️ 🏘️ 🚶 🏠 🔍
|
||||
|
||||
1 Pixel 1 Pixel 1 Pixel 1 Pixel 1 Pixel 1 Pixel
|
||||
= 100m = 10m = 1m = 10cm = 5cm = 1cm
|
||||
```
|
||||
|
||||
### Was der Spieler erlebt
|
||||
|
||||
```
|
||||
Spaeter (Post-MVP):
|
||||
Weltkarte aufmachen → "Ich sehe Kontinente, Ozeane, Staedte als Punkte"
|
||||
In eine Stadt klicken → "Ich sehe Strassen, Gebaeude von oben, Plaetze"
|
||||
|
||||
MVP:
|
||||
In eine Strasse gehen → "Ich laufe durch die Strasse, sehe Haeuser,
|
||||
Laternen, andere Spieler, NPCs"
|
||||
In ein Haus gehen → "Ich bin drin. Moebel, Treppen, Teppiche.
|
||||
Ich gehe nach oben -- zweites Stockwerk."
|
||||
Ein Item anschauen → "Ich oeffne den Detail-View. Jeder Pixel
|
||||
des Schwerts ist sichtbar. Gravuren, Farben."
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 2. Die sechs Zoom-Stufen
|
||||
|
||||
### Uebersicht
|
||||
|
||||
| Stufe | Name | Pixel = | Sichtfeld | Gameplay | Phase |
|
||||
|-------|------|---------|-----------|----------|-------|
|
||||
| 1 | Weltkarte | 100m | Ganze Welt | Navigation, Strategie | Spaeter |
|
||||
| 2 | Region | 10m | ~5km × 5km | Reisen, Ueberblick | Spaeter |
|
||||
| 3 | Stadt/Dorf | 1m | ~500m × 500m | Erkunden, Orientierung | Spaeter |
|
||||
| 4 | **Strasse** | **10cm** | **~50m × 30m** | **Laufen, Interaktion, Kampf** | **MVP** |
|
||||
| 5 | **Innenraum** | **5cm** | **~20m × 15m** | **Erkunden, Einrichten, Raetsel** | **MVP** |
|
||||
| 6 | **Detail** | **1cm** | **~2m × 1.5m** | **Items/Chars betrachten, editieren** | **MVP** |
|
||||
|
||||
### Stufe 4: Strasse (10cm) -- Der Hauptspiel-View
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────┐
|
||||
│ │
|
||||
│ ██████████ ████████████ ███████████████ │
|
||||
│ █ Baecker █ █ Schmiede █ █ Taverne █ │
|
||||
│ █ [Tuer] █ █ [Tuer] █ █ [Tuer] █ │
|
||||
│ ██████████ ████████████ ███████████████ │
|
||||
│ │
|
||||
│ 🌳 🪑 ⚒️ 🌳 💡 │
|
||||
│ │
|
||||
│ ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ │
|
||||
│ ░░░░░░░░░░░░░░░░░ STRASSE ░░░░░░░░░░░░░░░░░░░░░░░░░░░ │
|
||||
│ ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ │
|
||||
│ │
|
||||
│ 🧑 (Spieler) 🧙 (NPC) 🐕 (Tier) │
|
||||
│ │
|
||||
│ ██████████ ████████████ │
|
||||
│ █ Haus A █ █ Haus B █ 🌳 🌳 │
|
||||
│ █ [Tuer] █ █ [Tuer] █ │
|
||||
│ ██████████ ████████████ │
|
||||
│ │
|
||||
└─────────────────────────────────────────────────────────────┘
|
||||
|
||||
Resolution: 1 Pixel = 10cm
|
||||
Sichtfeld: ~50m × 30m (500 × 300 Pixel auf Screen bei 1:1)
|
||||
Gerendert: Skaliert auf Display-Resolution (z.B. 1920×1080)
|
||||
|
||||
Der Spieler sieht: Gebaeude von oben, Strassen, Baeume, andere Spieler
|
||||
Der Spieler kann: Laufen, mit Tueren interagieren (→ Innenraum),
|
||||
NPCs ansprechen, Items aufheben, kaempfen
|
||||
```
|
||||
|
||||
**Welt-Daten bei 10cm:**
|
||||
- Eine Strasse (50m × 30m): 500 × 300 = 150.000 Pixel = 300 KB roh, ~15 KB komprimiert
|
||||
- Ein Stadtviertel (200m × 200m): 2.000 × 2.000 = 4M Pixel = 8 MB roh, ~400 KB komprimiert
|
||||
- Layer: Boden, Objekte, Dach/Decke (3 Layer) = ×3
|
||||
|
||||
### Stufe 5: Innenraum (5cm) -- Haeuser und Dungeons
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────┐
|
||||
│ │
|
||||
│ ████████████████████████████████████████████████████████ │
|
||||
│ █ █ │
|
||||
│ █ ┌────────┐ ┌──────────────┐ ┌───────────┐ █ │
|
||||
│ █ │ Regal │ │ │ │ Kamin │ █ │
|
||||
│ █ │ ▪▪▪▪▪ │ │ Tisch │ │ 🔥 │ █ │
|
||||
│ █ │ ▪▪▪▪▪ │ │ ▫ ▫ ▫ ▫ │ │ │ █ │
|
||||
│ █ │ ▪▪▪▪▪ │ │ │ └───────────┘ █ │
|
||||
│ █ └────────┘ └──────────────┘ █ │
|
||||
│ █ █ │
|
||||
│ █ 🪑 🪑 🧑 ┌──────────────┐ █ │
|
||||
│ █ (Spieler) │ Truhe 🔒 │ █ │
|
||||
│ █ │ ▪▪▪▪▪▪▪▪▪ │ █ │
|
||||
│ █ ┌──────────┐ └──────────────┘ █ │
|
||||
│ █ │ Bett │ 🕯️ █ │
|
||||
│ █ │ ░░░░░░ │ █ │
|
||||
│ █ │ ░░░░░░ │ [Treppe ↑ OG] █ │
|
||||
│ █ └──────────┘ █ │
|
||||
│ █ █ │
|
||||
│ ████████████░░░░████████████████████████████████████████ █
|
||||
│ (Tuer → Strasse) │
|
||||
└─────────────────────────────────────────────────────────────┘
|
||||
|
||||
Resolution: 1 Pixel = 5cm (doppelt so detailliert wie Strasse!)
|
||||
Sichtfeld: ~20m × 15m (400 × 300 Pixel bei 1:1)
|
||||
|
||||
Der Spieler sieht: Moebel im Detail, Buchruecken im Regal,
|
||||
Muster auf Teppichen, Kerzenflammen
|
||||
Der Spieler kann: Moebel platzieren, Truhen oeffnen, Treppen steigen,
|
||||
Items untersuchen (→ Detail-View)
|
||||
```
|
||||
|
||||
**Stockwerke:**
|
||||
```
|
||||
Stockwerk-System (Layer-basiert):
|
||||
|
||||
OG: [Schlafzimmer] [Bad] [Balkon] ← Layer 2
|
||||
─────────────────────────────
|
||||
EG: [Wohnzimmer] [Kueche] [Flur] ← Layer 1 (Standard)
|
||||
─────────────────────────────
|
||||
UG: [Keller] [Geheimraum] ← Layer 0
|
||||
|
||||
Spieler geht auf Treppe → Fade-Transition → anderer Layer sichtbar
|
||||
Aktuelles Stockwerk: Volle Opacity
|
||||
Anderes Stockwerk: Ausgeblendet oder 20% Opacity als Schatten
|
||||
```
|
||||
|
||||
**Welt-Daten bei 5cm:**
|
||||
- Ein Raum (8m × 6m): 160 × 120 = 19.200 Pixel = 38 KB roh
|
||||
- Ein ganzes Haus (20m × 15m, 3 Stockwerke): 400 × 300 × 3 = 360.000 Pixel = 720 KB roh, ~35 KB komprimiert
|
||||
- Trivial!
|
||||
|
||||
### Stufe 6: Detail-View (1cm) -- Items und Charaktere
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────┐
|
||||
│ │
|
||||
│ DETAIL VIEW: "Flammenklinge" [X Zurueck]│
|
||||
│ │
|
||||
│ ┌────────────────────────────────┐ ┌───────────────────┐ │
|
||||
│ │ │ │ Eigenschaften: │ │
|
||||
│ │ ░░▓░░ │ │ │ │
|
||||
│ │ ░▓█▓░ │ │ Schaden: 80 │ │
|
||||
│ │ ░▓███▓░ │ │ Element: Feuer │ │
|
||||
│ │ ░▓█████▓░ │ │ Haltb.: 95/100 │ │
|
||||
│ │ ░▓███▓░ │ │ Seltenheit: Leg. │ │
|
||||
│ │ ░▓█▓░ │ │ │ │
|
||||
│ │ ░▓░ │ │ Verhalten: │ │
|
||||
│ │ ░▓░ │ │ ┌────────────────┐│ │
|
||||
│ │ ░▓░ │ │ │WENN benutzt ││ │
|
||||
│ │ ░▓░ │ │ │DANN Feuerball ││ │
|
||||
│ │ ░▓▓▓░ │ │ │UND 10 Schaden ││ │
|
||||
│ │ ░▓▓▓▓▓░ │ │ └────────────────┘│ │
|
||||
│ │ ░▓█▓░ │ │ │ │
|
||||
│ │ ░▓░ │ │ [Bearbeiten] │ │
|
||||
│ │ ░▓░ │ │ [Duplizieren] │ │
|
||||
│ │ │ │ [Teilen] │ │
|
||||
│ └────────────────────────────────┘ └───────────────────┘ │
|
||||
│ │
|
||||
│ Canvas: 64 × 128 Pixel bei 1cm │
|
||||
│ Erstellt von: alice | Trades: 47 | Likes: 312 │
|
||||
│ │
|
||||
└─────────────────────────────────────────────────────────────┘
|
||||
|
||||
Resolution: 1 Pixel = 1cm
|
||||
Canvas: 64 × 128 (oder frei waehlbar bis 128 × 128)
|
||||
Items: Schwert, Trank, Ring, Buch, Schluessel...
|
||||
Charaktere: Avatar von allen Seiten, mit Zubehoer
|
||||
Moebel: Stuhl, Tisch, Lampe im Detail
|
||||
|
||||
Hier wird auch EDITIERT:
|
||||
→ Sprite-Editor oeffnet sich im gleichen View
|
||||
→ Pixel fuer Pixel zeichnen
|
||||
→ Properties und Verhalten zuweisen
|
||||
```
|
||||
|
||||
**Welt-Daten bei 1cm:**
|
||||
- Ein Item (64 × 128): 8.192 Pixel = 16 KB roh, ~2 KB komprimiert
|
||||
- 100 Items im Inventar: ~200 KB komprimiert
|
||||
- Trivial!
|
||||
|
||||
---
|
||||
|
||||
## 3. Wie die Stufen verbunden sind
|
||||
|
||||
### Uebergaenge (MVP)
|
||||
|
||||
```
|
||||
STRASSE (10cm)
|
||||
│
|
||||
│ Spieler geht auf Tuer-Pixel
|
||||
│ → Fade/Slide-Transition (~0.3s)
|
||||
│
|
||||
▼
|
||||
INNENRAUM (5cm)
|
||||
│
|
||||
│ Spieler geht auf Treppe
|
||||
│ → Fade (~0.2s) → anderes Stockwerk
|
||||
│
|
||||
│ Spieler interagiert mit Item/Moebel
|
||||
│ → Detail-Panel oeffnet sich (Slide-In)
|
||||
│
|
||||
▼
|
||||
DETAIL VIEW (1cm)
|
||||
│
|
||||
│ [X] oder Escape → zurueck zum Innenraum
|
||||
│
|
||||
▲
|
||||
│
|
||||
INNENRAUM
|
||||
│
|
||||
│ Spieler geht auf Tuer → zurueck zur Strasse
|
||||
│
|
||||
▲
|
||||
STRASSE
|
||||
```
|
||||
|
||||
### Uebergaenge (Spaeter)
|
||||
|
||||
```
|
||||
WELTKARTE (100m/px) → Klick auf Stadt → STADT (1m/px)
|
||||
STADT (1m/px) → Klick auf Strasse/Laufen → STRASSE (10cm/px)
|
||||
STRASSE (10cm/px) → Tuer → INNENRAUM (5cm/px)
|
||||
INNENRAUM (5cm/px) → Item anklicken → DETAIL (1cm/px)
|
||||
|
||||
Jede Stufe ist ein eigener "Raum" mit eigenen Daten.
|
||||
Uebergaenge sind wie Tuer-Portale in Zelda: Fade → Neuer Raum laedt.
|
||||
```
|
||||
|
||||
### Daten-Hierarchie
|
||||
|
||||
```
|
||||
World
|
||||
├── (spaeter) regions[]
|
||||
│ ├── (spaeter) towns[]
|
||||
│ │ ├── streets[] ← MVP: Hier startet der Spieler
|
||||
│ │ │ ├── tiles (10cm) ← Pixel-Grid der Strasse
|
||||
│ │ │ ├── entities[] ← NPCs, Items, Spieler
|
||||
│ │ │ └── portals[] ← Tueren zu Innenraeumen
|
||||
│ │ │ └── interior
|
||||
│ │ │ ├── floors[] ← Stockwerke
|
||||
│ │ │ │ └── tiles (5cm)
|
||||
│ │ │ ├── furniture[] ← Moebel (platzierte Items)
|
||||
│ │ │ └── entities[]
|
||||
│ │ └── portals[] ← Strasse-zu-Strasse-Verbindungen
|
||||
│ └── connections[] ← Region-zu-Region
|
||||
└── items[] ← Globaler Item-Katalog (1cm Sprites)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. Resolution & Memory: Die Zahlen
|
||||
|
||||
### Pro Zoom-Stufe
|
||||
|
||||
| Stufe | Pixel/m | Typische Area | Pixel total | RAM (3 Layer) | Komprimiert |
|
||||
|-------|---------|--------------|-------------|---------------|-------------|
|
||||
| Strasse | 10 | 50m × 30m | 150K | 900 KB | ~50 KB |
|
||||
| Innenraum | 20 | 20m × 15m × 3 Etagen | 540K | 3.2 MB | ~160 KB |
|
||||
| Detail-Item | 100 | 0.64m × 1.28m | 8K | 16 KB | ~2 KB |
|
||||
|
||||
### Eine komplette Spielwelt (MVP)
|
||||
|
||||
```
|
||||
Ein Dorf mit 5 Strassen und 20 Haeusern:
|
||||
|
||||
Strassen: 5 × 50 KB = 250 KB
|
||||
Innenraeume: 20 × 160 KB = 3.2 MB
|
||||
Items: 200 × 2 KB = 400 KB
|
||||
Entities: 100 × 1 KB = 100 KB
|
||||
────────────────────────────────────
|
||||
TOTAL: ~4 MB komprimiert
|
||||
|
||||
→ Laedt in <1 Sekunde
|
||||
→ Passt 100× in den Browser-Speicher
|
||||
→ Netzwerk-Transfer: trivial
|
||||
```
|
||||
|
||||
### Spaetere Stufen (Post-MVP)
|
||||
|
||||
```
|
||||
Stadt-View hinzufuegen:
|
||||
Eine Stadt (500m × 500m bei 1m/px): 500 × 500 = 250K Pixel = 500 KB roh
|
||||
Komprimiert: ~25 KB
|
||||
→ Trivial
|
||||
|
||||
Region-View:
|
||||
Eine Region (5km × 5km bei 10m/px): 500 × 500 = 250K Pixel = 500 KB
|
||||
→ Trivial
|
||||
|
||||
Weltkarte:
|
||||
Eine Welt (50km × 50km bei 100m/px): 500 × 500 = 250K Pixel = 500 KB
|
||||
→ Trivial
|
||||
|
||||
ALLE Zoom-Stufen zusammen: <10 MB. Kein Problem.
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 5. Technischer Stack
|
||||
|
||||
### Architektur
|
||||
|
||||
```
|
||||
╔═══════════════════════════════════════════════════════════╗
|
||||
║ BROWSER (PixiJS + Svelte 5 + Dexie.js) ║
|
||||
║ ║
|
||||
║ ┌──────────────────────────┐ ┌───────────────────────┐ ║
|
||||
║ │ PixiJS 8 (WebGL/WebGPU) │ │ Svelte 5 UI │ ║
|
||||
║ │ │ │ │ ║
|
||||
║ │ TilemapRenderer │ │ HUD / Inventar │ ║
|
||||
║ │ → Strasse (10cm Layer) │ │ Dialog-System │ ║
|
||||
║ │ → Innenraum (5cm Layer) │ │ Pixel-Editor │ ║
|
||||
║ │ SpriteRenderer │ │ Sprite-Editor │ ║
|
||||
║ │ → Entities, Items │ │ Property Sliders │ ║
|
||||
║ │ LightingSystem │ │ Trigger-Action Builder │ ║
|
||||
║ │ → 2D Raycasts │ │ Code Editor (Monaco) │ ║
|
||||
║ │ ParticleSystem │ │ Chat, Social │ ║
|
||||
║ │ TransitionManager │ │ Marketplace │ ║
|
||||
║ │ → Zoom-Stufen-Wechsel │ │ Minimap │ ║
|
||||
║ └─────────────┬────────────┘ └──────────┬────────────┘ ║
|
||||
║ │ │ ║
|
||||
║ ┌─────────────▼───────────────────────────▼────────────┐║
|
||||
║ │ Dexie.js (IndexedDB) -- Local-First ║║
|
||||
║ │ Streets, Interiors, Items, Inventory (alles offline!) ║║
|
||||
║ └───────────────────────┬──────────────────────────────┘║
|
||||
╚══════════════════════════╪════════════════════════════════╝
|
||||
│ WebSocket + REST
|
||||
╔══════════════════════════╪════════════════════════════════╗
|
||||
║ GAME SERVER (Go) ║
|
||||
║ ║
|
||||
║ ┌───────────────────────────────────────────────────┐ ║
|
||||
║ │ mana-game-server (Go) │ ║
|
||||
║ │ │ ║
|
||||
║ │ 1 Goroutine pro aktive Strasse/Innenraum │ ║
|
||||
║ │ Welt-State: map[AreaID]*Area (Pixel-Grid + Entities)│ ║
|
||||
║ │ Physik: 2D AABB Collision (simple, custom) │ ║
|
||||
║ │ Scripting: wazero (WASM) fuer Item-Scripts │ ║
|
||||
║ │ Networking: coder/websocket │ ║
|
||||
║ │ Tick Rate: 20 Hz (50ms, wie Minecraft) │ ║
|
||||
║ └───────────────────────────────────────────────────┘ ║
|
||||
║ ║
|
||||
║ ┌───────────────────────────────────────────────────┐ ║
|
||||
║ │ mana-area-manager (Go) │ ║
|
||||
║ │ Laedt/entlaedt Areas on-demand │ ║
|
||||
║ │ Routet Spieler zwischen Areas │ ║
|
||||
║ │ Managed Uebergaenge (Strasse ↔ Innenraum) │ ║
|
||||
║ └───────────────────────────────────────────────────┘ ║
|
||||
╠═══════════════════════════════════════════════════════════╣
|
||||
║ BESTEHENDE SERVICES (unveraendert) ║
|
||||
║ mana-auth │ mana-credits │ mana-sync │ mana-notify ║
|
||||
║ mana-user │ mana-sub │ mana-analytics │ mana-search ║
|
||||
║ mana-llm │ mana-image-gen │ mana-stt │ mana-tts ║
|
||||
╠═══════════════════════════════════════════════════════════╣
|
||||
║ DATA ║
|
||||
║ PostgreSQL │ TigerBeetle │ Dragonfly │ MinIO │ Dexie.js ║
|
||||
║ + Meilisearch │ + ClickHouse (spaeter) ║
|
||||
╠═══════════════════════════════════════════════════════════╣
|
||||
║ INFRA (Self-Hosted, Mac Mini) ║
|
||||
║ Forgejo │ Grafana │ Loki │ GlitchTip │ Cloudflare Tunnel║
|
||||
╚═══════════════════════════════════════════════════════════╝
|
||||
```
|
||||
|
||||
### Warum dieser Stack?
|
||||
|
||||
| Entscheidung | Begruendung |
|
||||
|-------------|-------------|
|
||||
| **PixiJS 8** statt Phaser/Three.js | Schnellster 2D-Renderer, WebGPU-ready, kein Framework-Overhead |
|
||||
| **Svelte 5** fuer UI | Bestehende Expertise, 19 Apps, Runes |
|
||||
| **Go** Game Server | Goroutines = 1 pro Area, bestehende Expertise, kein Rust noetig |
|
||||
| **wazero** fuer Scripts | WASM in Go, kein CGo, sandboxed |
|
||||
| **WebSocket** statt WebTransport | Reicht fuer 2D (keine unreliable datagrams noetig bei 20Hz) |
|
||||
| **Dexie.js** Local-First | Bestehend, 19 Apps, Offline-Editor |
|
||||
| **TigerBeetle** Economy | Double-Entry, 70% Creator Share |
|
||||
| **Dragonfly** statt Redis | Drop-in Upgrade, multi-threaded |
|
||||
|
||||
### Rendering-Architektur (PixiJS)
|
||||
|
||||
```
|
||||
PixiJS Application
|
||||
├── Stage
|
||||
│ ├── WorldContainer (aktuelle Zoom-Stufe)
|
||||
│ │ ├── BackgroundLayer (Tilemap: Boden, Wasser, Gras)
|
||||
│ │ ├── ObjectLayer (Tilemap: Waende, Moebel, Baeume)
|
||||
│ │ ├── EntityLayer (Sprites: Spieler, NPCs, Items)
|
||||
│ │ ├── EffectLayer (Partikel, Projectiles)
|
||||
│ │ └── LightLayer (2D Lighting Overlay)
|
||||
│ │
|
||||
│ ├── UIContainer (Svelte-managed, HTML Overlay)
|
||||
│ │ ├── HUD (HP, Mana, Minimap)
|
||||
│ │ ├── Inventar
|
||||
│ │ ├── Dialog-Fenster
|
||||
│ │ └── Editor-Panels
|
||||
│ │
|
||||
│ └── TransitionOverlay (Fade/Slide bei Zoom-Wechsel)
|
||||
│
|
||||
└── Ticker (60fps Game Loop)
|
||||
├── InputSystem (Keyboard, Mouse, Touch)
|
||||
├── PhysicsSystem (2D AABB, simple)
|
||||
├── AnimationSystem (Sprite Frames)
|
||||
├── ScriptSystem (WASM Trigger-Evaluation)
|
||||
├── NetworkSystem (WebSocket Sync)
|
||||
└── RenderSystem (PixiJS Draw)
|
||||
```
|
||||
|
||||
### Layer-System fuer Stockwerke
|
||||
|
||||
```
|
||||
Innenraum mit 3 Stockwerken:
|
||||
|
||||
Layer 0 (Keller):
|
||||
├── floor_tiles (5cm) Steinboden
|
||||
├── wall_tiles (5cm) Waende
|
||||
├── objects[] Truhen, Faesser
|
||||
└── entities[] Ratten, Spinnen
|
||||
|
||||
Layer 1 (Erdgeschoss): ← Spieler ist hier
|
||||
├── floor_tiles (5cm) Holzboden
|
||||
├── wall_tiles (5cm) Waende mit Fenstern
|
||||
├── objects[] Moebel, Kamin
|
||||
└── entities[] NPC, Katze
|
||||
|
||||
Layer 2 (Obergeschoss):
|
||||
├── floor_tiles (5cm) Teppich
|
||||
├── wall_tiles (5cm) Waende
|
||||
├── objects[] Bett, Schrank
|
||||
└── entities[] -
|
||||
|
||||
Rendering:
|
||||
Aktueller Layer: 100% Opacity, voll interaktiv
|
||||
Layer darüber: Nicht sichtbar (oder 10% als Schatten)
|
||||
Layer darunter: Nicht sichtbar (oder 10% als Schatten)
|
||||
|
||||
Treppe: Special Tile das bei Betreten den Layer wechselt
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 6. Der Area-Editor
|
||||
|
||||
### Strassen-Editor (10cm)
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────┐
|
||||
│ STRASSEN-EDITOR: "Marktplatz" [▶ Testen]│
|
||||
├─────────────────────────────────────────────────────┤
|
||||
│ Werkzeuge: │
|
||||
│ [Pinsel][Radierer][Fuellen][Box][Auswahl] │
|
||||
│ [Tuer platzieren][NPC platzieren][Item platzieren] │
|
||||
├─────────────────────────────────────────────────────┤
|
||||
│ │
|
||||
│ ██████████ ████████████ ██████████ │
|
||||
│ █ Shop A █ █ Brunnen █ █ Shop B █ │
|
||||
│ █ 🚪 █ █ 💧 █ █ 🚪 █ │
|
||||
│ ██████████ ████████████ ██████████ │
|
||||
│ │
|
||||
│ 💡 🌳 🌳 💡 │
|
||||
│ │
|
||||
│ ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ │
|
||||
│ ░░░░░░░░░░░░░ KOPFSTEINPFLASTER ░░░░░░░░░░░░░ │
|
||||
│ ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ │
|
||||
│ │
|
||||
├─────────────────────────────────────────────────────┤
|
||||
│ Layer: [Boden ▼] Palette: [█ Stein][█ Holz][...] │
|
||||
│ Groesse: 50m × 30m Pixel: 500 × 300 │
|
||||
└─────────────────────────────────────────────────────┘
|
||||
|
||||
🚪 = Tuer-Portal (verlinkt zu einem Innenraum)
|
||||
💡 = Laterne (Lichtquelle, Item mit Leucht-Property)
|
||||
🌳 = Baum (Objekt-Sprite)
|
||||
💧 = Brunnen (interaktives Objekt)
|
||||
```
|
||||
|
||||
### Innenraum-Editor (5cm)
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────┐
|
||||
│ INNENRAUM-EDITOR: "Schmiede EG" [▶ Testen] │
|
||||
├─────────────────────────────────────────────────────┤
|
||||
│ Werkzeuge: │
|
||||
│ [Pixel][Radierer][Fuellen][Moebel platzieren] │
|
||||
│ [Stockwerk: EG ▼] [+ Stockwerk] [Treppe setzen] │
|
||||
├─────────────────────────────────────────────────────┤
|
||||
│ │
|
||||
│ ████████████████████████████████████████████████ │
|
||||
│ █ █ │
|
||||
│ █ [Amboss] [Schmelzofen] █ │
|
||||
│ █ █ │
|
||||
│ █ [Werkbank mit Werkzeug] █ │
|
||||
│ █ █ │
|
||||
│ █ 🔥 (Feuer, leuchtet) █ │
|
||||
│ █ █ │
|
||||
│ █ [Waffenstaender] [Regal mit Erzen] █ │
|
||||
│ █ █ │
|
||||
│ █ [Treppe ↑ OG] █ │
|
||||
│ ████████████░░░░░░░░████████████████████████████ │
|
||||
│ Tuer → Strasse │
|
||||
├─────────────────────────────────────────────────────┤
|
||||
│ Layer: [Objekte ▼] Stockwerk: [EG ▼] │
|
||||
│ Groesse: 12m × 8m Pixel: 240 × 160 │
|
||||
└─────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
### Sprite-Editor (1cm) -- Items & Charaktere
|
||||
|
||||
```
|
||||
┌────────────────────────────────────────────────────────┐
|
||||
│ SPRITE-EDITOR: "Flammenschwert" [X Zurueck] │
|
||||
├────────────────────────────────────────────────────────┤
|
||||
│ │
|
||||
│ ┌──────────────────────┐ ┌────────────────────────┐ │
|
||||
│ │ │ │ Werkzeuge: │ │
|
||||
│ │ Pixel-Canvas │ │ [Pinsel 1px] │ │
|
||||
│ │ 64 × 128 bei 1cm │ │ [Pinsel 3px] │ │
|
||||
│ │ │ │ [Radierer] │ │
|
||||
│ │ (Schwert-Form │ │ [Fuellen] │ │
|
||||
│ │ wird hier │ │ [Pipette] │ │
|
||||
│ │ Pixel fuer │ │ [Spiegeln H/V] │ │
|
||||
│ │ Pixel gemalt) │ │ [Drehen 90°] │ │
|
||||
│ │ │ │ │ │
|
||||
│ │ │ │ Palette: │ │
|
||||
│ │ │ │ [█][█][█][█][█][█][+] │ │
|
||||
│ │ │ │ │ │
|
||||
│ └──────────────────────┘ │ Animation: │ │
|
||||
│ │ Frame: [1/4] │ │
|
||||
│ Vorschau: │ [◀][▶][+ Frame] │ │
|
||||
│ ┌──────┐ ┌──────┐ │ [▶ Abspielen] │ │
|
||||
│ │ 1x │ │ 2x │ │ │ │
|
||||
│ │ 🗡️ │ │ 🗡️ │ │ Anchor: [Mitte-Unten ▼] │ │
|
||||
│ └──────┘ └──────┘ │ Hitbox: [Auto ▼] │ │
|
||||
│ └────────────────────────┘ │
|
||||
├────────────────────────────────────────────────────────┤
|
||||
│ [Properties] [Verhalten] [Testen] [Speichern] │
|
||||
└────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 7. Programmierbare Items (gleich wie 3D)
|
||||
|
||||
Identisches 3-Ebenen-System:
|
||||
|
||||
```
|
||||
Ebene 1: Sliders (ab 6 Jahre) → Werte einstellen
|
||||
Ebene 2: Trigger-Actions (ab 8) → WENN X DANN Y
|
||||
Ebene 3: TypeScript (ab 12+) → Voller Code → WASM
|
||||
|
||||
Alle kompilieren zu WASM → laufen in wazero auf dem Go-Server.
|
||||
```
|
||||
|
||||
### 2D-spezifische Trigger & Actions
|
||||
|
||||
**Trigger:**
|
||||
- Spieler beruehrt / betritt Bereich / verlaesst Bereich
|
||||
- Spieler interagiert (Klick / Taste)
|
||||
- Timer (alle X Sekunden)
|
||||
- Tageszeit / Wetter
|
||||
- HP/Mana unter/ueber X
|
||||
- Anderes Item in Radius
|
||||
- Spieler betritt Stockwerk / Area
|
||||
- NPC-Dialog-Option gewaehlt
|
||||
|
||||
**Actions:**
|
||||
- Schaden / Heilen
|
||||
- Partikel (2D) / Sound abspielen
|
||||
- Pixel setzen / loeschen (Welt veraendern!)
|
||||
- Tuer oeffnen / schliessen
|
||||
- Licht ein/aus / Farbe aendern
|
||||
- Teleport (innerhalb Area oder zu anderer Area)
|
||||
- Nachricht / Dialog anzeigen
|
||||
- Variable setzen / abfragen
|
||||
- Item geben / nehmen
|
||||
- NPC bewegen / Animation abspielen
|
||||
- Kamera-Effekt (Shake, Zoom, Flash)
|
||||
|
||||
---
|
||||
|
||||
## 8. Multiplayer
|
||||
|
||||
### Area-basiertes Multiplayer
|
||||
|
||||
```
|
||||
Jede Area (Strasse oder Innenraum) ist eine eigene Server-Instanz.
|
||||
|
||||
Spieler A ist auf Strasse "Marktplatz":
|
||||
→ Verbunden mit Goroutine "marktplatz_001"
|
||||
→ Sieht andere Spieler auf dem Marktplatz
|
||||
→ Sieht NICHT Spieler in Haeusern
|
||||
|
||||
Spieler A geht in die Schmiede:
|
||||
→ Wechselt zu Goroutine "schmiede_eg_001"
|
||||
→ Sieht Spieler in der Schmiede
|
||||
→ Marktplatz-Goroutine laeuft weiter (fuer andere Spieler)
|
||||
|
||||
Server-Architektur:
|
||||
mana-game-server (Go)
|
||||
├── World "bobs-village"
|
||||
│ ├── Area "marktplatz" → Goroutine (5 Spieler)
|
||||
│ ├── Area "schmiede_eg" → Goroutine (2 Spieler)
|
||||
│ ├── Area "schmiede_og" → Goroutine (0 → suspended)
|
||||
│ ├── Area "taverne_eg" → Goroutine (3 Spieler)
|
||||
│ └── Area "taverne_keller" → Goroutine (1 Spieler)
|
||||
│
|
||||
└── World "alices-dungeon"
|
||||
├── Area "eingang" → Goroutine (4 Spieler)
|
||||
└── Area "bosskammer" → Goroutine (4 Spieler)
|
||||
```
|
||||
|
||||
**Vorteile dieses Ansatzes:**
|
||||
- Jede Area ist klein (~50 KB State) → tausende koennen gleichzeitig laufen
|
||||
- Leere Areas werden suspendiert (0 CPU)
|
||||
- Spieler sehen nur relevante andere Spieler
|
||||
- Kein kompliziertes Interest Management (Area = Interest Boundary)
|
||||
- Go-Goroutines sind perfekt dafuer (~4 KB Stack pro Goroutine)
|
||||
|
||||
### Bandbreite pro Spieler
|
||||
|
||||
```
|
||||
Area-Wechsel: ~50-200 KB (komprimierte Area laden)
|
||||
Laufend:
|
||||
Spieler-Positionen: 20Hz × ~20 Bytes × Spieler-in-Area = ~2 KB/s
|
||||
Pixel-Aenderungen: ~0.5 KB/s
|
||||
Chat + Events: ~0.5 KB/s
|
||||
Total: ~3 KB/s pro Spieler
|
||||
|
||||
→ Mac Mini mit 100 Mbit/s Upload: ~3.000 gleichzeitige Spieler
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 9. Datenmodell
|
||||
|
||||
### PostgreSQL
|
||||
|
||||
```sql
|
||||
-- Welten
|
||||
CREATE TABLE worlds (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
creator_id UUID NOT NULL,
|
||||
name TEXT NOT NULL,
|
||||
description TEXT,
|
||||
is_published BOOLEAN DEFAULT false,
|
||||
play_count INTEGER DEFAULT 0,
|
||||
settings JSONB DEFAULT '{}',
|
||||
created_at TIMESTAMPTZ DEFAULT now(),
|
||||
updated_at TIMESTAMPTZ DEFAULT now()
|
||||
);
|
||||
|
||||
-- Areas (Strassen, Innenraeume)
|
||||
CREATE TABLE areas (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
world_id UUID NOT NULL REFERENCES worlds(id) ON DELETE CASCADE,
|
||||
name TEXT NOT NULL,
|
||||
type TEXT NOT NULL, -- 'street', 'interior', 'dungeon'
|
||||
resolution REAL NOT NULL, -- 0.10 oder 0.05
|
||||
width INTEGER NOT NULL, -- in Pixeln
|
||||
height INTEGER NOT NULL,
|
||||
floors INTEGER DEFAULT 1, -- Anzahl Stockwerke
|
||||
pixel_data BYTEA NOT NULL, -- Komprimiert (alle Layer)
|
||||
palette JSONB DEFAULT '[]',
|
||||
entities JSONB DEFAULT '[]', -- NPCs, Items, Lichtquellen
|
||||
portals JSONB DEFAULT '[]', -- Tueren zu anderen Areas
|
||||
spawn_point JSONB, -- Wo Spieler erscheinen
|
||||
created_at TIMESTAMPTZ DEFAULT now(),
|
||||
updated_at TIMESTAMPTZ DEFAULT now()
|
||||
);
|
||||
|
||||
-- Items (Sprites + Properties + Behavior)
|
||||
CREATE TABLE items (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
creator_id UUID NOT NULL,
|
||||
name TEXT NOT NULL,
|
||||
description TEXT,
|
||||
sprite_data BYTEA NOT NULL, -- PNG oder Roh-Pixel komprimiert
|
||||
sprite_width INTEGER NOT NULL,
|
||||
sprite_height INTEGER NOT NULL,
|
||||
animation_frames INTEGER DEFAULT 1,
|
||||
resolution REAL DEFAULT 0.01, -- 1cm fuer Items
|
||||
properties JSONB DEFAULT '{}',
|
||||
behavior JSONB DEFAULT '[]',
|
||||
script TEXT,
|
||||
wasm_binary BYTEA,
|
||||
rarity TEXT DEFAULT 'common',
|
||||
capabilities TEXT[] DEFAULT '{}',
|
||||
is_published BOOLEAN DEFAULT false,
|
||||
created_at TIMESTAMPTZ DEFAULT now()
|
||||
);
|
||||
|
||||
-- Portale (Verbindungen zwischen Areas)
|
||||
CREATE TABLE portals (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
from_area_id UUID NOT NULL REFERENCES areas(id),
|
||||
to_area_id UUID NOT NULL REFERENCES areas(id),
|
||||
from_position JSONB NOT NULL, -- {x, y, floor}
|
||||
to_position JSONB NOT NULL, -- {x, y, floor}
|
||||
requires_key UUID REFERENCES items(id) -- Optional: Item zum Oeffnen
|
||||
);
|
||||
|
||||
-- Inventar
|
||||
CREATE TABLE inventories (
|
||||
player_id UUID NOT NULL,
|
||||
item_id UUID NOT NULL,
|
||||
slot INTEGER NOT NULL,
|
||||
quantity INTEGER DEFAULT 1,
|
||||
instance_data JSONB DEFAULT '{}',
|
||||
PRIMARY KEY (player_id, item_id, slot)
|
||||
);
|
||||
```
|
||||
|
||||
### Dexie.js (Client-Side)
|
||||
|
||||
```typescript
|
||||
const db = new Dexie('manavoxel');
|
||||
|
||||
db.version(1).stores({
|
||||
worlds: 'id, creatorId, name, isPublished, updatedAt',
|
||||
areas: 'id, worldId, name, type, updatedAt',
|
||||
items: 'id, creatorId, name, rarity, updatedAt',
|
||||
portals: 'id, fromAreaId, toAreaId',
|
||||
inventories: '[playerId+itemId+slot], playerId',
|
||||
settings: 'key'
|
||||
});
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 10. Build-Plan
|
||||
|
||||
### Phase 0: Proof of Concept (2 Wochen)
|
||||
|
||||
```
|
||||
Woche 1: PixiJS + Tilemap
|
||||
□ PixiJS 8 + SvelteKit Setup
|
||||
□ Tilemap-Renderer (10cm, 500 × 300)
|
||||
□ Kamera: Scroll + Zoom
|
||||
□ 10 Materialien (Stein, Holz, Gras, Wasser, Sand, ...)
|
||||
□ Pixel-Editor: Setzen/Loeschen/Fuellen
|
||||
□ Undo/Redo
|
||||
|
||||
Woche 2: Spieler + Physik
|
||||
□ Charakter-Sprite (Standard, 4 Richtungen)
|
||||
□ 2D AABB Kollision mit Tilemap
|
||||
□ Laufen in 8 Richtungen
|
||||
□ Tueren: Portal zu anderem Tilemap (Innenraum-Prototyp)
|
||||
|
||||
Ergebnis: Zelda-aehnlich im Browser, Welt editierbar, Tuer fuehrt in Raum
|
||||
```
|
||||
|
||||
### Phase 1: Zoom-Stufen + Editor (3 Wochen)
|
||||
|
||||
```
|
||||
Woche 3: Innenraum (5cm)
|
||||
□ Innenraum-Renderer (5cm Tilemap, 3 Layer fuer Stockwerke)
|
||||
□ Treppen (Layer-Wechsel mit Transition)
|
||||
□ Moebel als platzierbare Objekte
|
||||
□ Portal-System: Strasse ↔ Innenraum
|
||||
|
||||
Woche 4: Detail-View + Sprite-Editor
|
||||
□ Detail-View Panel (Svelte, 1cm Canvas)
|
||||
□ Sprite-Editor (Pixel-fuer-Pixel, 64×128)
|
||||
□ Farb-Palette, Pinsel, Spiegeln
|
||||
□ Animation Frames (Spritesheet)
|
||||
□ "Als Item speichern"
|
||||
|
||||
Woche 5: Items in der Welt
|
||||
□ Item im Inventar (Svelte UI)
|
||||
□ Item in der Hand halten
|
||||
□ Item in der Welt ablegen / aufheben
|
||||
□ Moebel platzieren (Innenraum einrichten)
|
||||
|
||||
Ergebnis: Strasse → Haus betreten → Moebel platzieren →
|
||||
Item zeichnen → in die Welt bringen
|
||||
```
|
||||
|
||||
### Phase 2: Programmierung (3 Wochen)
|
||||
|
||||
```
|
||||
Woche 6: Property Sliders
|
||||
□ Property-Panel (Svelte): Schaden, Element, Haltbarkeit...
|
||||
□ Standard-Verhalten pro Property-Kombination
|
||||
□ Item benutzen → Effekt sichtbar (Partikel, Sound)
|
||||
|
||||
Woche 7: Trigger-Actions
|
||||
□ Trigger-Action Builder UI (Svelte)
|
||||
□ 10 Trigger + 15 Actions
|
||||
□ Trigger-Action → TypeScript → WASM Compilation
|
||||
□ wazero Sandbox auf Go-Server
|
||||
|
||||
Woche 8: Interaktive Welt
|
||||
□ Items interagieren mit Welt (Pixel zerstoeren, setzen)
|
||||
□ Items interagieren miteinander (Events)
|
||||
□ NPCs (einfach: feste Position, Dialog bei Interaktion)
|
||||
□ Licht-System (2D Raycast, optional)
|
||||
|
||||
Ergebnis: Spieler baut Schwert → gibt ihm Feuer → benutzt es →
|
||||
Pixel-Explosion an der Wand → Geheimgang dahinter!
|
||||
```
|
||||
|
||||
### Phase 3: Multiplayer (3 Wochen)
|
||||
|
||||
```
|
||||
Woche 9: Go Game Server
|
||||
□ mana-game-server (Go)
|
||||
□ Area-basiertes Multiplayer (1 Goroutine pro Area)
|
||||
□ WebSocket (coder/websocket)
|
||||
□ Pixel-Sync (Delta-Kompression)
|
||||
|
||||
Woche 10: Multiplayer-Sync
|
||||
□ Spieler-Positionen
|
||||
□ Area-Wechsel (Spieler geht in Haus → verschwindet auf Strasse)
|
||||
□ Item-Sync + Inventar
|
||||
□ Server-autoritative Validierung
|
||||
|
||||
Woche 11: Social Features
|
||||
□ Text-Chat (per Area)
|
||||
□ Emotes
|
||||
□ PvP (Items benutzen gegen andere Spieler)
|
||||
□ Welt-Link teilen
|
||||
|
||||
Ergebnis: 2-20 Spieler im gleichen Dorf, betreten Haeuser,
|
||||
kaempfen, handeln, chatten
|
||||
```
|
||||
|
||||
### Phase 4: Platform (3 Wochen)
|
||||
|
||||
```
|
||||
Woche 12: Auth + Persistence
|
||||
□ mana-auth Integration
|
||||
□ Welten in PostgreSQL speichern
|
||||
□ Local-First (Dexie.js ↔ mana-sync)
|
||||
□ Guest Mode
|
||||
□ Offline-Editor
|
||||
|
||||
Woche 13: Discovery + Economy
|
||||
□ Welt veroeffentlichen
|
||||
□ Welt-Suche (Meilisearch)
|
||||
□ TigerBeetle: Mana-Waehrung
|
||||
□ Item-Marketplace (einfach)
|
||||
□ Creator-Profil
|
||||
|
||||
Woche 14: Polish + Launch
|
||||
□ Onboarding Tutorial ("Baue dein erstes Haus")
|
||||
□ Welt-Templates (Dorf, Dungeon, Arena, Haus)
|
||||
□ Mobile Touch-Steuerung
|
||||
□ Performance-Optimierung
|
||||
□ Bug Fixes
|
||||
|
||||
Ergebnis: MVP FERTIG! Spielbare 2D-Plattform im Browser.
|
||||
```
|
||||
|
||||
### Timeline
|
||||
|
||||
```
|
||||
Woche 1 2 3 4 5 6 7 8 9 10 11 12 13 14
|
||||
[PoC ][Zoom+Editor ][Programmier.][Multiplayer][Platform ]
|
||||
|
||||
Monat 1 2 3 3.5
|
||||
|
||||
MVP spielbar nach: ~14 Wochen (3.5 Monate)
|
||||
```
|
||||
|
||||
### Post-MVP Roadmap
|
||||
|
||||
```
|
||||
Phase 5 (Monat 4-5): AI + Voice
|
||||
□ AI NPCs (mana-llm, Dialog-System)
|
||||
□ Voice Chat (mana-stt + WebRTC)
|
||||
□ AI Palette-Generierung (mana-image-gen)
|
||||
□ TypeScript Scripting (Ebene 3)
|
||||
|
||||
Phase 6 (Monat 5-6): Advanced World
|
||||
□ Tag/Nacht-Zyklus
|
||||
□ Wetter (Regen-Pixel, Schnee)
|
||||
□ Erweiterte Physik (Wasser fliessen, Sand fallen)
|
||||
□ Voxel-Destruction (Krater, Truemmer)
|
||||
|
||||
Phase 7 (Monat 6-8): Zoom-Out-Stufen
|
||||
□ Stadt-View (1m/px) -- Strassen verbinden
|
||||
□ Region-View (10m/px) -- Staedte verbinden
|
||||
□ Weltkarte (100m/px) -- Ueberblick
|
||||
|
||||
Phase 8 (Monat 8+): Scale + Community
|
||||
□ Gilden / Gruppen
|
||||
□ Events / Wettbewerbe
|
||||
□ Advanced Economy (Handel, Auktionen)
|
||||
□ Cross-Game Items
|
||||
□ Community-Moderation-Tools
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 11. Zusammenfassung
|
||||
|
||||
```
|
||||
PROJEKT: ManaVoxel 2D
|
||||
ANSICHT: Top-Down, 6 Zoom-Stufen
|
||||
RESOLUTION: Strasse 10cm, Innenraum 5cm, Items 1cm
|
||||
STACK: PixiJS 8 + Svelte 5 + Go Server + Dexie.js
|
||||
SPRACHEN: TypeScript (90%) + Go (10%)
|
||||
MVP: 14 Wochen (3.5 Monate)
|
||||
FEATURES MVP: Strasse + Innenraum + Stockwerke + Item-Editor
|
||||
+ 3 Programmier-Ebenen + Multiplayer + Economy
|
||||
+ Local-First + Guest Mode + Offline-Editor
|
||||
SPAETER: Stadt-View, Region, Weltkarte, AI NPCs, Voice
|
||||
HOSTING: Mac Mini (Self-Hosted) + Cloudflare
|
||||
ECONOMY: TigerBeetle, 70% Creator Revenue
|
||||
WIEDERVERW.: 14 bestehende Services direkt nutzbar
|
||||
NEUER CODE: ~15.000-25.000 Zeilen TypeScript + ~5.000 Zeilen Go
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
*Erstellt: 28. Maerz 2026*
|
||||
*Projekt: ManaVoxel 2D -- Teil des Manacore-Oekosystems*
|
||||
470
NewAppIdeas/Roblox Reimagined/Resolution-10cm-Analyse.md
Normal file
470
NewAppIdeas/Roblox Reimagined/Resolution-10cm-Analyse.md
Normal file
|
|
@ -0,0 +1,470 @@
|
|||
# 10cm Voxel: Detaillierte Analyse
|
||||
|
||||
## Kleine Welten, hohe Aufloesung -- warum das funktioniert
|
||||
|
||||
---
|
||||
|
||||
## Die Kern-Erkenntnis
|
||||
|
||||
Die meisten Voxel-Games optimieren fuer **grosse Welten bei niedriger Resolution**:
|
||||
|
||||
```
|
||||
Minecraft: Unendliche Welt × 1.0m Bloecke = riesig, grob
|
||||
Unsere Vision: Begrenzte Welt × 0.1m Voxel = klein, detailliert
|
||||
```
|
||||
|
||||
Das ist kein Nachteil -- es ist eine **bewusste Design-Entscheidung** die zu einem voellig anderen Spiel fuehrt:
|
||||
|
||||
| Minecraft-Ansatz | Unser Ansatz |
|
||||
| ------------------------------------ | ------------------------------------------------ |
|
||||
| Riesige Landschaften erkunden | Dichte, handgefertigte Raeume |
|
||||
| Grobe Bloecke, Texturen geben Detail | Voxel SIND das Detail |
|
||||
| Items sind 2D-Sprites im 3D-Raum | Items sind 3D-Voxel-Skulpturen |
|
||||
| Gebaeude: 10-30 Bloecke hoch | Gebaeude: 30-100 Voxel hoch, mit Moebeln |
|
||||
| Ein Schwert = 1 Pixel-Icon | Ein Schwert = detailliertes 3D-Modell aus Voxeln |
|
||||
|
||||
---
|
||||
|
||||
## Die Zahlen: 10cm vs. 25cm vs. 50cm
|
||||
|
||||
### Voxel-Dichte pro Kubikmeter
|
||||
|
||||
| Resolution | Voxel/Meter | Voxel/m³ | Ein Mensch (~0.5m × 0.3m × 1.8m) |
|
||||
| ---------- | ----------- | --------- | ----------------------------------------- |
|
||||
| 0.50 m | 2 | 8 | 2 × 1 × 4 = **8 Voxel** (Klötzchen) |
|
||||
| 0.25 m | 4 | 64 | 2 × 1 × 7 = **14 Voxel** (erkennbar) |
|
||||
| **0.10 m** | **10** | **1.000** | **5 × 3 × 18 = 270 Voxel** (detailliert!) |
|
||||
| 0.05 m | 20 | 8.000 | 10 × 6 × 36 = **2.160 Voxel** (sehr fein) |
|
||||
|
||||
**Bei 10cm kann man:**
|
||||
|
||||
- Tischbeine einzeln modellieren (1 Voxel breit)
|
||||
- Fensterrahmen bauen (1 Voxel dick)
|
||||
- Treppen mit realistischen Stufen (2 Voxel hoch, 3 Voxel tief)
|
||||
- Moebel mit erkennbarer Form (Stuhl: ~5×5×9 Voxel)
|
||||
- Schwerter mit Klinge UND Griff (3×1×15 Voxel)
|
||||
- Gesichter auf Charakteren andeuten (Kopf: ~3×3×3 Voxel, Augen 1 Voxel)
|
||||
|
||||
### Memory pro Chunk
|
||||
|
||||
| Chunk-Groesse | Welt-Abdeckung bei 0.1m | Voxel-Count | Speicher (u16) | Komprimiert |
|
||||
| ------------- | ----------------------- | ----------- | -------------- | ------------ |
|
||||
| 16³ | 1.6m × 1.6m × 1.6m | 4.096 | 8 KB | ~1-4 KB |
|
||||
| **32³** | **3.2m × 3.2m × 3.2m** | **32.768** | **64 KB** | **~3-15 KB** |
|
||||
| 64³ | 6.4m × 6.4m × 6.4m | 262.144 | 512 KB | ~20-100 KB |
|
||||
|
||||
**Empfehlung: 32³ Chunks bei 0.1m.** Ein Chunk deckt 3.2m ab -- etwa die Groesse eines kleinen Raums. Das ist eine natuerliche Einheit.
|
||||
|
||||
### Welten-Groessen (komplett im Speicher)
|
||||
|
||||
| Welt-Groesse | Beschreibung | Chunks (32³) | Voxel total | RAM (roh) | RAM (komprimiert) |
|
||||
| --------------------- | ------------------ | ----------------- | ----------- | --------- | ----------------- |
|
||||
| **16m × 16m × 8m** | Ein Zimmer / Arena | 5×5×3 = 75 | 2.4M | 5 MB | ~0.5-1.5 MB |
|
||||
| **32m × 32m × 16m** | Ein Haus / Dungeon | 10×10×5 = 500 | 16M | 32 MB | ~3-8 MB |
|
||||
| **64m × 64m × 32m** | Ein Dorf / Level | 20×20×10 = 4.000 | 131M | 256 MB | ~25-60 MB |
|
||||
| **128m × 128m × 32m** | Kleine Insel | 40×40×10 = 16.000 | 524M | 1 GB | ~100-200 MB |
|
||||
|
||||
**Sweet Spot fuer den Browser: 32m × 32m × 16m bis 64m × 64m × 32m.**
|
||||
|
||||
Das sind 500-4.000 Chunks, komprimiert 3-60 MB. Passt locker in WASM-Memory (max 4 GB).
|
||||
|
||||
---
|
||||
|
||||
## Warum 10cm die RICHTIGE Wahl ist
|
||||
|
||||
### 1. Items brauchen keine separate Resolution mehr
|
||||
|
||||
Bei 0.25m brauchten wir zwei Resolutionen (Welt 0.25m, Items 0.0625m). Bei 10cm:
|
||||
|
||||
```
|
||||
0.25m Welt + 0.0625m Items:
|
||||
Zwei verschiedene Voxel-Systeme
|
||||
Zwei verschiedene Editoren
|
||||
Zwei verschiedene Mesh-Pipelines
|
||||
Komplex!
|
||||
|
||||
0.10m fuer ALLES:
|
||||
Ein Voxel-System
|
||||
Ein Editor (Zoom rein = Item-Detail, Zoom raus = Welt)
|
||||
Eine Mesh-Pipeline
|
||||
Einfach!
|
||||
```
|
||||
|
||||
Ein Schwert bei 10cm: ~3 × 1 × 15 Voxel = 45 Voxel. Detailliert genug fuer erkennbare Form mit Klinge, Parierstange und Griff. Kein separater Item-Editor noetig -- das Item wird direkt in der Welt geformt.
|
||||
|
||||
### 2. MagicaVoxel-Aesthetik nativ
|
||||
|
||||
10cm Voxel erzeugen genau die Aesthetik, die MagicaVoxel-Kuenstler lieben:
|
||||
|
||||
```
|
||||
MagicaVoxel-Modelle: Typisch 32³ bis 128³ bei frei gewaehlter Skala
|
||||
Unsere Welt bei 10cm: Natuerlich im MagicaVoxel-Look
|
||||
→ Die gesamte MagicaVoxel-Community wird sich heimisch fuehlen
|
||||
→ Import von .vox-Dateien trivial (1:1 Mapping)
|
||||
```
|
||||
|
||||
### 3. Kleinere Welten = Bessere Welten
|
||||
|
||||
Grosse leere Welten sind langweilig. Kleine dichte Welten sind aufregend:
|
||||
|
||||
| Grosse Minecraft-Welt | Kleine 10cm-Welt |
|
||||
| ------------------------------------------- | ------------------------------- |
|
||||
| 90% leere Landschaft | Jeder Voxel ist bewusst gesetzt |
|
||||
| "Erkunde 20 Minuten bis zum naechsten Dorf" | "Jeder Raum hat Geheimnisse" |
|
||||
| Generiertes Terrain (repetitiv) | Handgefertigt (einzigartig) |
|
||||
| Spieler verirren sich | Spieler entdecken Details |
|
||||
|
||||
**Denke an:** Portal-Level, Zelda-Dungeons, Untitled Goose Game's Dorf, Hitman-Maps -- alles kleine, dichte, meisterhafte Raeume.
|
||||
|
||||
### 4. Voxel-Destruction wird machbar
|
||||
|
||||
Bei 10cm koennen einzelne Voxel zerbroeckeln und es sieht gut aus:
|
||||
|
||||
```
|
||||
Schwert trifft Wand:
|
||||
0.25m: Ein riesiger Block verschwindet → sieht unnatuerlich aus
|
||||
0.10m: 3-5 kleine Voxel splittern ab → sieht nach Beschaedigung aus
|
||||
|
||||
Explosion (Radius 1m):
|
||||
0.25m: ~60 Bloecke betroffen → grober Krater
|
||||
0.10m: ~4.200 Voxel betroffen → detaillierter Krater mit Truemmern
|
||||
```
|
||||
|
||||
### 5. Inneneinrichtung wird moeglich
|
||||
|
||||
Bei 10cm kann man Raeume **einrichten**, nicht nur bauen:
|
||||
|
||||
```
|
||||
Ein Raum (3m × 4m × 3m) bei verschiedenen Resolutionen:
|
||||
|
||||
0.25m: 12 × 16 × 12 = 2.304 Voxel
|
||||
→ Waende, Boden, Decke. Fertig. Kein Platz fuer Moebel-Details.
|
||||
|
||||
0.10m: 30 × 40 × 30 = 36.000 Voxel
|
||||
→ Waende + Boden + Decke: ~6.000 Voxel
|
||||
→ Verbleibend fuer Moebel: ~30.000 Voxel
|
||||
→ Tisch (8×5×8), Stuhl (5×5×9), Regal (10×3×15), Lampe (3×3×6)
|
||||
→ Detaillierte Innenraeume moeglich!
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Performance-Analyse: Kann der Browser das?
|
||||
|
||||
### Greedy Meshing bei 10cm
|
||||
|
||||
```
|
||||
32³ Chunk = 32.768 Voxel
|
||||
Greedy Meshing: ~0.5-1ms pro Chunk (Rust, CPU, single thread)
|
||||
→ Identisch zu 0.25m! Die Chunk-Groesse bestimmt die Mesh-Zeit, nicht die Resolution.
|
||||
|
||||
Aber: Mehr Chunks pro Welt (weil jeder Chunk weniger Welt abdeckt)
|
||||
→ 32m × 32m × 16m = 500 Chunks
|
||||
→ Alle meshen: 500 × 1ms = 500ms (initial, parallelisiert: ~60ms auf 8 Cores)
|
||||
→ Laufend: Nur geaenderte Chunks remeshen (1-5 pro Frame)
|
||||
→ Kein Problem!
|
||||
```
|
||||
|
||||
### Triangle Count
|
||||
|
||||
```
|
||||
Typische Voxel-Szene (Raum mit Moebeln):
|
||||
~30% der Voxel sind an der Oberflaeche sichtbar
|
||||
500 Chunks × 32.768 Voxel × 30% sichtbar = ~5 Millionen sichtbare Voxel
|
||||
|
||||
Greedy Meshing reduziert um ~80-90%:
|
||||
→ ~500.000 - 1.000.000 Quads = 1-2 Millionen Dreiecke
|
||||
|
||||
Zum Vergleich:
|
||||
- Ein modernes Game rendert 5-50 Millionen Dreiecke
|
||||
- WebGPU kann problemlos 2M Dreiecke bei 60fps
|
||||
→ Performance ist kein Problem!
|
||||
```
|
||||
|
||||
### GPU Memory
|
||||
|
||||
```
|
||||
Mesh-Daten pro Chunk:
|
||||
Durchschnitt: ~500-2000 Quads × 4 Vertices × 24 Bytes/Vertex = ~50-200 KB
|
||||
|
||||
500 Chunks: 25-100 MB GPU Memory fuer Meshes
|
||||
+ 32 MB Voxel-Daten (komprimiert im RAM, nicht VRAM)
|
||||
+ Engine, UI, Texturen: ~100 MB
|
||||
|
||||
Gesamt: ~160-230 MB
|
||||
→ Passt auf jede Desktop-GPU (2+ GB)
|
||||
→ Passt auf Mid-Range Mobile (1-2 GB shared)
|
||||
→ Passt in WASM-Memory (4 GB max)
|
||||
```
|
||||
|
||||
### Netzwerk
|
||||
|
||||
```
|
||||
Multiplayer: 500 Chunks initial laden
|
||||
|
||||
Komprimiert (RLE + LZ4): ~3-8 MB
|
||||
Transfer bei 10 Mbit/s: 2.4-6.4 Sekunden
|
||||
→ Akzeptabel! Und mit progressivem Laden (nahe Chunks zuerst) spielbar nach ~1 Sek.
|
||||
|
||||
Laufende Aenderungen:
|
||||
Spieler setzt 1 Voxel: ~12 Bytes Netzwerk-Paket
|
||||
20 Spieler, je 5 Voxel/Sekunde: ~1.2 KB/s
|
||||
→ Vernachlaessigbar
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Adaptive Skalierung bei 10cm
|
||||
|
||||
### Quality Tiers (angepasst fuer kleinere Welten)
|
||||
|
||||
```
|
||||
Tier 1: Desktop High
|
||||
├─ Volle 10cm Resolution ueberall
|
||||
├─ Sichtweite: 64m (gesamte grosse Welt sichtbar)
|
||||
├─ Schatten: Shadow Maps
|
||||
├─ AO: Screen-Space Ambient Occlusion
|
||||
├─ Partikel: Voll
|
||||
└─ Ziel: 60fps
|
||||
|
||||
Tier 2: Desktop/Tablet Medium
|
||||
├─ 10cm nah (0-16m), 20cm fern (16-32m)
|
||||
├─ Sichtweite: 32m
|
||||
├─ Schatten: Vereinfacht
|
||||
├─ AO: Voxel-basiert (precomputed)
|
||||
├─ Partikel: Reduziert
|
||||
└─ Ziel: 60fps
|
||||
|
||||
Tier 3: Mobile Low
|
||||
├─ 10cm nah (0-8m), 20cm fern (8-16m), 40cm sehr fern
|
||||
├─ Sichtweite: 16m (reicht fuer kleine Welten!)
|
||||
├─ Schatten: Keine
|
||||
├─ AO: Keine
|
||||
├─ Partikel: Minimal
|
||||
└─ Ziel: 30fps
|
||||
```
|
||||
|
||||
**Wichtig:** Bei kleinen Welten (32m × 32m) ist selbst 16m Sichtweite oft ausreichend -- der Spieler sieht die halbe Welt!
|
||||
|
||||
### LOD fuer 10cm Voxel
|
||||
|
||||
```
|
||||
Distanz 0-16m: Volle 10cm (1:1)
|
||||
Distanz 16-32m: 20cm (2×2×2 Voxel → 1, dominanter Typ)
|
||||
Distanz 32-64m: 40cm (4×4×4 → 1)
|
||||
Distanz 64m+: Nicht noetig (Welt endet vorher)
|
||||
```
|
||||
|
||||
LOD-Uebergaenge sind bei 10cm weniger auffaellig als bei groesseren Voxeln, weil der Detailverlust subtiler ist.
|
||||
|
||||
---
|
||||
|
||||
## Design-Konsequenzen: Wie aendert 10cm das Spiel?
|
||||
|
||||
### Welt-Typen die moeglich werden
|
||||
|
||||
| Welt-Typ | Groesse | Chunks | Beschreibung |
|
||||
| ----------------- | --------------- | --------------- | ----------------------------- |
|
||||
| **Zimmer** | 6m × 8m × 3m | 2×3×1 = 6 | Escape Room, Puzzle, Diorama |
|
||||
| **Wohnung** | 15m × 12m × 3m | 5×4×1 = 20 | Roleplay, Verstecken, Mystery |
|
||||
| **Haus + Garten** | 20m × 20m × 8m | 7×7×3 = 147 | Simulation, Story, Abenteuer |
|
||||
| **Dungeon** | 32m × 32m × 16m | 10×10×5 = 500 | RPG, Kampf, Exploration |
|
||||
| **Dorf** | 48m × 48m × 16m | 15×15×5 = 1.125 | Open World (klein), Social |
|
||||
| **Arena** | 30m × 30m × 10m | 10×10×4 = 400 | PvP, Sport, Wettbewerb |
|
||||
| **Kleine Insel** | 64m × 64m × 20m | 20×20×7 = 2.800 | Exploration, Survival |
|
||||
|
||||
### Was Spieler bauen koennen
|
||||
|
||||
```
|
||||
Ein detailliertes Haus bei 10cm:
|
||||
|
||||
Erdgeschoss (10m × 8m × 3m = 100 × 80 × 30 Voxel):
|
||||
├── Wohnzimmer: Sofa (15×6×8), Couchtisch (8×5×4), TV (12×1×8), Teppich
|
||||
├── Kueche: Herd (6×6×9), Kuehlschrank (6×6×18), Arbeitsplatte, Spuele
|
||||
├── Flur: Garderobe, Schuhregal, Spiegel
|
||||
├── Treppen: Echte Stufen (jede 2 Voxel hoch, 3 tief)
|
||||
└── Details: Tuerklinken (1 Voxel), Fensterrahmen, Lichtschalter
|
||||
|
||||
Dachgeschoss:
|
||||
├── Schlafzimmer: Bett (20×12×5), Nachttisch, Kleiderschrank
|
||||
├── Bad: Badewanne (15×7×6), Waschbecken, Toilette
|
||||
└── Balkon mit Gelaender (1 Voxel dick!)
|
||||
|
||||
→ Das ist UNMOEGLICH bei 0.25m oder 1m. Bei 10cm ist es natuerlich.
|
||||
```
|
||||
|
||||
### Items bei 10cm (ohne separate Resolution!)
|
||||
|
||||
```
|
||||
Schwert: 3 × 1 × 15 Voxel → Klinge, Parierstange, Griff erkennbar
|
||||
Axt: 5 × 1 × 12 Voxel → Kopf + Stiel
|
||||
Schild: 8 × 2 × 10 Voxel → Rund oder rechteckig, mit Emblem
|
||||
Trank: 3 × 3 × 5 Voxel → Flasche mit Korken
|
||||
Ring: 3 × 3 × 1 Voxel → Erkennbar als Ring
|
||||
Fackel: 1 × 1 × 8 Voxel → Stab + leuchtender Kopf
|
||||
Buch: 4 × 1 × 5 Voxel → Buchform mit Seiten
|
||||
Krone: 5 × 5 × 3 Voxel → Zacken sichtbar!
|
||||
Schluessel: 1 × 1 × 5 Voxel → Bart + Griff
|
||||
Blume: 2 × 2 × 4 Voxel → Stiel + Bluete
|
||||
```
|
||||
|
||||
**Alle Items werden direkt in der Welt modelliert, im gleichen Voxel-System.** Kein Wechsel in einen separaten Editor noetig.
|
||||
|
||||
---
|
||||
|
||||
## Vergleich mit existierenden Spielen
|
||||
|
||||
| Spiel | Voxel-Groesse | Welt-Groesse | Aesthetik |
|
||||
| ----------- | ------------- | ------------------------- | --------------------- |
|
||||
| Minecraft | 1.0m | Unendlich | Ikonisch, grob |
|
||||
| Trove | ~0.5m | Gross, generiert | Cartoon, bunt |
|
||||
| Cube World | ~0.5m | Gross, generiert | Anime-inspiriert |
|
||||
| LEGO Worlds | ~0.3m | Mittel | LEGO-artig |
|
||||
| Teardown | ~0.1m | Klein (ein Level) | Fotorealistisch |
|
||||
| **Wir** | **0.1m** | **Klein (handgefertigt)** | **MagicaVoxel-artig** |
|
||||
|
||||
Teardown beweist, dass 10cm-Voxel in kleinen Welten funktionieren. Wir nehmen deren Resolution, aber statt Fotorealismus gehen wir Richtung **stylisierter MagicaVoxel-Look** -- bunter, charmanter, browser-freundlicher.
|
||||
|
||||
---
|
||||
|
||||
## Angepasstes Chunk-System
|
||||
|
||||
### 32³ Chunks bei 0.1m
|
||||
|
||||
```
|
||||
Ein Chunk = 32 × 32 × 32 Voxel = 3.2m × 3.2m × 3.2m
|
||||
|
||||
┌───────────────────────────┐
|
||||
│ 3.2m (32 Voxel) │
|
||||
│ │
|
||||
│ Ein Chunk ist ungefaehr │ 3.2m
|
||||
│ so gross wie: │
|
||||
│ - Ein Schreibtisch │
|
||||
│ - Eine Badewanne │
|
||||
│ - Ein kleines Auto │
|
||||
│ │
|
||||
└───────────────────────────┘
|
||||
```
|
||||
|
||||
### Welt-Layout Beispiel: "Dungeon" (32m × 32m × 16m)
|
||||
|
||||
```
|
||||
10 × 10 × 5 = 500 Chunks
|
||||
|
||||
Aufsicht (jedes Kaestchen = 1 Chunk = 3.2m):
|
||||
┌──┬──┬──┬──┬──┬──┬──┬──┬──┬──┐
|
||||
│ │ │██│██│██│██│██│ │ │ │
|
||||
├──┼──┼──┼──┼──┼──┼──┼──┼──┼──┤
|
||||
│ │██│░░│░░│░░│░░│░░│██│ │ │
|
||||
├──┼──┼──┼──┼──┼──┼──┼──┼──┼──┤
|
||||
│ │██│░░│██│██│░░│░░│██│██│ │
|
||||
├──┼──┼──┼──┼──┼──┼──┼──┼──┼──┤
|
||||
│██│░░│░░│██│ │░░│██│░░│██│ │
|
||||
├──┼──┼──┼──┼──┼──┼──┼──┼──┼──┤
|
||||
│██│░░│░░│░░│░░│░░│░░│░░│██│ │
|
||||
├──┼──┼──┼──┼──┼──┼──┼──┼──┼──┤
|
||||
│██│░░│██│░░│░░│██│░░│░░│░░│██│
|
||||
├──┼──┼──┼──┼──┼──┼──┼──┼──┼──┤
|
||||
│ │██│██│░░│██│██│░░│██│░░│██│
|
||||
├──┼──┼──┼──┼──┼──┼──┼──┼──┼──┤
|
||||
│ │ │██│░░│░░│░░│░░│██│░░│██│
|
||||
├──┼──┼──┼──┼──┼──┼──┼──┼──┼──┤
|
||||
│ │ │ │██│██│██│██│░░│██│ │
|
||||
├──┼──┼──┼──┼──┼──┼──┼──┼──┼──┤
|
||||
│ │ │ │ │ │ │██│██│ │ │
|
||||
└──┴──┴──┴──┴──┴──┴──┴──┴──┴──┘
|
||||
██ = Wand-Chunks (viele Voxel, groessere Meshes)
|
||||
░░ = Raum-Chunks (meist Luft, winzige Meshes)
|
||||
= Ausserhalb (nicht geladen)
|
||||
```
|
||||
|
||||
**Optimierung: Nur Chunks mit Inhalt laden.** In einem Dungeon sind ~60% der Chunks leer (Luft oder ausserhalb). Statt 500 werden nur ~200 Chunks wirklich gemesht.
|
||||
|
||||
---
|
||||
|
||||
## Einheitliche Resolution: Kein Item-Editor mehr noetig
|
||||
|
||||
### Das vereinfachte System
|
||||
|
||||
```
|
||||
VORHER (Dual-Resolution): NACHHER (10cm einheitlich):
|
||||
|
||||
Welt-Editor (0.25m) Ein Editor fuer alles:
|
||||
└── Grosse Bloecke setzen ├── Zoom out: Welt formen
|
||||
├── Zoom in: Moebel bauen
|
||||
Item-Editor (0.0625m) └── Zoom ganz nah: Item sculpting
|
||||
└── Separater Modus
|
||||
└── Anderes Grid Alles im gleichen Voxel-Space!
|
||||
└── Andere Tools Gleiche Tools, gleicher Workflow!
|
||||
|
||||
Zwei Mesh-Pipelines: Eine Mesh-Pipeline:
|
||||
└── Welt-Mesh (Greedy) └── Alles ist Greedy Meshing
|
||||
└── Item-Mesh (separat)
|
||||
|
||||
Zwei Datenformate: Ein Datenformat:
|
||||
└── Chunk-Voxel (u16) └── Chunk-Voxel (u16)
|
||||
└── Item-Voxel (u16, anderes Grid) └── Items = markierte Voxel-Gruppen
|
||||
```
|
||||
|
||||
### Items als "Voxel-Gruppen"
|
||||
|
||||
Statt Items in einem separaten Editor zu bauen, werden sie **in der Welt definiert**:
|
||||
|
||||
```
|
||||
1. Spieler baut einen Tisch aus Voxeln (direkt in der Welt)
|
||||
2. Spieler waehlt die Voxel-Gruppe aus (Box-Select oder Flood-Fill)
|
||||
3. "Als Item speichern" → Voxel-Gruppe wird zum tragbaren Item
|
||||
4. Spieler weist Properties/Verhalten zu (Slider, Trigger-Actions)
|
||||
5. Item landet im Inventar → kann platziert, benutzt, geteilt werden
|
||||
|
||||
Das Item "merkt sich" seine Voxel-Daten relativ zum Ankerpunkt.
|
||||
```
|
||||
|
||||
**Das ist viel intuitiver:** Kinder bauen etwas in der Welt und machen es dann "lebendig" -- kein Moduswechsel, kein separates Tool.
|
||||
|
||||
---
|
||||
|
||||
## Zusammenfassung: 10cm ist die bessere Wahl
|
||||
|
||||
| Aspekt | 0.25m + 0.0625m (alt) | 0.10m einheitlich (neu) |
|
||||
| ------------------- | ----------------------- | ------------------------------- |
|
||||
| Systeme | Zwei (Welt + Items) | Eines |
|
||||
| Editoren | Zwei Modi | Ein Editor, Zoom-basiert |
|
||||
| Mesh-Pipelines | Zwei | Eine |
|
||||
| Detail-Level | Welt: grob, Items: fein | Alles gleichmaessig detailliert |
|
||||
| Innenraeume | Nicht moeglich | Natuerlich (Moebel, Details) |
|
||||
| Welt-Groesse | 128m+ (gross, leer) | 32-64m (klein, dicht) |
|
||||
| Aesthetik | Minecraft-artig | MagicaVoxel-artig |
|
||||
| Komplexitaet (Code) | Hoeher | Niedriger |
|
||||
| Performance | Vergleichbar | Vergleichbar |
|
||||
| Spielgefuehl | Erkunden | Entdecken, Details finden |
|
||||
| MVP-Aufwand | Hoeher (zwei Systeme) | Niedriger (ein System) |
|
||||
|
||||
### Was sich im MVP aendert
|
||||
|
||||
```
|
||||
ENTFAELLT:
|
||||
✗ Separater Item-Editor
|
||||
✗ Dual-Resolution Voxel-System
|
||||
✗ Separate Item-Mesh-Pipeline
|
||||
✗ Wechsel zwischen Welt/Item-Modus
|
||||
|
||||
WIRD EINFACHER:
|
||||
✓ Ein Voxel-System fuer alles
|
||||
✓ Items = Voxel-Gruppen in der Welt
|
||||
✓ "Zoom to Edit" statt Moduswechsel
|
||||
✓ Import von MagicaVoxel-Dateien (.vox) 1:1
|
||||
|
||||
WIRD ANDERS:
|
||||
~ Welt-Groessen-Limit: Max 64m × 64m × 32m (statt "unbegrenzt")
|
||||
~ Welt-Templates: Zimmer, Dungeon, Arena, Insel (statt Flat World)
|
||||
~ Focus auf Dichte statt Weite
|
||||
```
|
||||
|
||||
### Die neue Vision in einem Satz
|
||||
|
||||
> **Eine Plattform, auf der Spieler detaillierte Miniaturwelten aus 10cm-Voxeln erschaffen, jedes Objekt darin zum Leben erwecken und mit Freunden teilen -- direkt im Browser, ohne Download, ohne externen Editor.**
|
||||
|
||||
---
|
||||
|
||||
_Erstellt: 28. Maerz 2026_
|
||||
394
NewAppIdeas/Roblox Reimagined/Resolution-Grenzen-Analyse.md
Normal file
394
NewAppIdeas/Roblox Reimagined/Resolution-Grenzen-Analyse.md
Normal file
|
|
@ -0,0 +1,394 @@
|
|||
# Voxel Resolution: Wie klein koennen wir gehen?
|
||||
|
||||
## Die physikalischen und praktischen Grenzen
|
||||
|
||||
---
|
||||
|
||||
## Uebersicht: Alle Resolutionen im Vergleich
|
||||
|
||||
```
|
||||
1.00m ██████████ Minecraft Grob, ikonisch
|
||||
0.50m █████ Trove LEGO-artig
|
||||
0.25m ███ Unser alter Plan Erkennbar
|
||||
0.10m █ Teardown, Plan B Detailliert, MagicaVoxel
|
||||
0.05m ▌ 5cm -- "HD Voxel" Sehr fein, fast smooth
|
||||
0.025m ▎ 2.5cm -- "Ultra" Skulptur-Qualitaet
|
||||
0.01m ▏ 1cm -- "Micro" Physisches Limit im Browser
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Die harte Grenze: Speicher
|
||||
|
||||
Alles haengt von einer Formel ab:
|
||||
|
||||
```
|
||||
Voxel-Count = Weltbreite/Resolution × Welttiefe/Resolution × Welthoehe/Resolution
|
||||
|
||||
Speicher (roh) = Voxel-Count × 2 Bytes (u16)
|
||||
Speicher (komprimiert) ≈ Speicher (roh) × 5-20% (je nach Inhalt)
|
||||
```
|
||||
|
||||
### Browser-Limits
|
||||
|
||||
```
|
||||
WASM Memory: Max 4 GB (theoretisch), praktisch 1-2 GB nutzbar
|
||||
GPU Memory (Desktop): 2-12 GB
|
||||
GPU Memory (Mobile): 0.5-2 GB (shared mit System)
|
||||
Netzwerk (Initial): Unter 50 MB fuer schnellen Start
|
||||
|
||||
Faustregel: Max ~200 Mio. Voxel roh, ~50 Mio. sichtbar gemesht
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Resolution × Weltgroesse Matrix
|
||||
|
||||
### 5cm Voxel (0.05m) -- "HD Voxel"
|
||||
|
||||
```
|
||||
20 Voxel pro Meter │ 8.000 Voxel pro m³
|
||||
```
|
||||
|
||||
| Welt-Groesse | Beschreibung | Chunks (32³) | Voxel | RAM roh | RAM kompr. | Machbar? |
|
||||
| ------------------- | -------------------- | --------------- | ----- | ------- | ---------- | ----------- |
|
||||
| **8m × 8m × 4m** | Ein Raum | 5×5×3 = 75 | 10M | 20 MB | 2-5 MB | Trivial |
|
||||
| **16m × 16m × 8m** | Wohnung/Dungeon-Flur | 10×10×5 = 500 | 82M | 160 MB | 15-40 MB | Gut |
|
||||
| **24m × 24m × 10m** | Grosses Level | 15×15×7 = 1.575 | 230M | 460 MB | 40-100 MB | Grenzwertig |
|
||||
| **32m × 32m × 12m** | Maximum | 20×20×8 = 3.200 | 490M | 980 MB | 80-200 MB | Nur Desktop |
|
||||
|
||||
**Detail bei 5cm:**
|
||||
|
||||
- Ein Mensch: 10 × 6 × 36 = **2.160 Voxel** -- Gesichtszuege andeutbar!
|
||||
- Schwert: 6 × 2 × 30 Voxel -- Gravuren auf der Klinge moeglich
|
||||
- Blume: 4 × 4 × 8 Voxel -- einzelne Blumenblaetter
|
||||
- Tuerklinke: 3 × 2 × 1 Voxel -- sichtbar und greifbar
|
||||
- Buch: 8 × 2 × 10 Voxel -- Seiten angedeutet, Titel auf Cover
|
||||
|
||||
**Sweet Spot: 16m × 16m × 8m** (eine Wohnung, ein Dungeon-Level, eine Arena). ~80 Mio. Voxel, komprimiert ~20 MB. Laeuft im Browser.
|
||||
|
||||
---
|
||||
|
||||
### 2.5cm Voxel (0.025m) -- "Ultra Detail"
|
||||
|
||||
```
|
||||
40 Voxel pro Meter │ 64.000 Voxel pro m³
|
||||
```
|
||||
|
||||
| Welt-Groesse | Beschreibung | Chunks (32³) | Voxel | RAM roh | RAM kompr. | Machbar? |
|
||||
| ------------------ | ----------------- | --------------- | ----- | ------- | ---------- | -------------- |
|
||||
| **4m × 4m × 3m** | Diorama / Vitrine | 5×5×4 = 100 | 31M | 62 MB | 6-15 MB | Gut |
|
||||
| **8m × 8m × 4m** | Ein Zimmer | 10×10×5 = 500 | 164M | 328 MB | 30-80 MB | Machbar |
|
||||
| **12m × 12m × 5m** | Groesserer Raum | 15×15×7 = 1.575 | 460M | 920 MB | 80-200 MB | Nur Desktop |
|
||||
| **16m × 16m × 6m** | Maximum | 20×20×8 = 3.200 | 980M | 1.96 GB | Zu gross | Nein (Browser) |
|
||||
|
||||
**Detail bei 2.5cm:**
|
||||
|
||||
- Ein Mensch: 20 × 12 × 72 = **17.280 Voxel** -- richtige Figurine!
|
||||
- Gesicht: Augen (2×1 Voxel), Mund (4×1), Nase (1×2×1) -- erkennbar
|
||||
- Schwert: 12 × 3 × 60 Voxel -- Muster auf der Klinge, umwickelter Griff
|
||||
- Uhr: 6 × 2 × 6 Voxel -- Zifferblatt mit Zeigern
|
||||
- Schluessel: 2 × 1 × 10 Voxel -- Bart-Muster sichtbar
|
||||
- Muenze: 3 × 3 × 1 Voxel -- rund angedeutet, Praegung moeglich
|
||||
|
||||
**Sweet Spot: 8m × 8m × 4m** (ein detailliertes Zimmer, ein Diorama). Das ist wie ein **digitaler Setzkasten** oder eine **interaktive Puppenstube**.
|
||||
|
||||
---
|
||||
|
||||
### 1cm Voxel (0.01m) -- "Micro Sculpting"
|
||||
|
||||
```
|
||||
100 Voxel pro Meter │ 1.000.000 Voxel pro m³
|
||||
```
|
||||
|
||||
| Welt-Groesse | Beschreibung | Chunks (32³) | Voxel | RAM roh | RAM kompr. | Machbar? |
|
||||
| ---------------------- | -------------------- | ---------------- | ----- | ------- | ---------- | ----------- |
|
||||
| **1.6m × 1.6m × 1.6m** | Ein einzelnes Objekt | 5×5×5 = 125 | 4M | 8 MB | 1-3 MB | Trivial |
|
||||
| **3.2m × 3.2m × 3.2m** | Schreibtisch-Szene | 10×10×10 = 1.000 | 33M | 66 MB | 6-15 MB | Gut |
|
||||
| **5m × 5m × 3m** | Kleines Diorama | 16×16×10 = 2.560 | 75M | 150 MB | 15-40 MB | Machbar |
|
||||
| **8m × 8m × 4m** | Maximum sinnvoll | 25×25×13 = 8.125 | 256M | 512 MB | 50-120 MB | Grenzwertig |
|
||||
|
||||
**Detail bei 1cm:**
|
||||
|
||||
- Ein Mensch: 50 × 30 × 180 = **270.000 Voxel** -- fast eine Statue
|
||||
- Auge: 5 × 3 Voxel -- Iris-Farbe, Pupille, Weisses
|
||||
- Hand: 8 × 5 × 12 Voxel -- einzelne Finger andeutbar
|
||||
- Schwert: 30 × 5 × 150 Voxel -- Gravuren, Juwelen im Griff, Lederbindung
|
||||
- Schachfigur: 3 × 3 × 8 Voxel -- Bauer erkennbar von Koenig
|
||||
- Insekt: 3 × 2 × 5 Voxel -- Beine, Fluegel, Fuehler
|
||||
|
||||
**Sweet Spot: 3.2m × 3.2m × 3.2m** -- ein Tisch voller Objekte, ein einzelnes kunstvolles Artefakt, ein Architektur-Detail. Das ist ein **Sculpting-Tool**, kein Game-World-Builder mehr.
|
||||
|
||||
---
|
||||
|
||||
## Alle Resolutionen auf einen Blick
|
||||
|
||||
```
|
||||
Max Welt (Browser) Detail-Beispiel
|
||||
Resolution Voxel/m Groesse Chunks "Schwert"
|
||||
─────────── ──────── ──────────── ────────── ──────────────────────
|
||||
1.00m 1 unbegrenzt unbegrenzt 1 × 1 × 2 (Strich)
|
||||
0.50m 2 512m tausende 1 × 1 × 3 (Stock)
|
||||
0.25m 4 256m tausende 2 × 1 × 7 (erkennbar)
|
||||
0.10m 10 64m ~4.000 3 × 1 × 15 (gut!)
|
||||
0.05m 20 24m ~1.500 6 × 2 × 30 (Gravuren)
|
||||
0.025m 40 8m ~500 12 × 3 × 60 (Meisterwerk)
|
||||
0.01m 100 3m ~1.000 30 × 5 × 150 (Kunstobjekt)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Die smarte Loesung: Multi-Resolution Zones
|
||||
|
||||
### Statt einer festen Resolution: Verschiedene Zonen in einer Welt
|
||||
|
||||
```
|
||||
┌──────────────────────────────────────────────┐
|
||||
│ WELT (32m × 32m × 16m) │
|
||||
│ Basis-Resolution: 10cm │
|
||||
│ │
|
||||
│ ┌──────────────────────┐ │
|
||||
│ │ Raum A │ │
|
||||
│ │ 10cm (Standard) │ │
|
||||
│ │ │ │
|
||||
│ │ ┌────────┐ │ │
|
||||
│ │ │Detail- │ │ │
|
||||
│ │ │Zone: │ │ ┌─────────────┐ │
|
||||
│ │ │5cm │ │ │ Raum B │ │
|
||||
│ │ │(Vitrine│ │ │ 10cm │ │
|
||||
│ │ │Altar, │ │ │ │ │
|
||||
│ │ │Werkb.) │ │ │ │ │
|
||||
│ │ └────────┘ │ └─────────────┘ │
|
||||
│ └──────────────────────┘ │
|
||||
│ │
|
||||
│ Aussenbereich: 20cm (weniger Detail noetig) │
|
||||
└──────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
**So funktioniert es:**
|
||||
|
||||
1. Die Welt hat eine **Basis-Resolution** (z.B. 10cm)
|
||||
2. Creator koennen **Detail-Zonen** definieren (z.B. 5cm oder 2.5cm)
|
||||
3. Detail-Zonen sind raeumlich begrenzt (z.B. 3m × 3m × 3m)
|
||||
4. Ausserhalb kann eine **Grob-Zone** gelten (z.B. 20cm fuer Landschaft)
|
||||
|
||||
**Technisch:**
|
||||
|
||||
- Jeder Chunk speichert seine Resolution als Metadatum
|
||||
- Chunks an Zone-Grenzen werden doppelt gehalten (fein + grob)
|
||||
- Das Greedy-Meshing passt sich pro Chunk an
|
||||
- Die Engine wechselt transparent zwischen Resolutionen
|
||||
|
||||
### Vorteile von Multi-Resolution
|
||||
|
||||
```
|
||||
Dungeon-Beispiel (32m × 32m × 16m):
|
||||
|
||||
Gaenge und grosse Raeume: 10cm
|
||||
→ 90% der Chunks, moderate Detail-Dichte
|
||||
|
||||
Schatzkammer (Detail-Zone): 5cm
|
||||
→ 5% der Chunks, doppeltes Detail
|
||||
→ Hier stehen kunstvolle Artefakte auf Sockeln
|
||||
|
||||
Bosskampf-Arena: 10cm
|
||||
→ Normale Resolution reicht fuer Kampf
|
||||
|
||||
Geheimraum mit Raetsel (Detail-Zone): 2.5cm
|
||||
→ 2% der Chunks, vierfaches Detail
|
||||
→ Kleine Mechanismen, Inschriften, Schluesselform erkennen
|
||||
|
||||
Ergebnis: 95% der Welt bei 10cm (performant)
|
||||
5% der Welt bei 5cm oder feiner (beeindruckend)
|
||||
→ Kaum mehr Speicher als reine 10cm-Welt
|
||||
```
|
||||
|
||||
### Detail-Zone als Creator-Tool
|
||||
|
||||
```
|
||||
Creator-Workflow:
|
||||
|
||||
1. Welt in 10cm bauen (Raeume, Gaenge, Terrain)
|
||||
2. "Detail-Zone erstellen" Tool waehlen
|
||||
3. Box aufziehen wo mehr Detail noetig ist (z.B. 3m × 3m × 3m)
|
||||
4. Resolution waehlen: [5cm ▼] oder [2.5cm ▼]
|
||||
5. In der Zone bauen: Feinere Voxel fuer Skulpturen, Mechanismen, Schrift
|
||||
|
||||
Regeln:
|
||||
├── Max 5 Detail-Zonen pro Welt (Performance-Budget)
|
||||
├── Max Volumen pro Zone: 4m × 4m × 4m
|
||||
├── Verfuegbare Resolutionen: 5cm, 2.5cm
|
||||
└── 1cm nur fuer "Item-Sculpting-Modus" (isoliert, nicht in Welt)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Item Sculpting: 1cm fuer Einzelobjekte
|
||||
|
||||
Auch wenn 1cm fuer ganze Welten zu teuer ist, kann man es fuer **einzelne Items** nutzen:
|
||||
|
||||
```
|
||||
Item-Sculpting-Modus:
|
||||
|
||||
┌──────────────────────────────────────┐
|
||||
│ SCULPTING-WERKSTATT │
|
||||
│ │
|
||||
│ Canvas: 64 × 64 × 64 bei 1cm │
|
||||
│ = 0.64m × 0.64m × 0.64m │
|
||||
│ = 262.144 Voxel │
|
||||
│ = ~500 KB (trivial!) │
|
||||
│ │
|
||||
│ Hier baut der Spieler: │
|
||||
│ - Detaillierte Waffen │
|
||||
│ - Schmuck mit Mustern │
|
||||
│ - Runen-Steine mit Inschriften │
|
||||
│ - Miniatur-Figuren │
|
||||
│ - Schluesse mit Bart-Detail │
|
||||
│ │
|
||||
│ Fertig → In Welt platzieren │
|
||||
│ (gerendert als einzelnes Mesh-Objekt)│
|
||||
└──────────────────────────────────────┘
|
||||
```
|
||||
|
||||
Das kostet fast nichts: 262K Voxel × 2 Bytes = 512 KB. Ein Item.
|
||||
|
||||
In der Welt wird das Item als **vorgerendertes Mesh** platziert -- es belastet das Chunk-System nicht. Wie ein 3D-Modell das man in die Szene stellt.
|
||||
|
||||
**Das ist de facto der separate Item-Editor zurueck** -- aber jetzt als optionaler "Sculpting-Modus" fuer Power-Creator, nicht als Pflicht fuer alle.
|
||||
|
||||
---
|
||||
|
||||
## Empfehlung: Dreistufiges System
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────┐
|
||||
│ │
|
||||
│ STUFE 1: Welt-Voxel (10cm) │
|
||||
│ ├── Standard fuer alles │
|
||||
│ ├── 32m × 32m × 16m max │
|
||||
│ ├── Bauen, Inneneinrichtung, Terrain │
|
||||
│ └── Items direkt in der Welt formen │
|
||||
│ │
|
||||
│ STUFE 2: Detail-Zonen (5cm oder 2.5cm) │
|
||||
│ ├── Vom Creator platzierte Bereiche │
|
||||
│ ├── Max 5 Zonen, je max 4m × 4m × 4m │
|
||||
│ ├── Fuer: Altaere, Vitrinen, Raetsel, Mechanismen │
|
||||
│ └── Nahtloser Uebergang zur 10cm-Welt │
|
||||
│ │
|
||||
│ STUFE 3: Sculpting-Modus (1cm, isoliert) │
|
||||
│ ├── Separater Modus fuer Einzelobjekte │
|
||||
│ ├── 64³ Grid (0.64m × 0.64m × 0.64m) │
|
||||
│ ├── Fuer: Kunstvolle Items, Artefakte, Trophäen │
|
||||
│ └── In Welt als Mesh platziert (kein Chunk-Overhead) │
|
||||
│ │
|
||||
└─────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
### Performance-Budget
|
||||
|
||||
```
|
||||
Voxel RAM Mesh-Dreiecke
|
||||
Welt (10cm, 500 Chunks) 50M ~30 MB ~1.5M
|
||||
Detail-Zonen (5×, 5cm) 5M ~10 MB ~200K
|
||||
Sculpted Items (20×) 5M ~10 MB ~100K (als Meshes)
|
||||
────────────────────────────────────────────────────────
|
||||
Total 60M ~50 MB ~1.8M
|
||||
|
||||
→ Locker im Browser machbar
|
||||
→ 60fps auf Mid-Range Desktop
|
||||
→ 30fps auf Mid-Range Mobile (mit LOD fuer ferne 10cm-Chunks)
|
||||
```
|
||||
|
||||
### Was der Spieler erlebt
|
||||
|
||||
```
|
||||
Spieler betritt Dungeon (10cm):
|
||||
→ Detaillierte Gaenge mit Fackeln, Rissen in Waenden, Moos
|
||||
|
||||
Spieler findet Schatzkammer (Detail-Zone, 5cm):
|
||||
→ Goldmuenzen mit erkennbarer Praegung
|
||||
→ Juwelen in Kronen mit einzelnen Facetten
|
||||
→ Inschriften auf einem Altar lesbar
|
||||
|
||||
Spieler findet legendaeres Schwert (Sculpted Item, 1cm):
|
||||
→ Klinge mit eingravierten Runen
|
||||
→ Griff mit Leder-Wicklung und Edelstein-Pommel
|
||||
→ Parierstange mit Drachenkopf-Motiv
|
||||
|
||||
Drei Resolutionen, ein nahtloses Erlebnis.
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Was sich im MVP aendert
|
||||
|
||||
### MVP (Monat 1-7): Nur Stufe 1
|
||||
|
||||
```
|
||||
✅ 10cm Welt-Voxel, einheitlich
|
||||
✅ Max 32m × 32m × 16m
|
||||
✅ Items = Voxel-Gruppen in der Welt
|
||||
✅ Kein separater Editor noetig
|
||||
```
|
||||
|
||||
### Post-MVP Phase A: Stufe 3 (Sculpting)
|
||||
|
||||
```
|
||||
✅ 1cm Sculpting-Modus (isoliert, 64³)
|
||||
✅ Items als vorgerenderte Meshes in der Welt
|
||||
✅ Relativ einfach zu implementieren (separates kleines Voxel-System)
|
||||
```
|
||||
|
||||
### Post-MVP Phase B: Stufe 2 (Detail-Zonen)
|
||||
|
||||
```
|
||||
✅ Detail-Zonen in der Welt (5cm, 2.5cm)
|
||||
✅ Multi-Resolution Chunk-System
|
||||
✅ Technisch anspruchsvoll (Chunk-Grenzen, LOD-Uebergaenge)
|
||||
✅ Aber gewaltiger visueller Impact
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Extrembeispiel: Was waere bei 1mm moeglich?
|
||||
|
||||
Nur der Vollstaendigkeit halber -- **nicht fuer die Plattform geplant**, aber faszinierend:
|
||||
|
||||
```
|
||||
0.001m (1mm): 1.000 Voxel pro Meter, 1 Milliarde pro m³
|
||||
|
||||
64³ bei 1mm = 6.4cm × 6.4cm × 6.4cm = ~500 KB
|
||||
→ Ein Ring mit Edelstein-Fassung
|
||||
→ Ein Uhrwerk mit einzelnen Zahnraedern
|
||||
→ Ein Insekt mit Gliederbeinen
|
||||
|
||||
Machbar als isoliertes Sculpting-Tool?
|
||||
→ 128³ = 4 Mio. Voxel = 8 MB → Ja, trivial!
|
||||
→ Aber: Editor-UX wird schwierig (Voxel zu klein zum Klicken)
|
||||
→ Braeuchte: Schicht-Editing, 2D-Schnittansicht, Auto-Smooth
|
||||
|
||||
Fazit: Als "Juwelier-Modus" denkbar, aber kein MVP-Feature.
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Zusammenfassung
|
||||
|
||||
```
|
||||
Resolution Welt-Groesse (Browser) Bestes fuer MVP?
|
||||
──────────── ──────────────────────── ──────────────────────── ─────
|
||||
0.10m 32-64m (Dungeon/Dorf) Hauptwelt, Bauen ✅ JA
|
||||
0.05m 16-24m (Haus/Arena) Detail-Zonen in der Welt Nach MVP
|
||||
0.025m 4-8m (Raum/Diorama) Kleine Detail-Zonen Nach MVP
|
||||
0.01m 0.6-3m (Einzelobjekte) Item-Sculpting Nach MVP
|
||||
```
|
||||
|
||||
**10cm bleibt die richtige Basis-Resolution fuer die Welt.**
|
||||
|
||||
Kleinere Resolutionen (5cm, 2.5cm, 1cm) kommen als **optionale Detail-Stufen** dazu -- entweder als markierte Zonen in der Welt oder als isolierter Sculpting-Modus fuer Einzelobjekte.
|
||||
|
||||
Das dreistufige System gibt Creatorn die Werkzeuge fuer jede Detailstufe, ohne die Basis-Performance zu gefaehrden.
|
||||
|
||||
---
|
||||
|
||||
_Erstellt: 28. Maerz 2026_
|
||||
490
NewAppIdeas/Roblox Reimagined/Roblox-Analyse.md
Normal file
490
NewAppIdeas/Roblox Reimagined/Roblox-Analyse.md
Normal file
|
|
@ -0,0 +1,490 @@
|
|||
# Roblox: Eine umfassende Analyse
|
||||
|
||||
## Plattform, Phaenomen, Kontroverse
|
||||
|
||||
---
|
||||
|
||||
## Inhaltsverzeichnis
|
||||
|
||||
1. [Was ist Roblox?](#1-was-ist-roblox)
|
||||
2. [Das geniale Konzept](#2-das-geniale-konzept)
|
||||
3. [Warum Kinder Roblox lieben](#3-warum-kinder-roblox-lieben)
|
||||
4. [Geschaeftszahlen](#4-geschaeftszahlen)
|
||||
5. [Kontroversen und Probleme](#5-kontroversen-und-probleme)
|
||||
6. [Feature-Historie: Vom Baukasten zum Metaverse](#6-feature-historie-vom-baukasten-zum-metaverse)
|
||||
7. [Vergleich mit Wettbewerbern](#7-vergleich-mit-wettbewerbern)
|
||||
8. [Fazit: Genie und Schatten](#8-fazit-genie-und-schatten)
|
||||
|
||||
---
|
||||
|
||||
## 1. Was ist Roblox?
|
||||
|
||||
Roblox ist kein Spiel. Es ist eine **Plattform** -- ein digitales Oekosystem, in dem Nutzer eigene Spiele (sogenannte "Experiences") erstellen, veroeffentlichen und spielen. Das Unternehmen selbst produziert keinen einzigen spielbaren Inhalt. Stattdessen stellt es die Infrastruktur bereit: eine Game-Engine, einen Marktplatz, ein Identitaetssystem und eine virtuelle Waehrung.
|
||||
|
||||
Man kann Roblox am besten als **"YouTube fuer Spiele"** verstehen: So wie YouTube keine eigenen Videos dreht, entwickelt Roblox keine eigenen Games. Millionen von Nutzern -- darunter viele Kinder und Jugendliche -- erstellen die Inhalte, und andere Millionen konsumieren sie.
|
||||
|
||||
Die Gruender **David Baszucki** und **Erik Cassel** begannen 2003-2004 mit der Entwicklung unter dem Namen **DynaBlocks**. Baszucki hatte zuvor "Interactive Physics" entwickelt, eine 2D-Physiksimulation von 1989. Die Vision war von Anfang an: eine Plattform, auf der jeder bauen und spielen kann. Am **1. September 2006** ging Roblox oeffentlich online.
|
||||
|
||||
---
|
||||
|
||||
## 2. Das geniale Konzept
|
||||
|
||||
### 2.1 Der doppelte Netzwerkeffekt
|
||||
|
||||
Roblox hat etwas geschafft, woran die meisten Plattformen scheitern: einen **sich selbst verstaerkenden Doppelkreislauf**.
|
||||
|
||||
- Mehr Spieler ziehen mehr Entwickler an (groesseres Publikum = mehr potenzielle Einnahmen)
|
||||
- Mehr Entwickler produzieren mehr Inhalte, was mehr Spieler anzieht
|
||||
|
||||
Dieses Schwungrad dreht sich seit fast 20 Jahren und beschleunigt sich stetig. 2025 gibt es **3,5 Millionen aktive Entwickler** in Roblox Studio und Millionen spielbarer Experiences.
|
||||
|
||||
### 2.2 Unendlicher Content bei null internen Produktionskosten
|
||||
|
||||
Waehrend traditionelle Spielefirmen hunderte Millionen in die Entwicklung eines einzelnen Titels investieren (GTA VI: geschaetzt $2 Mrd.), hat Roblox **null Content-Produktionskosten**. Die Community liefert pausenlos neues Material. Es gibt keinen "Content Drought" -- das Problem, das Spiele wie Destiny oder Fortnite zwischen Updates plagt, existiert nicht.
|
||||
|
||||
Die Genrevielfalt ist dabei enorm:
|
||||
|
||||
- **Roleplay:** Brookhaven RP (69 Milliarden Besuche -- das meistbesuchte Game aller Zeiten auf der Plattform)
|
||||
- **Adoption/Pet-Simulation:** Adopt Me (erstes Spiel mit 20+ Mrd. Besuchen)
|
||||
- **Simulatoren:** Grow a Garden (innerhalb von 3 Monaten in den Top 6 aller Zeiten)
|
||||
- **Fashion:** Dress to Impress
|
||||
- **Obbys** (Hindernisparcours), **Tycoons**, **Horror**, **FPS**, **Racing** und hunderte weitere Nischen
|
||||
|
||||
### 2.3 Niedrige Einstiegshuerde
|
||||
|
||||
**Roblox Studio** ist kostenlos, nutzt die relativ einfache Programmiersprache **Lua**, bietet Drag-and-Drop-Building, und Kinder ab 8 Jahren koennen damit anfangen, eigene Spiele zu bauen. Der Uebergang vom Spieler zum Schoepfer ist fliessend -- ein brillanter Designentscheid.
|
||||
|
||||
### 2.4 Die Metaverse-Realitaet
|
||||
|
||||
Waehrend Meta Milliarden in ein Metaverse versenkt hat, das niemand nutzt, hat Roblox **still und leise das funktionierendste Metaverse der Welt** aufgebaut: persistente soziale Raeume, eine eigene Wirtschaft, ein Identitaetssystem, virtuelle Events, Markenkooperationen und plattformuebergreifende Erreichbarkeit.
|
||||
|
||||
**Roblox vereinnahmt 4,5% aller globalen Consumer-Gaming-Ausgaben (ausserhalb Chinas) und 60% des Netto-Gaming-Wachstums seit 2021.**
|
||||
|
||||
---
|
||||
|
||||
## 3. Warum Kinder Roblox lieben
|
||||
|
||||
### 3.1 Psychologische Faktoren
|
||||
|
||||
**Autonomie und Kontrolle (Selbstbestimmungstheorie)**
|
||||
|
||||
Kinder haben in der realen Welt wenig Kontrolle. Roblox gibt ihnen Agency: Sie entscheiden, was sie spielen, was sie bauen, wie sie aussehen, mit wem sie interagieren. In einer Phase der Entwicklung, in der Autonomie ein Grundbeduerfnis ist, bietet die Plattform einen Raum totaler Selbstbestimmung.
|
||||
|
||||
**Soziale Zugehoerigkeit -- "Alle meine Freunde spielen das"**
|
||||
|
||||
Der mit Abstand meistgenannte Grund, warum Kinder Roblox spielen: ihre Freunde sind dort. Roblox funktioniert als **virtueller Schulhof** -- das digitale Aequivalent des Pausenhofs, der Uebernachtungsparty, des Einkaufszentrums. Kinder loggen sich nicht ein, um ein bestimmtes Spiel zu spielen. **Sie loggen sich ein, um ihre Freunde zu treffen.**
|
||||
|
||||
**Status und Anerkennung**
|
||||
|
||||
Roblox bietet eine alternative Statushierarchie. Kinder, die in der Schule "unsichtbar" sind, koennen auf der Plattform gefeierte Entwickler oder geschaetzte Spieler werden. Der Status manifestiert sich ueber:
|
||||
|
||||
- Avatar-Ausstattung (teure, seltene Items)
|
||||
- Spielerfolge und Skills
|
||||
- Kreative Leistungen (eigene Spiele)
|
||||
|
||||
Es gibt eine echte soziale Stigmatisierung: "Noobs" und "Bacon Hairs" (Standard-Avatare ohne gekaufte Items) werden ausgegrenzt. Fuer Kinder, deren digitale Identitaet Teil ihres Selbstverstaendnisses ist, schafft das enormen Kaufdruck.
|
||||
|
||||
**Variable Belohnungssysteme (Operante Konditionierung)**
|
||||
|
||||
Seltene Item-Drops, Gacha-aehnliche Mechaniken und unvorhersehbare Belohnungen aktivieren dieselben Dopamin-Kreislaeufe wie Spielautomaten. Die Unvorhersehbarkeit treibt die Bindung an die Plattform.
|
||||
|
||||
**Sammeln und Handeln**
|
||||
|
||||
"Limited"-Items erzeugen durch kuenstliche Verknappung Sekundaermaerkte. Kinder handeln wie Daytrader -- der Sammelinstinkt, verstaerkt durch Knappheit, ist ein maechtiger Engagement-Treiber.
|
||||
|
||||
**Imaginatives Spiel**
|
||||
|
||||
Roblox ermoeglicht Kindern, Rollen auszuprobieren, die sie in der Realitaet nicht einnehmen koennen. In Brookhaven spielen sie Familienleben, in Tycoon-Spielen sind sie Unternehmer, in Rollenspielen erkunden sie erwachsene Identitaeten. Das entspricht der entwicklungspsychologischen Funktion des Rollenspiels.
|
||||
|
||||
### 3.2 Was kleinen Kindern in Spielen wichtig ist
|
||||
|
||||
Basierend auf Entwicklungsforschung an Kindern im Alter von 6-12 Jahren:
|
||||
|
||||
| Beduerfnis | Wie Roblox es erfuellt |
|
||||
| ------------------------ | -------------------------------------------------------------- |
|
||||
| **Zugehoerigkeit** | Soziale Plattform, Freundeslisten, Gruppen, Party-System |
|
||||
| **Selbstausdruck** | Avatar-Customization, eigene Spiele erstellen |
|
||||
| **Sozialer Status** | Rare Items, Errungenschaften, Follower als Entwickler |
|
||||
| **Erfolgserlebnisse** | Obby-Completion, Level-Aufstiege, Sammlungen vervollstaendigen |
|
||||
| **Imaginatives Spiel** | Roleplay-Games, Simulatoren, offene Welten |
|
||||
| **Kompetenzentwicklung** | Vom Spieler zum Entwickler, Programmieren lernen |
|
||||
| **Peer-Einfluss** | "Alle spielen es" als sozialer Gravitationsfaktor |
|
||||
|
||||
### 3.3 Warum Eltern es erlauben
|
||||
|
||||
- **Kostenlos** (kein Kaufpreis)
|
||||
- **Wahrgenommen als kreativ/lehrreich** ("Mein Kind lernt programmieren")
|
||||
- Eingebaute Kinderschutzfunktionen (altersbasierte Accounts, Chat-Filter, Bildschirmzeitlimits)
|
||||
- Buntes, gewaltfreies Erscheinungsbild
|
||||
- Coding-Akademien (Tynker, CodaKid) bieten Roblox-Kurse an -- verstaerkt die Bildungswahrnehmung
|
||||
- Sozialer Druck der Kinder
|
||||
|
||||
Was Eltern unterschaetzen: den In-App-Kaufdruck, die Gacha-Mechaniken, die Exposition gegenueber unangemessenen nutzergenerierten Inhalten und die suchtfoerdernden Belohnungsschleifen.
|
||||
|
||||
---
|
||||
|
||||
## 4. Geschaeftszahlen
|
||||
|
||||
### 4.1 Umsatz (GAAP)
|
||||
|
||||
| Jahr | Umsatz | Wachstum YoY |
|
||||
| ---- | ---------- | ------------- |
|
||||
| 2018 | ~$335 Mio. | - |
|
||||
| 2019 | ~$435 Mio. | +30% |
|
||||
| 2020 | ~$924 Mio. | +112% (COVID) |
|
||||
| 2021 | $1,92 Mrd. | +108% |
|
||||
| 2022 | $2,23 Mrd. | +16% |
|
||||
| 2023 | $2,80 Mrd. | +26% |
|
||||
| 2024 | $3,60 Mrd. | +29% |
|
||||
| 2025 | $4,89 Mrd. | +36% |
|
||||
|
||||
**Wichtiger Hinweis:** Roblox unterscheidet zwischen "Bookings" (Barzahlungen fuer Robux) und GAAP-Umsatz (ueber die geschaetzte Nutzungsdauer verteilt). 2025 lagen die Bookings bei **~$6,76 Mrd.** (+55% YoY). Q4 2025 allein: $2,2 Mrd. Bookings (+63% YoY).
|
||||
|
||||
### 4.2 Profitabilitaet: Die rote Null
|
||||
|
||||
Roblox hat **noch nie Gewinn gemacht** (GAAP-Basis):
|
||||
|
||||
| Jahr | Nettoverlust |
|
||||
| ---- | ------------------ |
|
||||
| 2020 | ~-$253 Mio. |
|
||||
| 2021 | ~-$491 Mio. |
|
||||
| 2022 | ~-$934 Mio. |
|
||||
| 2023 | ~-$1,16 Mrd. |
|
||||
| 2024 | ~-$941 Mio. |
|
||||
| 2025 | ~-$2,41 Mrd. (TTM) |
|
||||
|
||||
Haupttreiber der Verluste: Aktienbasierte Verguetung, Infrastrukturkosten und Investitionen in Trust & Safety.
|
||||
|
||||
### 4.3 Boersengang und Marktkapitalisierung
|
||||
|
||||
- **10. Maerz 2021:** Direct Public Offering (DPO, kein klassischer IPO) an der NYSE unter dem Ticker RBLX
|
||||
- Referenzpreis: $45/Aktie, Eroeffnung bei $64,50, Schluss Tag 1: $69,50
|
||||
- Bewertung am ersten Tag: ~$38-45 Mrd.
|
||||
- Vorhergehende Privatrunde (Jan 2021): $29,5 Mrd.
|
||||
- **Allzeithoch:** ~$141,56 (September 2025), Marktkapitalisierung ~$80 Mrd.+
|
||||
- **Aktuell (Maerz 2026):** ~$52/Aktie, Marktkapitalisierung ~$38 Mrd.
|
||||
|
||||
### 4.4 Nutzerzahlen (DAU = Daily Active Users)
|
||||
|
||||
| Zeitraum | DAU |
|
||||
| -------- | ------------------------ |
|
||||
| 2018 | 12,0 Mio. |
|
||||
| 2019 | 17,6 Mio. |
|
||||
| 2020 | 32,6 Mio. |
|
||||
| 2021 | 45,5 Mio. (Durchschnitt) |
|
||||
| 2022 | 56,0 Mio. |
|
||||
| 2023 | 68,4 Mio. |
|
||||
| Q4 2024 | 85,3 Mio. |
|
||||
| Q1 2025 | 97,8 Mio. |
|
||||
| Q2 2025 | 111,8 Mio. |
|
||||
| Q3 2025 | 151,5 Mio. |
|
||||
| Q4 2025 | 144,0 Mio. |
|
||||
|
||||
- **MAU Ende 2025:** ~380 Millionen
|
||||
- **Registrierte Accounts gesamt:** 1,5 Milliarden+
|
||||
- **Durchschnittliche Session-Dauer:** ~2,8 Stunden/Tag (Q3 2025)
|
||||
- **Engagement-Stunden 2025:** 124 Milliarden (Gesamtjahr) -- mehr als Steam, PlayStation und Fortnite **zusammen**
|
||||
- **Zahlende Nutzer:** 36,7 Mio. monatlich einzigartige Zahler (Q4 2025)
|
||||
|
||||
### 4.5 Demografie
|
||||
|
||||
| Altersgruppe | Anteil (2025) |
|
||||
| ------------ | --------------------------------- |
|
||||
| Unter 13 | ~33-37% (~49,8 Mio.) |
|
||||
| 13-16 | ~20% |
|
||||
| 17-24 | ~25-30% (am schnellsten wachsend) |
|
||||
| 25+ | ~14-17% |
|
||||
|
||||
**Wichtiger Trend:** 44% der Nutzer sind 2025 aelter als 17 -- Roblox "altert" mit seiner Nutzerbasis und zieht gleichzeitig aeltere Nutzer an.
|
||||
|
||||
**Geografische Verteilung (2025):**
|
||||
|
||||
- APAC: ~29,5% (~43 Mio. DAU, groesste Region)
|
||||
- US/Kanada: ~28% (~24 Mio. DAU, aber 60%+ des Umsatzes)
|
||||
- Europa: ~23% (~34 Mio. DAU)
|
||||
- Rest der Welt: ~19,5%
|
||||
- Wachstumstreiber: Indien, Philippinen, Suedkorea
|
||||
|
||||
**Plattformen:** ~80% Mobile, ~17% PC, ~3% Konsole
|
||||
|
||||
### 4.6 Robux-Oekonomie
|
||||
|
||||
**Preise:**
|
||||
|
||||
| Paket | Preis (USD) |
|
||||
| ------------ | ----------- |
|
||||
| 400 Robux | $4,99 |
|
||||
| 800 Robux | $9,99 |
|
||||
| 1.700 Robux | $19,99 |
|
||||
| 4.500 Robux | $49,99 |
|
||||
| 10.000 Robux | $99,99 |
|
||||
|
||||
**Premium-Abonnements:**
|
||||
|
||||
| Tier | Preis/Monat | Robux/Monat |
|
||||
| ------------ | ----------- | ----------- |
|
||||
| Premium 450 | $5 | 450 |
|
||||
| Premium 1000 | $11 | 1.000 |
|
||||
| Premium 2200 | $20 | 2.200 |
|
||||
|
||||
### 4.7 Entwickler-Oekonomie
|
||||
|
||||
**Auszahlungen an Entwickler:**
|
||||
|
||||
| Jahr | Auszahlung |
|
||||
| ---- | ---------- |
|
||||
| 2019 | ~$110 Mio. |
|
||||
| 2020 | ~$329 Mio. |
|
||||
| 2021 | ~$539 Mio. |
|
||||
| 2022 | ~$624 Mio. |
|
||||
| 2023 | ~$740 Mio. |
|
||||
| 2024 | ~$923 Mio. |
|
||||
| 2025 | ~$1,5 Mrd. |
|
||||
|
||||
**Verteilung:**
|
||||
|
||||
- Top 10 Entwickler: durchschnittlich $38,5 Mio./Jahr
|
||||
- Top 100: durchschnittlich $7 Mio./Jahr
|
||||
- Top 1.000: durchschnittlich $980.000/Jahr
|
||||
- **Median** DevEx-Entwickler: $1.575 ueber 12 Monate
|
||||
- 100+ Entwickler verdienten 2025 ueber $1 Mio.
|
||||
- 35.500+ DevEx-qualifizierte Entwickler (Ende 2025)
|
||||
|
||||
Die extreme Ungleichverteilung ist auffaellig: Die Top-Entwickler verdienen Millionen, waehrend der Median-Entwickler weniger als $150/Monat macht.
|
||||
|
||||
---
|
||||
|
||||
## 5. Kontroversen und Probleme
|
||||
|
||||
### 5.1 Kinderschutz: Raubtiere auf dem Spielplatz
|
||||
|
||||
Roblox' groesstes strukturelles Problem ist die Sicherheit von Kindern auf einer Plattform, die von Kindern dominiert wird.
|
||||
|
||||
- **BBC-Investigation 2022:** Kinder ab 7 Jahren wurden sexuellen Inhalten ausgesetzt und von Erwachsenen kontaktiert
|
||||
- **"Condo Games":** Nutzerkreierte, sexuell explizite Spiele mit simulierten Sexakten tauchen trotz Moderation immer wieder unter verschleierten Namen auf -- ein permanentes Whack-a-Mole-Problem
|
||||
- **Grooming:** Private Chats und Messaging-Funktionen werden zur Kontaktaufnahme mit Minderjaehrigen missbraucht
|
||||
- **Hindenburg Research Report (Oktober 2024):** Der Short-Seller-Bericht nannte Roblox "einen X-Rated-Spielplatz, verkleidet als Kinderspiel" und zitierte interne Dokumente, die zeigen, dass das Unternehmen das Ausmass der Kinderschutzprobleme kannte
|
||||
|
||||
### 5.2 Ausbeuterische Monetarisierung
|
||||
|
||||
- **Robux als Verschleierungsschicht:** Wie Casino-Chips abstrahiert die virtuelle Waehrung den realen Geldwert -- Kinder verlieren das Gefuehl dafuer, was sie ausgeben
|
||||
- **Sozialer Kaufdruck:** Standard-Avatare werden stigmatisiert ("Noob", "Bacon Hair"), was Kinder unter massiven Kaufdruck setzt
|
||||
- **Limited Items:** Das System kuenstlicher Verknappung erzeugt spekulative Sekundaermaerkte -- Kinder handeln wie an einer Boerse
|
||||
- **Drittanbieter-Gluecksspielseiten:** Aehnlich dem CS:GO-Skin-Gambling-Skandal nutzen externe Websites Robux als Waehrung fuer Gluecksspiel
|
||||
- **Eltern-Schock:** Regelmaessige Berichte ueber unerwartet hohe Rechnungen von hunderten bis tausenden Dollar
|
||||
|
||||
### 5.3 Entwickler-Ausbeutung
|
||||
|
||||
Dies ist einer der strukturell problematischsten Aspekte:
|
||||
|
||||
- **Revenue Share:** Entwickler erhalten effektiv nur ~24,5% des ausgegebenen Robux-Werts. Zum Vergleich: Apple nimmt 30%, Steam 30%, Epic nur 12%. Roblox nimmt den hoechsten Anteil aller grossen Plattformen
|
||||
- **Kinderarbeit:** Das Geschaeftsmodell basiert substantiell auf Inhalten, die von Minderjaehrigen erstellt werden
|
||||
- **People Make Games (2022):** Zwei investigative YouTube-Dokumentationen zeigten, wie Kinder lange Stunden in die Spielentwicklung investieren und im Verhaeltnis zu Roblox' Gewinn verschwindend wenig verdienen
|
||||
- **DevEx-Huerden:** Mindestbetraege fuer Auszahlungen und die Conversion-Rate Robux zu USD reduzieren die effektiven Einnahmen weiter
|
||||
|
||||
Die fundamentale Spannung: Roblox hat eine Plattform geschaffen, auf der **Kinder gleichzeitig das Publikum, die Schoepfer und das Produkt** sind.
|
||||
|
||||
### 5.4 Moderationsprobleme
|
||||
|
||||
- KI-Moderation produziert sowohl **falsche Positive** (unschuldige Nutzer werden gebannt) als auch **falsche Negative** (schaedliche Inhalte bleiben unentdeckt)
|
||||
- Der Hindenburg-Bericht behauptete, interne Daten zeigten, dass die Moderation weit weniger effektiv war als oeffentlich dargestellt
|
||||
- Kinder entwickeln kodierte Sprache und Workarounds, um Chat-Filter zu umgehen
|
||||
- Grosse Creator erhalten angeblich bevorzugte Behandlung
|
||||
|
||||
### 5.5 Datenschutz und COPPA
|
||||
|
||||
- Fragen zur COPPA-Compliance angesichts der massiven Unter-13-Nutzerbasis
|
||||
- Hindenburg-Bericht behauptete, Roblox habe die Zahl der Unter-13-Nutzer systematisch zu niedrig angegeben -- mit Implikationen fuer COPPA und Werberegulierung
|
||||
- 2024: Mehrere US-Generalstaatsanwaelte und Kinderschutz-Organisationen forderten staerkere Aufsicht
|
||||
|
||||
### 5.6 Sucht
|
||||
|
||||
- **Designmuster:** Taegliche Login-Belohnungen, zeitlich begrenzte Events, Streaks, Benachrichtigungen, soziale Druckmechaniken
|
||||
- **Keine natuerlichen Stopppunkte** in einer offenen Plattform
|
||||
- **Durchschnittlich 2,8 Stunden/Tag** pro aktivem Nutzer -- bemerkenswert fuer eine von Kindern dominierte Plattform
|
||||
- Kinderpsychologen warnen vor Auswirkungen auf Entwicklung, Schlaf und Sozialverhalten
|
||||
- Elternkontrollen werden als unzureichend und leicht umgehbar kritisiert
|
||||
|
||||
### 5.7 Scams und Betrug
|
||||
|
||||
- **"Free Robux"-Scams** sind allgegenwaertig auf YouTube, Discord und Fake-Websites (Phishing, Malware, Account-Diebstahl)
|
||||
- In-Game-Betrug im Trading-System, gezielt auf juengere/naive Spieler
|
||||
- Cookie-Logging als persistenter Angriffsvektor fuer Account-Diebstahl
|
||||
- Florierender Schwarzmarkt fuer Accounts, Items und Robux ueber Discord und Drittanbieter-Seiten
|
||||
|
||||
### 5.8 Rechtliche Probleme
|
||||
|
||||
- Mehrere Sammelklagen: Ermoeglichung illegalen Kindergluecksspiels, Kinderschutzversagen
|
||||
- **Hindenburg Research Report (Oktober 2024):** Vorwuerfe aufgeblaehter Nutzerzahlen, heruntergespielte Kinderschutzrisiken, Wissen ueber Raubtier-Aktivitaeten. Die Aktie fiel deutlich. Roblox bestritt die Vorwuerfe
|
||||
- US-Senatsanhoerungen 2024: Roblox namentlich erwaehnt im Kontext von KOSA (Kids Online Safety Act)
|
||||
- Zunehmende regulatorische Pruefung in UK, EU und Australien
|
||||
|
||||
### 5.9 Der 73-Stunden-Ausfall
|
||||
|
||||
**28.-31. Oktober 2021:** Der laengste Ausfall in der Plattform-Geschichte. Ausgeloest durch einen Traffic-Anstieg einer Chipotle-Promotion in Kombination mit einem Bug in HashiCorp Consul. 73 Stunden Totalausfall. Geschaetzter Verlust: zweistelliger Millionenbetrag.
|
||||
|
||||
### 5.10 Zeitleiste der wichtigsten Vorfaelle
|
||||
|
||||
| Datum | Vorfall |
|
||||
| ------------ | ------------------------------------------------------------- |
|
||||
| Maerz 2021 | DPO, ~$45B Bewertung |
|
||||
| Oktober 2021 | 73-Stunden-Ausfall |
|
||||
| 2022 | People Make Games -- Kinderarbeit-Dokumentationen |
|
||||
| 2022 | BBC -- Raubtier-/Sexualcontent-Investigation |
|
||||
| 2023 | Gluecksspiel-Klagen eingereicht |
|
||||
| 2023-2024 | US-Senatsanhoerungen referenzieren Roblox |
|
||||
| Oktober 2024 | Hindenburg Research Short-Seller-Report |
|
||||
| 2024-2025 | Globaler regulatorischer Druck (US, UK, EU, Australien), KOSA |
|
||||
|
||||
---
|
||||
|
||||
## 6. Feature-Historie: Vom Baukasten zum Metaverse
|
||||
|
||||
### 2004-2006: Die Gruenderjahre
|
||||
|
||||
- **2003-2004:** Entwicklung beginnt als **DynaBlocks**
|
||||
- **2004:** Alpha/Beta-Tests. Umbenennung zu **Roblox** ("robots" + "blocks")
|
||||
- **Januar 2005:** roblox.com registriert
|
||||
- **1. September 2006:** Oeffentlicher Launch. Kern: blockbasiertes Physik-Building, einfache Avatare, Free-to-Play
|
||||
|
||||
### 2007-2010: Grundsteinlegung
|
||||
|
||||
- **2007:** **Builders Club** (erste Paid Membership mit taeglichem Robux). Doppeltes Waehrungssystem: **Robux** + **Tix** (Tickets, gratis verdienbar)
|
||||
- **2008:** ~1 Million Nutzer
|
||||
- **2010:** Erik Cassel erkrankt an Krebs (stirbt Februar 2013)
|
||||
|
||||
### 2011-2016: Wachstumsphase und Mobile-Revolution
|
||||
|
||||
- **2011:** ~10 Mio. registrierte Nutzer, verbesserte Terrain-Tools, Gruppensystem
|
||||
- **Dezember 2012:** **iOS-Launch** -- der Wendepunkt. Mobile macht Roblox fuer sehr junge Kinder zugaenglich (Eltern-Tablet)
|
||||
- **Februar 2013:** Erik Cassel stirbt. **DevEx-Programm** startet (Robux zu USD-Conversion) -- aus Hobby wird echte Oekonomie
|
||||
- **2014:** **Android-Launch**. Smooth Terrain. Filtering Enabled fuer Sicherheit
|
||||
- **November 2015:** **Xbox One-Launch** (erste Konsole). CSG (Constructive Solid Geometry) hinzugefuegt
|
||||
- **April 2016:** **Tix abgeschafft** (kontrovers), Robux wird einzige Waehrung. VR-Support (experimentell). 30+ Mio. MAU. **R15-Avatar** neben klassischem R6
|
||||
|
||||
### 2017-2019: Mainstream-Durchbruch
|
||||
|
||||
- **2017:** Major Roblox Studio-Verbesserungen, 50+ Mio. MAU, ShadowMap-Lighting, gesponserte Events
|
||||
- **Maerz 2018:** **Ready Player One**-Crosspromotion (Spielberg-Film). **Rthro (Anthro)-Avatare** (realistischere Koerperproportionen). Roblox Education-Initiative
|
||||
- **September 2019:** **Roblox Premium ersetzt Builders Club** (BC/TBC/OBC konsolidiert). MAU ueberschreitet 100 Mio. Collaborative Editing beginnt. "Future is Bright"-Lighting Phase 1-2
|
||||
|
||||
### 2020-2021: Die Pandemie-Explosion
|
||||
|
||||
- **2020 Q1-Q2:** COVID-Lockdowns katapultieren DAU von ~15 Mio. auf 30 Mio.+. Roblox wird de facto sozialer Raum fuer eingesperrte Kinder
|
||||
- **November 2020:** **Lil Nas X-Konzert** -- mehrtaegiges Event mit ~33 Millionen Besuchen. Beweis, dass Roblox als virtueller Event-Ort funktioniert
|
||||
- **10. Maerz 2021:** **DPO an der NYSE**, Eroeffnung $64,50/Aktie
|
||||
- **Mai 2021:** **Gucci Garden** -- virtuelle Modeerfahrung, eine Gucci-Tasche wurde fuer mehr als den realen Preis gehandelt
|
||||
- **2021:** **Spatial Voice** (altersverifizierter Voice-Chat), **Layered Clothing** angekuendigt, **Twenty One Pilots-Konzert**, **KSI Launch Party**
|
||||
- **Oktober 2021:** 73-Stunden-Ausfall
|
||||
|
||||
### 2022-2023: Reifephase
|
||||
|
||||
- **2022:** **Altersverifizierung** via Ausweis. **17+-Experiences** (begrenzt). **Layered Clothing** offiziell. PBR-Materialien. Text-Chat-Ueberarbeitung. **Roblox Community Fund** ($10 Mio.+). Marken-Events: Walmart Land, NIKELAND, Spotify Island, Samsung, Elton John-Konzert, Chipotle, Twice
|
||||
- **2023:** **Face-Tracking/Avatar-Animation** via Geraetekameras. **Generative AI-Tools** angekuendigt (KI-Code-Completion, Material/Textur-Generierung in Studio). **Immersive Ads** Launch. **17+-Rating** vollstaendig ausgerollt. **Subscriptions-Feature** fuer Entwickler. Realistische Avatare (Dynamic Heads, Facial Expressions). DAU 65 Mio.+
|
||||
- **Oktober 2023 (RDC):** AI Assistant, Material/Terrain-Generierung, Open Cloud APIs, erweitertes UGC-Programm
|
||||
|
||||
### 2024-2025: Die KI-Aera und Expansion
|
||||
|
||||
- **2024:** **Roblox Assistant** (KI-Coding/Building) breitere Beta. **Party-System** (gruppenuebegreidendes Beitreten). Commerce-Expansion (Limited UGC Items, UGC Body Parts). **Clip-basiertes Sharing**. Verbesserte KI-Moderation. **Roblox Connect** angekuendigt (Video-Calling mit Avatar Face-Tracking). **Shopify-Integration** angekuendigt. Screen-Space Reflections, verbesserte Partikel
|
||||
- **2025:** Fortgesetzte KI-Integration in Studio (Code-Generierung, Asset-Erstellung, NPC-Verhalten). Push Richtung breitere Demografie. 150+ Mio. DAU. Verbesserte Elternkontrollen. Potentieller PS5-Launch. Werbe-Plattform-Reifung. **Rewarded Video Ads** (April 2025, Partnership mit Google, 80%+ Completion-Rate)
|
||||
|
||||
### Marken-Kooperationen (Highlights)
|
||||
|
||||
| Jahr | Events |
|
||||
| ------- | ------------------------------------------------------------------------- |
|
||||
| 2018 | Ready Player One, Aquaman |
|
||||
| 2019 | Stranger Things, Star Wars, NFL |
|
||||
| 2020 | Lil Nas X, Ava Max |
|
||||
| 2021 | Gucci Garden, Twenty One Pilots, Vans World, KSI, Chipotle |
|
||||
| 2022 | Walmart, Nike, Spotify, Samsung, Elton John, Twice |
|
||||
| 2023 | Paris Hilton, Olivia Rodrigo, NFL, diverse Modemarken |
|
||||
| 2024-25 | Zahlreiche Brand-Integrationen, Musik-Events, Real-World-Commerce-Piloten |
|
||||
|
||||
### Grafik-Engine-Evolution
|
||||
|
||||
| Aera | Technologie |
|
||||
| --------- | ------------------------------------------------------------------ |
|
||||
| 2006-2013 | Basis-Part-Rendering, simple Beleuchtung |
|
||||
| 2014-2016 | Smooth Terrain, CSG, ShadowMap |
|
||||
| 2017-2019 | Future is Bright Phase 1-2, PBR (SurfaceAppearance) |
|
||||
| 2020-2022 | Phase 3, Atmosphaere/Skybox, Material-Varianten, Layered Clothing |
|
||||
| 2023-2025 | Screen-Space-Effekte, Dynamic Face Rendering, verbesserte Partikel |
|
||||
|
||||
### Safety-Features-Timeline
|
||||
|
||||
| Aera | Massnahmen |
|
||||
| --------- | -------------------------------------------------------------------------------------------------------- |
|
||||
| 2007-2012 | Basis-Chat-Filter, Moderationsteam |
|
||||
| 2013-2016 | Filtering Enabled (Server-autoritativ) |
|
||||
| 2017-2019 | KI-Moderation, Elternkontrollen, altersbasierte Privatsphaere |
|
||||
| 2020-2021 | Verbessertes Reporting, 2FA-Push |
|
||||
| 2022-2023 | Ausweis-Altersverifizierung, 17+-Content-Gating, Voice-Chat-KI-Moderation, Eltern-PIN |
|
||||
| 2024-2025 | Echtzeit-Sprach-Moderation-KI, verbessertes Eltern-Dashboard, 4 Content-Reifestufen, proaktive Erkennung |
|
||||
|
||||
---
|
||||
|
||||
## 7. Vergleich mit Wettbewerbern
|
||||
|
||||
| Dimension | Roblox | Minecraft | Fortnite Creative |
|
||||
| --------------------- | ------------------------------ | ------------------------------------ | ------------------------------ |
|
||||
| MAU | ~380 Mio. | ~170 Mio. | ~100 Mio. |
|
||||
| Creator Revenue Share | ~24,5% | Kein offizielles Auszahlungsprogramm | 5% |
|
||||
| Content | Millionen UGC-Experiences | Ein Sandbox + Mods | Custom Maps, begrenzter Umfang |
|
||||
| Zielgruppe | Juengste (50%+ unter 12) | Breiter | Aeltere Teenager |
|
||||
| Creator Tools | Kostenlos, Lua, Drag-and-Drop | Redstone/Java-Mods (schwieriger) | UEFN (steile Lernkurve) |
|
||||
| Geschaeftsmodell | Plattform (0% eigener Content) | Produktverkauf + Marketplace | Content-Inseln eines Spiels |
|
||||
|
||||
---
|
||||
|
||||
## 8. Fazit: Genie und Schatten
|
||||
|
||||
Roblox ist eines der bemerkenswertesten digitalen Produkte des 21. Jahrhunderts. Es hat ein **selbsterhaltendes Oekosystem** geschaffen, in dem:
|
||||
|
||||
- Kinder gleichzeitig Konsumenten, Produzenten und Produkt sind
|
||||
- Unendlicher Content bei null internen Produktionskosten entsteht
|
||||
- Ein Doppel-Netzwerkeffekt fuer exponentielles Wachstum sorgt
|
||||
- Eine virtuelle Wirtschaft reales Geld bewegt ($1,5 Mrd. an Entwickler in 2025)
|
||||
- Soziale Dynamiken (Zugehoerigkeit, Status, FOMO) die Bindung antreiben
|
||||
|
||||
Gleichzeitig ist Roblox ein **Mikrokosmos aller Probleme der Tech-Industrie**:
|
||||
|
||||
- Monetarisierungsdruck auf Minderjaehrige
|
||||
- Die Unmoeglichkeit, nutzergenerierte Inhalte bei Hunderten Millionen Nutzern sicher zu moderieren
|
||||
- Ein Geschaeftsmodell, das trotz Milliardenumsaetzen keinen Gewinn abwirft
|
||||
- Die ethische Spannung zwischen "Kinder lernen programmieren" und "Kinder produzieren kostenlosen Content fuer ein $38-Mrd.-Unternehmen"
|
||||
|
||||
Die Zahlen sind beeindruckend: 150+ Mio. taegliche Nutzer, $4,89 Mrd. Jahresumsatz, 124 Milliarden Engagement-Stunden -- mehr als Steam, PlayStation und Fortnite zusammen. Aber hinter jeder Zahl steht die Frage, ob eine Plattform, die von und fuer Kinder gebaut ist, gleichzeitig ein profitables Werbe- und Microtransaction-Geschaeft sein kann, ohne die Interessen ihrer juengsten Nutzer zu verraten.
|
||||
|
||||
Roblox hat das funktionierendste Metaverse der Welt gebaut. Die Frage ist, ob es auch das ethischste sein kann.
|
||||
|
||||
---
|
||||
|
||||
## Quellen
|
||||
|
||||
- [Roblox Revenue -- MacroTrends](https://www.macrotrends.net/stocks/charts/RBLX/roblox/revenue)
|
||||
- [Roblox Revenue -- Stock Analysis](https://stockanalysis.com/stocks/rblx/revenue/)
|
||||
- [Roblox Q4 & Full Year 2024 Financial Results](https://ir.roblox.com/news/news-details/2025/Roblox-Reports-Fourth-Quarter-and-Full-Year-2024-Financial-Results/)
|
||||
- [Roblox Q1 2025 Financial Results](https://ir.roblox.com/news/news-details/2025/Roblox-Reports-First-Quarter-2025-Financial-Results/)
|
||||
- [Roblox Statistics -- Business of Apps](https://www.businessofapps.com/data/roblox-statistics/)
|
||||
- [Roblox Users -- Backlinko](https://backlinko.com/roblox-users)
|
||||
- [Roblox Player Count -- Udonis](https://www.blog.udonis.co/mobile-marketing/mobile-games/roblox-player-count)
|
||||
- [Roblox Demographics -- Takeaway Reality](https://www.takeaway-reality.com/post/roblox-demographics-statistics)
|
||||
- [How Roblox Hit 111M DAU -- GrowthCurve](https://growthcurve.co/how-roblox-hit-111-million-dau)
|
||||
- [Roblox Annual Economic Impact Report](https://about.roblox.com/newsroom/2025/09/roblox-annual-economic-impact-report)
|
||||
- [Why Roblox Is Addictive for Kids -- Virtual Addiction Center](https://virtual-addiction.com/blog/why-roblox-is-so-addictive-for-kids/)
|
||||
- [Psychology Behind Roblox Appeal -- Alibaba Insights](https://www.alibaba.com/product-insights/why-do-kids-love-roblox-so-much-psychology-behind-its-appeal.html)
|
||||
- [Roblox Isn't a Game -- Psychology Today](https://www.psychologytoday.com/us/blog/video-game-health/202511/roblox-isnt-a-game)
|
||||
- [Parental Concerns in Children's Engagement with Roblox -- BMC Psychology](https://link.springer.com/article/10.1186/s40359-025-03802-w)
|
||||
- [Roblox: Child-Friendly Game Engineered for Addiction -- West Rike Back](https://westrikeback.com/roblox-the-child-friendly-game-engineered-for-addiction/)
|
||||
- [State of UGC Games 2026 -- Naavik](https://naavik.co/deep-dives/the-state-of-ugc-games-2026/)
|
||||
- [How Roblox Grows -- How They Grow](https://www.howtheygrow.co/p/how-roblox-grows)
|
||||
- [Roblox vs Minecraft vs Fortnite -- Critical Hit](https://www.criticalhit.net/gaming/roblox-vs-minecraft-vs-fortnite/)
|
||||
- [Roblox Community Features -- ScreenWise](https://screenwiseapp.com/guides/roblox-community-features-explained)
|
||||
- [Roblox Explained for Parents -- Raising Digital Citizens](https://raisingdigitalcitizens.com/blogs/articles/roblox-explained-for-parents)
|
||||
- [What Parents Need to Know About Roblox -- ESRB](https://www.esrb.org/blog/what-parents-need-to-know-about-roblox-2/)
|
||||
- [Impact of Playing Roblox on Problem-Solving -- Cognizance Journal](https://cognizancejournal.com/vol5issue10/V5I1021.pdf)
|
||||
- [Roblox Information for Parents -- AAP](https://www.aap.org/en/patient-care/media-and-children/center-of-excellence-on-social-media-and-youth-mental-health/)
|
||||
|
||||
---
|
||||
|
||||
_Recherche-Datum: 28. Maerz 2026_
|
||||
764
NewAppIdeas/Roblox Reimagined/Stack-Optionen-Manacore.md
Normal file
764
NewAppIdeas/Roblox Reimagined/Stack-Optionen-Manacore.md
Normal file
|
|
@ -0,0 +1,764 @@
|
|||
# Roblox Reimagined: Stack-Optionen fuer Manacore
|
||||
|
||||
## Welche Technologien koennten wir nutzen -- und was haben wir bereits?
|
||||
|
||||
---
|
||||
|
||||
## Inhaltsverzeichnis
|
||||
|
||||
1. [Was wir bereits haben](#1-was-wir-bereits-haben)
|
||||
2. [Drei moegliche Stacks](#2-drei-moegliche-stacks)
|
||||
3. [Warum nicht Go?](#3-warum-nicht-go)
|
||||
4. [Self-Hosting & Local-First](#4-self-hosting--local-first)
|
||||
5. [Konkrete Architektur-Empfehlung](#5-konkrete-architektur-empfehlung)
|
||||
6. [Was wir wiederverwenden koennen](#6-was-wir-wiederverwenden-koennen)
|
||||
7. [Build-Phasen](#7-build-phasen)
|
||||
|
||||
---
|
||||
|
||||
## 1. Was wir bereits haben
|
||||
|
||||
### Unser aktueller Tech-Stack (Zusammenfassung)
|
||||
|
||||
Wir betreiben bereits ein komplexes, polyglot, selbst-gehostetes Oekosystem:
|
||||
|
||||
| Schicht | Technologie | Services |
|
||||
| ---------------------- | ---------------------------------------- | -------------------------------------------------- |
|
||||
| **Sprachen** | Go, TypeScript (Bun), Python, Svelte | ~25 Services |
|
||||
| **Go Services (6)** | Go 1.23-1.25, pgx, goroutines | sync, search, crawler, gateway, notify, matrix-bot |
|
||||
| **Hono Services (5)** | Hono 4.7 + Bun, Drizzle ORM | auth, credits, user, subscriptions, analytics |
|
||||
| **Python AI (5+)** | FastAPI, MLX, Whisper, FLUX | llm, image-gen, stt, tts, voice-bot |
|
||||
| **Frontend (19 Apps)** | SvelteKit 2 + Svelte 5 + Tailwind 4 | Todo, Chat, Calendar, Photos, etc. |
|
||||
| **Mobile** | Expo 55 + React Native 0.83 + NativeWind | ManaCore, Traces |
|
||||
| **Local-First** | Dexie.js + mana-sync (Go WebSocket) | 19 Apps migriert |
|
||||
| **Datenbank** | PostgreSQL 16 | Alle Services |
|
||||
| **Cache** | Redis 7 | Rate Limiting, Sessions |
|
||||
| **Object Storage** | MinIO | S3-kompatibel, selbst-gehostet |
|
||||
| **Git/CI** | Forgejo 11 + Runner | Selbst-gehostet |
|
||||
| **Monitoring** | Grafana + VictoriaMetrics + Loki | Metriken, Logs, Alerts |
|
||||
| **Error Tracking** | GlitchTip | Selbst-gehostet (Sentry-Alternative) |
|
||||
| **Chat/Komm.** | Matrix Synapse + Element | Selbst-gehostet |
|
||||
| **Auth** | Better Auth + EdDSA JWT + JWKS | Zentral via mana-auth |
|
||||
| **Payments** | Stripe | Credits + Subscriptions |
|
||||
| **Hosting** | Mac Mini M4 + Docker + Cloudflare Tunnel | mana.how |
|
||||
| **Suche** | SearXNG + mana-search (Go) | Meta-Search |
|
||||
| **LLM** | Ollama + OpenRouter + Groq | Lokal + Cloud |
|
||||
|
||||
### Kern-Staerken, die wir mitbringen
|
||||
|
||||
1. **Local-First-Architektur ist gebaut und bewiesen** -- Dexie.js + mana-sync mit Field-Level LWW, 19 Apps migriert
|
||||
2. **Go-Expertise fuer High-Performance-Services** -- 6 produktive Go-Services
|
||||
3. **Self-Hosting-Infrastruktur existiert** -- Forgejo, MinIO, Matrix, Monitoring, alles auf Mac Mini
|
||||
4. **SvelteKit-Frontend-System ist ausgereift** -- 19 Web-Apps, geteilte Packages, PWA-Support
|
||||
5. **Auth-System ist zentral und robust** -- Better Auth + EdDSA + JWKS + SSO
|
||||
6. **Credit-/Abo-System existiert** -- Stripe-Integration, Gilden, Gift Codes
|
||||
7. **AI-Pipeline existiert** -- LLM, Image Gen, STT, TTS, alles lokal auf Apple Silicon
|
||||
|
||||
---
|
||||
|
||||
## 2. Drei moegliche Stacks
|
||||
|
||||
### Option A: "Pragmatisch" -- SvelteKit + Canvas/WebGL + Go Backend
|
||||
|
||||
**Philosophie:** Maximale Wiederverwendung, minimaler Neuaufbau. Kein eigener Game-Engine -- stattdessen 2D/2.5D-Experiences im Browser mit Canvas/WebGL.
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────┐
|
||||
│ Browser (SvelteKit + PWA) │
|
||||
│ ┌─────────────┐ ┌──────────────────────┐ │
|
||||
│ │ Game Canvas │ │ SvelteKit UI/Editor │ │
|
||||
│ │ PixiJS/Three │ │ (bekannter Stack) │ │
|
||||
│ └──────┬──────┘ └──────────┬───────────┘ │
|
||||
│ │ WebSocket │ REST/WS │
|
||||
└─────────┼─────────────────────┼──────────────┘
|
||||
│ │
|
||||
┌─────────▼─────────────────────▼──────────────┐
|
||||
│ mana-game-server (Go) │
|
||||
│ - Goroutine pro Game-Instanz │
|
||||
│ - Lua-Sandbox (GopherLua oder Luau) │
|
||||
│ - Physik: Box2D (2D) oder Rapier (WASM) │
|
||||
│ - State → PostgreSQL + Redis │
|
||||
└───────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
| Komponente | Technologie | Begruendung |
|
||||
| -------------- | ------------------------------------------ | ------------------------------------------ |
|
||||
| Frontend | SvelteKit + PixiJS (2D) oder Three.js (3D) | Bestehender Stack, PWA, SSR |
|
||||
| Game Rendering | PixiJS (2D) oder Three.js/WebGPU | Browser-nativ, kein Download |
|
||||
| Game Server | Go + goroutines | Haben wir, performant, 1 goroutine/Instanz |
|
||||
| User Scripting | GopherLua (Lua in Go) | Einfach, sandboxed, Roblox-aehnlich |
|
||||
| Physik | Box2D (2D) oder Rapier via WASM | Bewaehrt |
|
||||
| Daten | PostgreSQL + Redis + Dexie.js | Bestehend |
|
||||
| Assets | MinIO | Bestehend, S3-kompatibel |
|
||||
| Auth | mana-auth | Bestehend |
|
||||
| Economy | mana-credits + Stripe | Bestehend |
|
||||
| Sync | mana-sync (angepasst) | Bestehend |
|
||||
|
||||
**Vorteile:**
|
||||
|
||||
- 70-80% des Stacks existiert bereits
|
||||
- Team kennt alle Technologien
|
||||
- Schnellster Time-to-Prototype (~3-6 Monate)
|
||||
- Self-Hosted von Tag 1
|
||||
|
||||
**Nachteile:**
|
||||
|
||||
- 2D/2.5D limitiert die Vision (kein "echtes" 3D-Roblox)
|
||||
- GopherLua ist langsamer als native Luau
|
||||
- Skalierung auf tausende gleichzeitige Instanzen braucht Arbeit
|
||||
- Kein nativer Mobile-Client (nur PWA)
|
||||
|
||||
**Geeignet fuer:** Ein "2D Roblox" -- eher wie ein Scratch/Roblox-Hybrid fuer kreative Browser-Experiences. Denke an Habbo Hotel trifft Roblox.
|
||||
|
||||
---
|
||||
|
||||
### Option B: "Ambitioniert" -- Go Game Server + WebGPU Frontend + WASM Scripting
|
||||
|
||||
**Philosophie:** Volle 3D-Plattform, aber Go als Backend-Kern statt Rust. Browser-first via WebGPU.
|
||||
|
||||
```
|
||||
┌──────────────────────────────────────────────┐
|
||||
│ Browser (WebGPU + WASM) │
|
||||
│ ┌─────────────────┐ ┌───────────────────┐ │
|
||||
│ │ 3D Engine (WASM) │ │ Editor UI │ │
|
||||
│ │ Three.js/Babylon │ │ (Svelte) │ │
|
||||
│ │ oder Custom │ │ │ │
|
||||
│ └───────┬─────────┘ └────────┬──────────┘ │
|
||||
│ │ WebTransport │ REST │
|
||||
└──────────┼──────────────────────┼─────────────┘
|
||||
│ │
|
||||
┌──────────▼──────────────────────▼─────────────┐
|
||||
│ mana-game-server (Go) │
|
||||
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
|
||||
│ │ Instanz A│ │ Instanz B│ │ Instanz C│ │
|
||||
│ │ WASM VM │ │ WASM VM │ │ WASM VM │ │
|
||||
│ │(wazero) │ │(wazero) │ │(wazero) │ │
|
||||
│ └──────────┘ └──────────┘ └──────────┘ │
|
||||
│ Rapier Physik (WASM) │ ECS State Mgmt │
|
||||
│ PostgreSQL + Redis + mana-sync │
|
||||
└───────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
| Komponente | Technologie | Begruendung |
|
||||
| -------------- | -------------------------------------------- | ---------------------------------------------- |
|
||||
| 3D-Rendering | Three.js + WebGPU Renderer ODER Babylon.js 7 | Reif, WebGPU-faehig, grosse Community |
|
||||
| Editor UI | SvelteKit (eingebettet) | Bestehende Expertise |
|
||||
| Game Server | Go + wazero (WASM Runtime) | Go-Expertise, wazero ist reine Go-WASM-Runtime |
|
||||
| User Scripting | TypeScript -> WASM (via AssemblyScript) | Moderner als Lua, in wazero sandboxed |
|
||||
| Physik | Rapier (via WASM in wazero) | Deterministisch, Rust-basiert, WASM-fertig |
|
||||
| Networking | WebTransport (Go: quic-go) | QUIC-basiert, unreliable datagrams |
|
||||
| Daten | PostgreSQL + Redis + Dexie.js | Bestehend |
|
||||
| Assets | MinIO + CDN-Layer | Bestehend |
|
||||
| Auth | mana-auth | Bestehend |
|
||||
| Economy | mana-credits (erweitert) | Bestehend, mit Double-Entry-Erweiterung |
|
||||
| AI NPCs | mana-llm (Ollama) | Bestehend |
|
||||
| AI Assets | mana-image-gen (FLUX) + 3D-Pipeline | Bestehend + Erweiterung |
|
||||
| Moderation | Custom ML + bestehende Filter | Neu + bestehende Infra |
|
||||
|
||||
**Schluessel-Technologie: wazero**
|
||||
|
||||
[wazero](https://wazero.io) ist eine **reine Go-WASM-Runtime** (null CGo-Abhaengigkeiten):
|
||||
|
||||
- Fuehrt WASM-Module in Go-Prozessen aus
|
||||
- Sandboxed: User-Code kann nicht aus dem WASM-Memory ausbrechen
|
||||
- Fuel Metering moeglich (Instruction Limits)
|
||||
- Laeuft ueberall wo Go laeuft -- inklusive Mac Mini M4
|
||||
- ~50-70% der Performance von Wasmtime (Rust), aber deutlich besser als Lua-Interpreter
|
||||
|
||||
**Vorteile:**
|
||||
|
||||
- Go-Kern -- Team kennt die Sprache, keine neue Sprache lernen
|
||||
- WASM-Scripting ist zukunftssicher und sicherer als Lua
|
||||
- WebGPU/Three.js fuer 3D im Browser
|
||||
- 50-60% bestehende Infrastruktur wiederverwendbar
|
||||
- Self-Hosted moeglich
|
||||
- quic-go existiert als reife Go-QUIC-Library
|
||||
|
||||
**Nachteile:**
|
||||
|
||||
- Three.js/Babylon.js sind keine "echten" Game Engines -- Physik, Networking, ECS muss custom gebaut werden
|
||||
- wazero ist langsamer als Wasmtime (kein JIT, nur Interpreter + AOT)
|
||||
- Go's GC kann bei Game-Server-Tick-Rates (60Hz) stoeren
|
||||
- Team braucht 3D-/Game-Development-Expertise
|
||||
- 12-18 Monate bis spielbarer Prototyp
|
||||
|
||||
**Geeignet fuer:** Eine ernsthafte 3D-UGC-Plattform mit Browser-First-Ansatz, die auf bestehendem Go-Wissen aufbaut.
|
||||
|
||||
---
|
||||
|
||||
### Option C: "State of the Art" -- Rust Engine + Go Services + Svelte Editor
|
||||
|
||||
**Philosophie:** Bestes Tool fuer jeden Job. Rust fuer Engine/Performance-kritisches, Go fuer Backend-Services, Svelte fuer UI. Maximale technische Qualitaet.
|
||||
|
||||
```
|
||||
┌───────────────────────────────────────────────┐
|
||||
│ Browser │
|
||||
│ ┌──────────────────┐ ┌───────────────────┐ │
|
||||
│ │ Bevy Engine (WASM)│ │ Svelte Editor UI │ │
|
||||
│ │ wgpu → WebGPU │ │ (bekannter Stack) │ │
|
||||
│ │ Rapier Physik │ │ │ │
|
||||
│ │ WASM User Scripts │ │ │ │
|
||||
│ └────────┬─────────┘ └───────┬───────────┘ │
|
||||
│ │ WebTransport │ REST │
|
||||
└───────────┼─────────────────────┼──────────────┘
|
||||
│ │
|
||||
┌───────────▼─────────────────────▼──────────────┐
|
||||
│ Game Server (Rust/Bevy headless) │
|
||||
│ - ECS mit Bevy │
|
||||
│ - WASM Sandbox (Wasmtime) │
|
||||
│ - Rapier Physik │
|
||||
│ - WebTransport via Quinn │
|
||||
│ - Orchestriert von Go-Service │
|
||||
└──────────────────┬─────────────────────────────┘
|
||||
│
|
||||
┌──────────────────▼─────────────────────────────┐
|
||||
│ Backend (Go -- bestehender Stack) │
|
||||
│ - mana-auth (Hono/Bun) │
|
||||
│ - mana-credits (Hono/Bun) │
|
||||
│ - mana-game-orchestrator (Go, NEU) │
|
||||
│ - mana-sync (Go) │
|
||||
│ - mana-matchmaker (Go, NEU) │
|
||||
│ - PostgreSQL + Redis + MinIO │
|
||||
└────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
| Komponente | Technologie | Begruendung |
|
||||
| ---------------- | ----------------------------------------- | ---------------------------------- |
|
||||
| Game Engine | Bevy (Rust) + wgpu | ECS, WebGPU-nativ, WASM-Output |
|
||||
| Client Rendering | wgpu → WebGPU | Hardware-nah, Cross-Platform |
|
||||
| Editor UI | Svelte 5 (eingebettet via Web Components) | Bestehende Expertise |
|
||||
| Game Server | Bevy headless (Rust) | Gleicher Engine-Code wie Client |
|
||||
| User Scripting | WASM (Wasmtime) + TypeScript | Maximum Security + Performance |
|
||||
| Physik | Rapier (Rust, nativ) | Deterministisch, schnell |
|
||||
| Networking | Quinn (Rust QUIC) | WebTransport, unreliable datagrams |
|
||||
| Orchestrierung | Go Service (mana-game-orchestrator) | Go-Expertise fuer Infrastruktur |
|
||||
| Matchmaking | Go Service (mana-matchmaker) | Go-Expertise |
|
||||
| Backend APIs | Go + Hono (bestehend) | Bestehend |
|
||||
| Economy | TigerBeetle + mana-credits | Double-Entry, 1M+ TPS |
|
||||
| Daten | PostgreSQL + Redis + Dexie.js | Bestehend |
|
||||
| Assets | MinIO + Cloudflare R2 | Self-Hosted + CDN |
|
||||
| AI | Bestehende Python-Services | LLM, Image Gen, STT, TTS |
|
||||
|
||||
**Vorteile:**
|
||||
|
||||
- Technisch ueberlegener Stack (Rust-Performance, ECS, native WASM)
|
||||
- Gleicher Engine-Code auf Client und Server (Bevy)
|
||||
- Maximum Security durch WASM + Rust
|
||||
- Langfristig die beste Architektur
|
||||
- Go fuer Infrastruktur/Orchestrierung (Staerke)
|
||||
- Bestehende Backend-Services wiederverwendbar
|
||||
|
||||
**Nachteile:**
|
||||
|
||||
- **Rust muss gelernt werden** -- signifikante Lernkurve (6-12 Monate)
|
||||
- Bevy ist pre-1.0, API-Churn moeglich
|
||||
- Laengster Time-to-Prototype (18-24 Monate)
|
||||
- Zwei Systemsprachen (Rust + Go) erhoehen Komplexitaet
|
||||
- Weniger Self-Hosting-freundlich (Rust-Compilation braucht mehr Build-Infra)
|
||||
|
||||
**Geeignet fuer:** Den langfristigen Bau einer technisch erstklassigen UGC-Plattform, wenn das Team bereit ist, in Rust zu investieren.
|
||||
|
||||
---
|
||||
|
||||
### Vergleich der drei Optionen
|
||||
|
||||
| Kriterium | A: Pragmatisch | B: Ambitioniert | C: State of Art |
|
||||
| --------------------------- | ------------------ | ---------------------- | --------------------------- |
|
||||
| **Time-to-Prototype** | 3-6 Monate | 12-18 Monate | 18-24 Monate |
|
||||
| **Wiederverwendung** | 70-80% | 50-60% | 40-50% |
|
||||
| **Neue Sprachen** | Keine | Keine (+ WASM-Tooling) | Rust |
|
||||
| **3D-Faehigkeit** | 2D/2.5D | Volles 3D | Volles 3D (beste Qualitaet) |
|
||||
| **Performance** | Gut | Sehr gut | Exzellent |
|
||||
| **Self-Hosting** | Trivial | Machbar | Aufwaendiger |
|
||||
| **Skalierung** | Hunderte Instanzen | Tausende Instanzen | Zehntausende Instanzen |
|
||||
| **Team-Fit** | Perfekt | Gut (neues 3D-Wissen) | Herausfordernd (Rust) |
|
||||
| **Max Spieler/Instanz** | ~20-50 | ~100-200 | ~200-500 |
|
||||
| **Langfristiges Potenzial** | Begrenzt | Hoch | Sehr hoch |
|
||||
| **Scripting** | Lua (GopherLua) | WASM (wazero) | WASM (Wasmtime) |
|
||||
| **Risiko** | Niedrig | Mittel | Hoch |
|
||||
|
||||
---
|
||||
|
||||
## 3. Warum nicht Go?
|
||||
|
||||
Go ist eine exzellente Sprache -- wir nutzen sie bereits fuer 6 Produktionsservices. Aber fuer einen **Game Engine** hat Go spezifische Limitierungen:
|
||||
|
||||
### Go's Staerken (warum wir es bereits nutzen)
|
||||
|
||||
- **Goroutines:** Perfekt fuer I/O-bound Services (WebSocket-Handling, API-Gateway, Crawler)
|
||||
- **Einfache Deployment:** Einzelnes Binary, Cross-Compilation
|
||||
- **Schnelle Kompilierung:** Sekunden statt Minuten
|
||||
- **Gute Standard-Library:** HTTP, JSON, Crypto, Testing
|
||||
- **Garbage Collector:** Fuer API-Services kein Problem (Pausen <1ms seit Go 1.19)
|
||||
|
||||
### Go's Schwaechen fuer Game Engines
|
||||
|
||||
**1. Garbage Collector bei 60Hz Game Loop**
|
||||
|
||||
Ein Game-Server-Tick laeuft idealerweise alle 16.6ms (60Hz). Go's GC ist exzellent fuer API-Services, aber fuer Game Loops:
|
||||
|
||||
- GC-Pausen sind zwar kurz (<1ms), aber nicht deterministisch
|
||||
- Bei 240Hz-Physik wie Roblox (4.16ms pro Tick) wird jede Pause spuerbar
|
||||
- Object Pooling und Arena Allocation helfen, kaempfen aber gegen Go's Speichermodell
|
||||
|
||||
**2. Kein SIMD / Low-Level-Kontrolle**
|
||||
|
||||
- Go abstrahiert Hardware-Details bewusst weg
|
||||
- Physik-Engines brauchen SIMD (Single Instruction Multiple Data) fuer Matrix/Vektor-Operationen
|
||||
- In Go gibt es kein `unsafe` SIMD -- man muss auf CGo oder Assembly zurueckgreifen
|
||||
- Rust/C++ haben nativen SIMD-Support
|
||||
|
||||
**3. CGo-Overhead**
|
||||
|
||||
Wenn wir Performance-kritische Libraries einbinden (Physik, Rendering):
|
||||
|
||||
- CGo-Aufrufe kosten ~100-200ns pro Call (vs. ~1ns fuer native Funktionsaufrufe)
|
||||
- Bei tausenden Physics-Calls pro Frame summiert sich das
|
||||
- wazero umgeht CGo, ist aber langsamer als native WASM-Runtimes (Wasmtime)
|
||||
|
||||
**4. Kein WebGPU/WASM-Output**
|
||||
|
||||
- Go kompiliert nicht zu WebAssembly mit WebGPU-Zugang (TinyGo kann WASM, aber ohne GPU)
|
||||
- Ein Browser-Client muesste in einer anderen Sprache geschrieben werden
|
||||
- Es gibt keine Go-Game-Engines fuer den Browser
|
||||
|
||||
### Wo Go trotzdem glaenzt
|
||||
|
||||
Go ist perfekt fuer alles **ausser dem Game-Engine-Kern**:
|
||||
|
||||
| Go verwenden fuer | Nicht Go verwenden fuer |
|
||||
| -------------------------- | ------------------------------- |
|
||||
| Game-Server-Orchestrierung | Game-Engine-Rendering |
|
||||
| Matchmaking-Service | Physik-Simulation (bei >60Hz) |
|
||||
| API-Gateway | WASM-Scripting-Runtime (Client) |
|
||||
| Asset-Pipeline/CDN | Client-Side Game Code |
|
||||
| Moderation-Service | ECS-Tick-Loop |
|
||||
| Economy-Backend | |
|
||||
| WebSocket-Relay/Proxy | |
|
||||
| Monitoring/Metriken | |
|
||||
|
||||
### Empfehlung
|
||||
|
||||
**Go als Backend-Infrastruktur, nicht als Game-Engine.** Das ist genau wie bei Option B: Go orchestriert die Game-Server, managed Matchmaking, Assets, Economy -- aber der Game-Loop selbst laeuft in einer optimierteren Runtime (WASM/Rust).
|
||||
|
||||
---
|
||||
|
||||
## 4. Self-Hosting & Local-First
|
||||
|
||||
### Was wir bereits selbst hosten
|
||||
|
||||
Unser Selbst-Hosting-Stack ist beeindruckend umfangreich:
|
||||
|
||||
| Dienst | Technologie | Ersetzt |
|
||||
| ---------------------- | -------------------------------- | ----------------------- |
|
||||
| **Git & CI/CD** | Forgejo 11 + Runner | GitHub + GitHub Actions |
|
||||
| **Object Storage** | MinIO | AWS S3 |
|
||||
| **Chat/Kommunikation** | Matrix Synapse + Element | Slack/Discord |
|
||||
| **Monitoring** | Grafana + VictoriaMetrics + Loki | Datadog |
|
||||
| **Error Tracking** | GlitchTip | Sentry |
|
||||
| **Metriken** | Prometheus + Pushgateway | CloudWatch |
|
||||
| **Alerts** | AlertManager + VMALert | PagerDuty |
|
||||
| **Meta-Suche** | SearXNG | Google API |
|
||||
| **LLM** | Ollama (lokal) | OpenAI API |
|
||||
| **Image Gen** | FLUX auf Apple Silicon | DALL-E/Midjourney |
|
||||
| **STT** | Whisper MLX (lokal) | Cloud STT |
|
||||
| **TTS** | Kokoro + Piper (lokal) | Cloud TTS |
|
||||
| **Datenbank** | PostgreSQL 16 | Supabase/RDS |
|
||||
| **Cache** | Redis 7 | ElastiCache |
|
||||
| **Alle 25+ Services** | Docker auf Mac Mini | Diverse Cloud-Services |
|
||||
|
||||
### Was fuer "Roblox Reimagined" zusaetzlich self-hosted werden muesste
|
||||
|
||||
#### Game-Server-Hosting
|
||||
|
||||
**Challenge:** Roblox betreibt 135.000 Server. Wir haben einen Mac Mini.
|
||||
|
||||
**Realistischer Self-Hosting-Ansatz:**
|
||||
|
||||
```
|
||||
Mac Mini M4 (16 GB) -- Phase 1
|
||||
├── 10-50 gleichzeitige Game-Instanzen (je ~100-200 MB)
|
||||
├── Go-Orchestrator spawnt/killt Instanzen
|
||||
├── ~200-500 gleichzeitige Spieler
|
||||
└── Reicht fuer Alpha/Beta mit Community
|
||||
|
||||
Mac Mini M4 Pro (48 GB) -- Phase 2
|
||||
├── 50-200 gleichzeitige Game-Instanzen
|
||||
├── ~1.000-2.000 gleichzeitige Spieler
|
||||
└── Reicht fuer fruehe Produktion
|
||||
|
||||
Mac Mini Cluster (3x M4 Pro) -- Phase 3
|
||||
├── 200-600 gleichzeitige Game-Instanzen
|
||||
├── ~3.000-5.000 gleichzeitige Spieler
|
||||
└── Serious Self-Hosting
|
||||
```
|
||||
|
||||
**Kostenschaetzung (Cloud-Vergleich):**
|
||||
|
||||
| Ansatz | Kosten/Monat | Kapazitaet |
|
||||
| ------------------------------- | ---------------------------- | ----------------- |
|
||||
| Mac Mini M4 (bereits vorhanden) | ~$10 Strom | 50 Instanzen |
|
||||
| 3x Mac Mini M4 Pro | ~$30 Strom + $3.600 einmalig | 600 Instanzen |
|
||||
| AWS/Fly.io (vergleichbar) | ~$500-2.000/Monat | 200-600 Instanzen |
|
||||
|
||||
Self-Hosting spart massiv -- aber skaliert nicht unbegrenzt. **Hybrid-Modell** empfohlen: Mac Mini fuer Basis-Last, Cloud-Burst fuer Peaks.
|
||||
|
||||
#### Asset-CDN
|
||||
|
||||
MinIO ist perfekt fuer Storage, aber nicht fuer globale Auslieferung.
|
||||
|
||||
**Loesung:**
|
||||
|
||||
- MinIO als Origin
|
||||
- Cloudflare R2 als CDN-Mirror (zero egress, kostenlos bis 10 GB/Monat)
|
||||
- Oder: Caddy/Nginx Reverse Proxy mit aggressivem Caching
|
||||
|
||||
#### TigerBeetle (Economy)
|
||||
|
||||
TigerBeetle ist self-hostable -- ein einzelnes Binary:
|
||||
|
||||
- Laeuft auf Linux/macOS
|
||||
- ~100 MB RAM fuer Millionen Accounts
|
||||
- Replication ueber VSR (Viewstamped Replication) fuer Hochverfuegbarkeit
|
||||
- Koennte direkt auf dem Mac Mini laufen
|
||||
|
||||
### Local-First fuer Game-Experiences?
|
||||
|
||||
Unsere Local-First-Architektur (Dexie.js + mana-sync) koennte **angepasst** werden:
|
||||
|
||||
#### Was Local-First im Game-Kontext bedeutet
|
||||
|
||||
```
|
||||
Spieler im Browser
|
||||
├── Dexie.js (IndexedDB)
|
||||
│ ├── Eigene erstellte Games (Offline-Editor!)
|
||||
│ ├── Avatar/Inventar/Einstellungen
|
||||
│ ├── Spielfortschritt (je Game)
|
||||
│ └── Asset-Cache (Meshes, Texturen)
|
||||
│
|
||||
├── Bei Online-Verbindung:
|
||||
│ ├── Sync eigener Games → mana-sync → PostgreSQL
|
||||
│ ├── Sync Spielfortschritt
|
||||
│ ├── Live-Multiplayer → Game-Server
|
||||
│ └── Asset-Streaming ← MinIO/CDN
|
||||
│
|
||||
└── Bei Offline:
|
||||
├── Eigene Games editieren (vollstaendig!)
|
||||
├── Singleplayer spielen (lokal)
|
||||
├── Inventar/Avatar aendern
|
||||
└── Beim naechsten Online: Sync
|
||||
```
|
||||
|
||||
#### Konkrete Anwendungsfaelle
|
||||
|
||||
**1. Offline Game-Editor (starker Differentiator!)**
|
||||
|
||||
- Creator baut Game offline im Browser (Dexie.js speichert alles lokal)
|
||||
- Bei Verbindung: Sync via mana-sync zum Server
|
||||
- Andere sehen das Game sobald synchronisiert
|
||||
- **Unser bestehender Local-First-Stack macht das moeglich!**
|
||||
|
||||
**2. Offline Singleplayer**
|
||||
|
||||
- Game-Logik laeuft als WASM im Browser
|
||||
- Keine Server-Verbindung noetig fuer Singleplayer
|
||||
- Spielfortschritt in Dexie.js, sync spaeter
|
||||
|
||||
**3. Guest Mode (bereits implementiert!)**
|
||||
|
||||
- Gast spielt ohne Account → alles in IndexedDB
|
||||
- Erstellt Account → IndexedDB-Daten werden synchronisiert
|
||||
- Exakt unser bestehendes Pattern
|
||||
|
||||
**4. Asset-Caching**
|
||||
|
||||
- 3D-Modelle und Texturen in IndexedDB zwischenspeichern
|
||||
- Beim zweiten Besuch: sofortiges Laden aus Cache
|
||||
- Roblox macht das mit ihrem nativen Client -- wir koennten es im Browser
|
||||
|
||||
### Self-Hosted Stack fuer "Roblox Reimagined"
|
||||
|
||||
| Dienst | Technologie | Self-Hosted? | Fallback |
|
||||
| ------------------ | -------------------------- | ------------ | ------------- |
|
||||
| Game Server | Go/Rust auf Mac Mini | Ja | Fly.io Burst |
|
||||
| Game Orchestrator | Go (custom) | Ja | - |
|
||||
| Matchmaking | Go (custom) | Ja | - |
|
||||
| Auth | mana-auth (besteht) | Ja | - |
|
||||
| Economy | TigerBeetle + mana-credits | Ja | - |
|
||||
| User Data | PostgreSQL (besteht) | Ja | - |
|
||||
| Cache | Redis (besteht) | Ja | - |
|
||||
| Asset Storage | MinIO (besteht) | Ja | Cloudflare R2 |
|
||||
| Asset CDN | Caddy + Cache | Ja | Cloudflare |
|
||||
| Local-First Sync | mana-sync (besteht) | Ja | - |
|
||||
| LLM (NPCs) | Ollama (besteht) | Ja | OpenRouter |
|
||||
| Image Gen (Assets) | FLUX (besteht) | Ja | - |
|
||||
| STT (Voice Chat) | Whisper (besteht) | Ja | - |
|
||||
| TTS (NPCs) | Kokoro/Piper (besteht) | Ja | - |
|
||||
| Monitoring | Grafana Stack (besteht) | Ja | - |
|
||||
| Error Tracking | GlitchTip (besteht) | Ja | - |
|
||||
| Git/CI | Forgejo (besteht) | Ja | - |
|
||||
| Moderation | Custom ML-Pipeline | Ja | Cloud API |
|
||||
|
||||
**Ergebnis: Ja, fast alles ist self-hostable.** Der einzige Punkt, der Cloud brauchen koennte, ist CDN fuer globale Asset-Auslieferung (Cloudflare R2 ist aber fast kostenlos) und Cloud-Burst fuer Spitzen-Last.
|
||||
|
||||
---
|
||||
|
||||
## 5. Konkrete Architektur-Empfehlung
|
||||
|
||||
### Empfehlung: Option B mit Local-First-Erweiterungen
|
||||
|
||||
Option B ("Ambitioniert") ist der beste Kompromiss:
|
||||
|
||||
- **Go als Kern** (kein Rust lernen)
|
||||
- **3D-faehig** (Three.js/Babylon.js + WebGPU)
|
||||
- **WASM-Scripting** (via wazero, zukunftssicher)
|
||||
- **Local-First** (bestehender Stack, Offline-Editor)
|
||||
- **Self-Hosted** (alles auf Mac Mini moeglich)
|
||||
- **12-18 Monate** bis Prototyp (realistisch)
|
||||
|
||||
### Detaillierte Architektur
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────┐
|
||||
│ BROWSER (Zero Install, PWA) │
|
||||
│ │
|
||||
│ ┌───────────────────┐ ┌─────────────────────────────────┐ │
|
||||
│ │ 3D Rendering │ │ UI Layer (Svelte 5) │ │
|
||||
│ │ Three.js + WebGPU │ │ ┌─────────┐ ┌──────────────┐ │ │
|
||||
│ │ Rapier.js (Physik) │ │ │ Editor │ │ Social/Chat │ │ │
|
||||
│ │ ECS (bitECS) │ │ │ Visual │ │ Marketplace │ │ │
|
||||
│ │ │ │ │ Script │ │ Profile │ │ │
|
||||
│ └────────┬────────────┘ │ │ Code │ │ Settings │ │ │
|
||||
│ │ │ └─────────┘ └──────────────┘ │ │
|
||||
│ ┌────────▼────────────┐ └─────────────────┬─────────────┘ │
|
||||
│ │ WASM Sandbox │ │ │
|
||||
│ │ (User Game Scripts) │ │ │
|
||||
│ └────────┬────────────┘ │ │
|
||||
│ │ │ │
|
||||
│ ┌────────▼──────────────────────────────────▼─────────────┐│
|
||||
│ │ Dexie.js (IndexedDB) ││
|
||||
│ │ - Eigene Games (Offline-Editor!) ││
|
||||
│ │ - Avatar, Inventar, Einstellungen ││
|
||||
│ │ - Spielfortschritt ││
|
||||
│ │ - Asset-Cache (Meshes, Texturen) ││
|
||||
│ └─────────────────────────┬───────────────────────────────┘│
|
||||
│ │ WebTransport + REST + WS │
|
||||
└─────────────────────────────┼────────────────────────────────┘
|
||||
│
|
||||
┌─────────────────────────────▼────────────────────────────────┐
|
||||
│ GAME SERVER LAYER (Go) │
|
||||
│ │
|
||||
│ ┌───────────────────────────────────────────────────────┐ │
|
||||
│ │ mana-game-orchestrator (Go, NEU) │ │
|
||||
│ │ - Spawnt/killt Game-Instanzen als Subprozesse │ │
|
||||
│ │ - Health Monitoring + Auto-Restart │ │
|
||||
│ │ - Ressourcen-Limits (Memory, CPU) │ │
|
||||
│ │ - WebTransport Proxy (quic-go) │ │
|
||||
│ └──────────┬────────────────────────────────────────────┘ │
|
||||
│ │ │
|
||||
│ ┌──────────▼──────┐ ┌──────────────┐ ┌──────────────┐ │
|
||||
│ │ Game Instance A │ │ Instance B │ │ Instance C │ │
|
||||
│ │ ┌──────────────┐│ │ │ │ │ │
|
||||
│ │ │ wazero WASM ││ │ (gleiche │ │ (gleiche │ │
|
||||
│ │ │ - User Script││ │ Struktur) │ │ Struktur) │ │
|
||||
│ │ │ - Physik ││ │ │ │ │ │
|
||||
│ │ │ - ECS State ││ │ │ │ │ │
|
||||
│ │ └──────────────┘│ │ │ │ │ │
|
||||
│ │ Go Host: │ │ │ │ │ │
|
||||
│ │ - Networking │ │ │ │ │ │
|
||||
│ │ - Persistence │ │ │ │ │ │
|
||||
│ │ - Auth Check │ │ │ │ │ │
|
||||
│ └──────────────────┘ └──────────────┘ └──────────────┘ │
|
||||
└──────────────────────────────┬────────────────────────────────┘
|
||||
│
|
||||
┌──────────────────────────────▼────────────────────────────────┐
|
||||
│ BACKEND SERVICES (bestehend + erweitert) │
|
||||
│ │
|
||||
│ ┌────────────┐ ┌──────────┐ ┌────────────┐ ┌──────────────┐ │
|
||||
│ │ mana-auth │ │ mana- │ │ mana-sync │ │ mana- │ │
|
||||
│ │ (Hono) │ │ credits │ │ (Go) │ │ matchmaker │ │
|
||||
│ │ BESTEHT │ │ (Hono) │ │ BESTEHT │ │ (Go, NEU) │ │
|
||||
│ │ │ │ BESTEHT │ │ + erweitert│ │ │ │
|
||||
│ └────────────┘ └──────────┘ └────────────┘ └──────────────┘ │
|
||||
│ │
|
||||
│ ┌────────────┐ ┌──────────┐ ┌────────────┐ ┌──────────────┐ │
|
||||
│ │ mana-user │ │ mana-sub │ │ mana- │ │ mana-notify │ │
|
||||
│ │ (Hono) │ │ (Hono) │ │ analytics │ │ (Go) │ │
|
||||
│ │ BESTEHT │ │ BESTEHT │ │ (Hono) │ │ BESTEHT │ │
|
||||
│ │ │ │ │ │ BESTEHT │ │ │ │
|
||||
│ └────────────┘ └──────────┘ └────────────┘ └──────────────┘ │
|
||||
└──────────────────────────────┬────────────────────────────────┘
|
||||
│
|
||||
┌──────────────────────────────▼────────────────────────────────┐
|
||||
│ AI SERVICES (bestehend) │
|
||||
│ │
|
||||
│ ┌────────────┐ ┌──────────┐ ┌────────────┐ ┌──────────────┐ │
|
||||
│ │ mana-llm │ │ mana- │ │ mana-stt │ │ mana-tts │ │
|
||||
│ │ (Python) │ │ image-gen│ │ (Python) │ │ (Python) │ │
|
||||
│ │ NPC-Dialog │ │ (Python) │ │ Voice Chat │ │ NPC-Sprache │ │
|
||||
│ │ + Quests │ │ Texturen │ │ + Moderat. │ │ │ │
|
||||
│ │ BESTEHT │ │ BESTEHT │ │ BESTEHT │ │ BESTEHT │ │
|
||||
│ └────────────┘ └──────────┘ └────────────┘ └──────────────┘ │
|
||||
└──────────────────────────────┬────────────────────────────────┘
|
||||
│
|
||||
┌──────────────────────────────▼────────────────────────────────┐
|
||||
│ DATA LAYER (bestehend) │
|
||||
│ │
|
||||
│ ┌────────────┐ ┌──────────┐ ┌────────────┐ ┌──────────────┐ │
|
||||
│ │ PostgreSQL │ │ Redis │ │ MinIO │ │ TigerBeetle │ │
|
||||
│ │ 16 │ │ 7 │ │ (S3) │ │ (NEU) │ │
|
||||
│ │ BESTEHT │ │ BESTEHT │ │ BESTEHT │ │ Economy │ │
|
||||
│ └────────────┘ └──────────┘ └────────────┘ └──────────────┘ │
|
||||
└───────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
### Schluessel-Bibliotheken (Client)
|
||||
|
||||
| Bibliothek | Zweck | Warum |
|
||||
| -------------------------------- | ----------------------- | ---------------------------------------------------------------- |
|
||||
| **Three.js** (+ WebGPU Renderer) | 3D-Rendering | 1.3M woechtl. npm Downloads, WebGPU seit r163, riesige Community |
|
||||
| **Rapier.js** | Client-Side Physik | Rust -> WASM, deterministisch, 2D + 3D |
|
||||
| **bitECS** | Entity Component System | Winzig (2 KB), extrem schnell, TypeScript |
|
||||
| **Dexie.js** | Local-First Storage | Besteht, bewiesen, 19 Apps |
|
||||
| **Svelte 5** | UI/Editor | Besteht, bewiesen, runes |
|
||||
|
||||
### Schluessel-Bibliotheken (Server)
|
||||
|
||||
| Bibliothek | Zweck | Warum |
|
||||
| ---------------------------------------------- | ------------------------------ | -------------------------- |
|
||||
| **wazero** | WASM-Sandbox fuer User-Scripts | Reine Go, kein CGo, sicher |
|
||||
| **quic-go** | WebTransport | Reife Go-QUIC-Library |
|
||||
| **pgx** | PostgreSQL | Besteht, performant |
|
||||
| **go-redis** | Redis | Besteht |
|
||||
| **gorilla/websocket** oder **coder/websocket** | WebSocket (Fallback) | Besteht in mana-sync |
|
||||
|
||||
---
|
||||
|
||||
## 6. Was wir wiederverwenden koennen
|
||||
|
||||
### Direkt wiederverwendbar (kein Umbau)
|
||||
|
||||
| Komponente | Dienst | Aufwand |
|
||||
| ------------------ | ---------------------------- | ---------------------------------------------- |
|
||||
| Authentifizierung | mana-auth | 0 -- funktioniert |
|
||||
| Credits/Economy | mana-credits | Minimal -- Erweiterung fuer Game-Transaktionen |
|
||||
| Subscriptions | mana-subscriptions | 0 -- Premium-Abo fuer Creators |
|
||||
| User-Profil | mana-user | Minimal -- Avatar-Daten hinzufuegen |
|
||||
| Benachrichtigungen | mana-notify | 0 -- Push/Email/Matrix |
|
||||
| Monitoring | Grafana/VictoriaMetrics/Loki | 0 -- neue Dashboards |
|
||||
| Error Tracking | GlitchTip | 0 -- SDK einbinden |
|
||||
| Object Storage | MinIO | 0 -- neuer Bucket fuer Game-Assets |
|
||||
| Git/CI | Forgejo | 0 -- Repo + Pipelines |
|
||||
| Suche | mana-search | Minimal -- Game-Suche als Endpoint |
|
||||
| LLM | mana-llm | Minimal -- NPC-Dialog-Endpoint |
|
||||
| Image Gen | mana-image-gen | Anpassung -- Textur-Generierung |
|
||||
| STT | mana-stt | 0 -- Voice-Chat-Transkription |
|
||||
| TTS | mana-tts | 0 -- NPC-Sprachausgabe |
|
||||
|
||||
### Anpassbar (moderater Umbau)
|
||||
|
||||
| Komponente | Basis | Aenderung |
|
||||
| ---------------- | --------------------------- | ------------------------------------------- |
|
||||
| Local-First Sync | mana-sync | Game-World-Sync + Asset-Sync hinzufuegen |
|
||||
| Local Store | @manacore/local-store | Game-Collections (worlds, assets, progress) |
|
||||
| Shared Auth | @manacore/shared-auth | Unveraendert, in neuer App nutzen |
|
||||
| Shared UI | @manacore/shared-ui | Editor-Komponenten ergaenzen |
|
||||
| API Client | @manacore/shared-api-client | Game-Server-Endpoints hinzufuegen |
|
||||
| PWA | @manacore/shared-pwa | Offline-Game-Support |
|
||||
| i18n | @manacore/shared-i18n | Game-spezifische Strings |
|
||||
| Analytics | mana-analytics | Game-Metriken (Plays, Retention) |
|
||||
|
||||
### Neu zu bauen
|
||||
|
||||
| Komponente | Technologie | Aufwand |
|
||||
| --------------------------- | ----------------------------- | -------------------- |
|
||||
| **Game Engine (Client)** | Three.js + Rapier.js + bitECS | Gross (Kern-Aufwand) |
|
||||
| **Game Server** | Go + wazero + quic-go | Gross |
|
||||
| **Game Orchestrator** | Go | Mittel |
|
||||
| **Matchmaker** | Go | Mittel |
|
||||
| **Visual Script Editor** | Svelte + Custom | Gross |
|
||||
| **Code Script Editor** | Monaco + Custom | Mittel |
|
||||
| **3D World Editor** | Three.js + Svelte | Gross |
|
||||
| **Asset Pipeline** | Go + WASM | Mittel |
|
||||
| **Moderation ML** | Python + Custom | Mittel |
|
||||
| **TigerBeetle Integration** | Go/Hono | Klein |
|
||||
|
||||
---
|
||||
|
||||
## 7. Build-Phasen
|
||||
|
||||
### Phase 0: Proof of Concept (1-2 Monate)
|
||||
|
||||
**Ziel:** Validieren, dass der Stack funktioniert.
|
||||
|
||||
- [ ] Three.js + WebGPU Renderer im Browser: Eine 3D-Szene mit Physik (Rapier.js)
|
||||
- [ ] bitECS: Entities spawnen, bewegen, synchronisieren
|
||||
- [ ] Go Server mit quic-go: WebTransport-Verbindung zum Browser
|
||||
- [ ] wazero: Ein einfaches User-Script (TypeScript -> WASM) ausfuehren, das Entities steuert
|
||||
- [ ] Dexie.js: Szene lokal speichern und offline laden
|
||||
|
||||
**Ergebnis:** Ein Browser-Fenster mit 3D-Wuerfel, der sich per WASM-Script bewegt, mit Go-Server-Sync.
|
||||
|
||||
### Phase 1: Minimal Spielbare Plattform (3-6 Monate)
|
||||
|
||||
- [ ] Einfacher 3D-Editor (Objekte platzieren, Eigenschaften setzen)
|
||||
- [ ] Visual Scripting (Knotenbasiert, kompiliert zu WASM)
|
||||
- [ ] Multiplayer: 2-10 Spieler in einer Instanz
|
||||
- [ ] mana-auth Integration (Login/Register)
|
||||
- [ ] Game-Veroeffentlichung (speichern → anderen zugaenglich)
|
||||
- [ ] Game-Entdeckung (Liste aller Games, Suche)
|
||||
- [ ] Offline-Editor via Dexie.js
|
||||
|
||||
### Phase 2: Creator Economy (2-3 Monate)
|
||||
|
||||
- [ ] TigerBeetle Integration fuer virtuelle Waehrung
|
||||
- [ ] In-Game-Purchases (Game Pass, Items)
|
||||
- [ ] Creator-Auszahlungen via mana-credits + Stripe
|
||||
- [ ] Asset Marketplace (3D-Modelle, Texturen, Scripts)
|
||||
- [ ] Avatar-System mit kaufbaren Items
|
||||
|
||||
### Phase 3: Social & Scale (3-4 Monate)
|
||||
|
||||
- [ ] Freundeslisten, Party-System
|
||||
- [ ] Voice Chat (mana-stt + WebRTC)
|
||||
- [ ] Chat (bestehend, angepasst)
|
||||
- [ ] Game-Orchestrator mit Auto-Scaling
|
||||
- [ ] Matchmaking
|
||||
- [ ] Moderation (Text + Voice + Content)
|
||||
|
||||
### Phase 4: AI & Advanced (laufend)
|
||||
|
||||
- [ ] LLM-NPCs (mana-llm)
|
||||
- [ ] AI-Textur-Generierung (mana-image-gen angepasst)
|
||||
- [ ] AI-Code-Assist im Editor (Claude API)
|
||||
- [ ] 3D-Modell-Generierung (Meshy/Tripo API oder self-hosted)
|
||||
- [ ] Voice-gesteuerte Spielerstellung
|
||||
|
||||
### Phase 5: Polish & Growth (laufend)
|
||||
|
||||
- [ ] Mobile PWA-Optimierung
|
||||
- [ ] Advanced Rendering (Schatten, Reflektionen, Partikel)
|
||||
- [ ] Performance-Optimierung (LOD, Streaming, Culling)
|
||||
- [ ] Community-Features (Gruppen, Events, Wettbewerbe)
|
||||
- [ ] Landing Page + Marketing
|
||||
|
||||
---
|
||||
|
||||
## Zusammenfassung
|
||||
|
||||
**Wir haben einen einzigartigen Vorteil:** Die meisten Teams, die eine UGC-Plattform bauen wollen, starten bei null. Wir haben bereits:
|
||||
|
||||
- 19 lokale-first-faehige Web-Apps in Produktion
|
||||
- 6 Go-High-Performance-Services
|
||||
- Ein funktionierendes Auth/Credit/Abo-System
|
||||
- Eine vollstaendige AI-Pipeline (LLM, Image, STT, TTS)
|
||||
- Komplette Self-Hosting-Infrastruktur
|
||||
- SvelteKit-Frontend-Expertise ueber 19 Apps
|
||||
|
||||
**Empfehlung: Option B** -- Go Game Server + Three.js/WebGPU + WASM (wazero) + bestehende Infrastruktur. Browser-first, Local-First, Self-Hosted. Kein Rust lernen, kein Cloud-Vendor-Lock-in, maximale Wiederverwendung.
|
||||
|
||||
**Der Kern-Neuaufbau** (Game Engine + Server + Editor) ist der grosse Brocken. Aber alles drumherum -- Auth, Economy, AI, Storage, Monitoring, Deployment -- ist **bereits gebaut**.
|
||||
|
||||
---
|
||||
|
||||
_Erstellt: 28. Maerz 2026_
|
||||
628
NewAppIdeas/Roblox Reimagined/Technische-Architektur.md
Normal file
628
NewAppIdeas/Roblox Reimagined/Technische-Architektur.md
Normal file
|
|
@ -0,0 +1,628 @@
|
|||
# Roblox: Technische Architektur & moderner Neuaufbau
|
||||
|
||||
## Wie Roblox technisch funktioniert -- und wie man es heute besser bauen wuerde
|
||||
|
||||
---
|
||||
|
||||
## Inhaltsverzeichnis
|
||||
|
||||
1. [Wie Roblox heute technisch funktioniert](#teil-1-wie-roblox-heute-technisch-funktioniert)
|
||||
2. [Wie man es heute mit modernster Technik bauen wuerde](#teil-2-wie-man-es-heute-mit-modernster-technik-bauen-wuerde)
|
||||
3. [Was neue Technik ermoeglicht, das Roblox nicht kann](#teil-3-was-neue-technik-ermoeglicht-das-roblox-nicht-kann)
|
||||
|
||||
---
|
||||
|
||||
# Teil 1: Wie Roblox heute technisch funktioniert
|
||||
|
||||
## 1.1 Die Game Engine
|
||||
|
||||
Roblox nutzt eine **komplett eigenentwickelte C++-Engine**. Kein Unity, kein Unreal -- alles in-house seit 2006, stetig weiterentwickelt.
|
||||
|
||||
### Rendering
|
||||
|
||||
- **Windows:** Direct3D (DirectX)
|
||||
- **macOS/iOS:** Metal
|
||||
- **Roblox Studio:** Vulkan-Support seit 2017
|
||||
- Seit 2014 eigener nativer Renderer (vorher OpenGL), optimiert fuer Low-End-Hardware und Mobile
|
||||
- **"Enhanced Spatial Engine":** Naechste Generation mit Real-Time Ray Tracing und KI-gesteuerter Optimierung
|
||||
|
||||
### Beleuchtungssystem ("Future Is Bright")
|
||||
|
||||
Das Lighting-System ist eines der technisch interessantesten Teilsysteme:
|
||||
|
||||
- **Voxel-basiertes Lighting:** Jeder Voxel misst 4 Studs, inkrementell/lazy auf der CPU aktualisiert
|
||||
- **Shadow-Mapping:** EVSM (Exponential Variance Shadow Maps) fuer weiche Schatten
|
||||
- **Per-Pixel-Lighting:** Fuer lokale Lichtquellen (PointLight, SpotLight, SurfaceLight)
|
||||
- **Forward-Rendering Pipeline:** Deferred Shading wurde evaluiert und verworfen -- zu viele transparente Objekte auf der Plattform
|
||||
- Quality Level 3 und darunter: Fallback auf voxelisierte Beleuchtung fuer Performance
|
||||
|
||||
### Physik-Engine
|
||||
|
||||
- **Aktueller Solver:** PGS (Projected Gauss-Seidel), laeuft bei **240 Hz**
|
||||
- Eigene Entwicklung -- kein ODE oder Bullet
|
||||
- Historisch: Spring Physics (bis Maerz 2019), ein Articulated-Physics-Versuch 2013 (zu langsam, abgebrochen)
|
||||
- **Network Ownership:** Jedes unverankerte Objekt hat einen "Network Owner" (Client), der die Physik berechnet. Kann manuell gesetzt werden (`BasePart:SetNetworkOwner`). `nil` = Server-exklusiv (Anti-Cheat)
|
||||
|
||||
## 1.2 Luau: Die Skriptsprache
|
||||
|
||||
Roblox hat aus Lua 5.1 eine eigene Sprache entwickelt: **Luau** (Open Source: github.com/luau-lang/luau).
|
||||
|
||||
### Sprach-Features
|
||||
|
||||
- **Gradual Typing:** Optionales Typsystem mit automatischer Typinferenz
|
||||
- **Zwei Modi:** Strict Mode (Fehler vor Runtime) und Non-Strict Mode (minimiert False Positives)
|
||||
- **Type Functions:** Fortgeschrittenes Typsystem fuer komplexere Patterns
|
||||
- Vollstaendig rueckwaertskompatibel mit Lua 5.1
|
||||
|
||||
### Performance
|
||||
|
||||
| Modus | Relative Performance |
|
||||
| -------------------- | -------------------------------------- |
|
||||
| Luau Interpreter | ~1x (Baseline) |
|
||||
| LuaJIT (Interpreter) | ~1x (vergleichbar) |
|
||||
| LuaJIT (JIT) | ~1.6x schneller |
|
||||
| Luau Native Code Gen | 1.5-2.5x schneller (seit Oktober 2023) |
|
||||
|
||||
- **Native Code Generation:** Kompiliert Bytecode zu Maschinencode fuer x64 und ARM64
|
||||
- **Garbage Collector:** Inkrementelles Design mit PID-Controller fuer Heap-Pacing (inspiriert vom Go GC), reduziert Pausenzeiten
|
||||
- **Compiler:** Multi-Pass ueber AST, Bytecode-Erzeugung, Post-Process-Optimierungen (Function Inlining, Loop Unrolling)
|
||||
|
||||
### Sandboxing
|
||||
|
||||
- VM kann selbst bei boesartigem Code nicht aus der Sandbox entkommen
|
||||
- `luaL_sandbox` auf Global State + `luaL_sandboxthread` pro Script-Thread
|
||||
- Globale Tabellen sind pro Script isoliert, Built-in-Libraries vor Monkey-Patching geschuetzt
|
||||
|
||||
## 1.3 Server-Infrastruktur
|
||||
|
||||
### Skalierung
|
||||
|
||||
Die Zahlen sind beeindruckend:
|
||||
|
||||
| Metrik | Wert |
|
||||
| ---------------------- | ----------- |
|
||||
| Server total | 135.000+ |
|
||||
| Core Data Centers | 2 |
|
||||
| Edge Data Centers | 24 |
|
||||
| Container | 170.000+ |
|
||||
| Peak CCU | 30 Mio.+ |
|
||||
| Concurrent Connections | 250 Mio.+ |
|
||||
| DAU | 111,8+ Mio. |
|
||||
|
||||
### Orchestrierung: HashiCorp Stack (nicht Kubernetes!)
|
||||
|
||||
Roblox nutzt **nicht** Kubernetes, sondern den **HashiCorp Stack**:
|
||||
|
||||
- **Nomad** fuer Workload-Orchestrierung (gewaehlt ueber K8s, DC/OS, Docker Swarm -- Grund: unterstuetzt Windows + Linux Container und VMs)
|
||||
- **Consul** fuer Service Discovery, automatisiertes Clustering, Service Mesh
|
||||
- **Vault** fuer Secrets Management
|
||||
- Setup-Zeit: Funktionierender Cluster + Bare-Metal-Deployment in **4 Tagen**
|
||||
|
||||
### Zellulaere Architektur
|
||||
|
||||
- Migration aller Services in **"Cells"** (wie Brandschutztueren -- Blast Radius containen)
|
||||
- Jede Cell: ~1.400 Server
|
||||
- Ganze Cells koennen repariert oder komplett neu provisioniert werden
|
||||
- Cloud Bursting fuer Peak-Demand wird erprobt
|
||||
|
||||
### Load Balancing
|
||||
|
||||
- **"Roblox Load Balancer" (RLB):** Basiert auf HAProxy Enterprise Docker Container
|
||||
- HAProxy handhabt **2 Mio.+ RPS mit SSL/TLS** auf einer einzigen Instanz
|
||||
- WAF (Web Application Firewall) mit "ultra-low latency"
|
||||
- Orchestriert mit Nomad, Service Discovery via Consul Template
|
||||
|
||||
### Containerisierung
|
||||
|
||||
- Evolution: Bare Metal -> Linux-basiert -> Container
|
||||
- Envoy Proxy Sidecars neben jedem Service
|
||||
- eBPF-Experimente zur Beobachtung von Connection State zwischen Proxies
|
||||
- Common Control Plane ueber Core und Edge Data Centers
|
||||
|
||||
## 1.4 Networking & Replikation
|
||||
|
||||
### Client-Server-Modell
|
||||
|
||||
- **FilteringEnabled** (seit 2014, jetzt permanent): Client-Property-Aenderungen propagieren NICHT zum Server
|
||||
- Client-zu-Server-Kommunikation ausschliesslich ueber **RemoteEvents** und **RemoteFunctions**
|
||||
- Server repliziert Workspace und ReplicatedStorage automatisch zu Clients
|
||||
|
||||
### Instance Streaming
|
||||
|
||||
- **StreamingEnabled:** Automatisches Streaming basierend auf Kamera-Position des Spielers
|
||||
- `StreamingTargetRadius` kontrolliert Streaming-Reichweite; `MinimumRadius` als Buffer
|
||||
- Mesh- und Texturdaten werden immer on-demand gestreamt, unabhaengig von StreamingEnabled
|
||||
- **Mesh Streaming:** Engine-Level-System fuer dynamische Detail- und Memory-Verwaltung ueber verschiedene Geraete
|
||||
|
||||
## 1.5 Game Hosting (RCCService)
|
||||
|
||||
- **RCCService:** Server-Komponente seit 2008
|
||||
- 2020 von Windows auf Ubuntu migriert, 2021 Windows komplett eingestellt
|
||||
- **Max Spieler pro Server:** 200 (Standard), 700 (Beta seit Juli 2024), war vor 2019 nur 100
|
||||
- **Architektur:** POPs (Points of Presence) mit Game-Servern + zentrale Data Centers fuer Services
|
||||
- Bei Join: Routing zu existierendem Server oder Spawn eines neuen, wenn alle voll
|
||||
- Ein **NetworkReplicator** pro verbundenem Spieler
|
||||
|
||||
## 1.6 Datenpersistenz
|
||||
|
||||
### DataStoreService (Langzeit-Daten)
|
||||
|
||||
- Fuer Spielfortschritt, Inventar, Einstellungen
|
||||
- **Nur serverseitig** zugaenglich (Sicherheit)
|
||||
- **4 MB pro Key** (frueher 256 KB), 30 Tage Versionshistorie
|
||||
- Rate Limits: 25 MB Lese-Durchsatz / 4 MB Schreib-Durchsatz pro Key pro Minute
|
||||
- Bekannte Zuverlaessigkeitsprobleme bei Peak-Traffic (Wochenenden, Feiertage)
|
||||
|
||||
### MemoryStoreService (Kurzzeit-Daten)
|
||||
|
||||
- Hoher Durchsatz, niedrige Latenz (Leaderboards, Matchmaking-Queues)
|
||||
- Daten verfallen nach maximal 45 Tagen
|
||||
- Usage-Limit skaliert mit taeglichen Concurrent Players
|
||||
|
||||
## 1.7 Asset Pipeline & CDN
|
||||
|
||||
- Assets auf **rbxcdn.com** gespeichert mit **Checksum-Hashes** (Content-Addressable, Deduplizierung)
|
||||
- CDN nutzt **AWS CloudFront** mit signierten URLs und Expiry-Timestamps
|
||||
- Mesh Streaming (Early Access): Dynamische Detail- und Memory-Verwaltung ueber Geraete hinweg
|
||||
|
||||
## 1.8 KI & Moderation
|
||||
|
||||
### Text-Filterung (Skalierung)
|
||||
|
||||
| Metrik | Wert |
|
||||
| ---------------------------- | -------------- |
|
||||
| Chat-Nachrichten/Tag | 6,1 Milliarden |
|
||||
| Peak RPS Text-Filtering | 750.000+ |
|
||||
| BERT-Classifier Requests/Tag | 1 Milliarde+ |
|
||||
| Median-Latenz | <20ms |
|
||||
| PII-Filter Peak RPS | 370.000 |
|
||||
|
||||
- **BERT-basierter Classifier** auf CPUs (nicht GPUs!) -- 30x Boost durch DistilBERT + Dynamic Shapes + Quantization
|
||||
- 3.000 Inferences/sec auf Intel Xeon 36-Core vs. 500/sec auf Tesla V100 (CPU ist kosteneffizienter)
|
||||
- PII-Filter: 30% weniger False Positives, 25% mehr PII erkannt
|
||||
|
||||
### Voice-Moderation
|
||||
|
||||
- Sprachsicherheits-Classifier moderiert Chat **innerhalb von 15 Sekunden** in 8 Sprachen
|
||||
- ASR-Transkription optimiert fuer Gaming-Sprache, dann Classifier-Analyse
|
||||
|
||||
### Fortgeschrittene Systeme
|
||||
|
||||
- **Sentinel:** KI-System zur Erkennung von Kindesgefaehrdungsmustern (2025 open-sourced)
|
||||
- **Multi-modale Echtzeit-KI-Moderation:** Analysiert Avatare, Text und Umgebungen zusammen
|
||||
- 0,01% der Milliarden Interaktionen als Policy-Verstoss erkannt, fast alle entfernt vor User-Exposition
|
||||
- Menschliche Moderatoren weltweit fuer Sarkasmus, kodierte Sprache, kulturellen Kontext
|
||||
|
||||
### Matchmaking
|
||||
|
||||
- Evaluiert bis zu **4 Milliarden moegliche Join-Kombinationen pro Sekunde** bei Peak
|
||||
|
||||
## 1.9 Roblox Studio
|
||||
|
||||
- Unified Interface fuer 3D-Weltenbau, Luau-Programmierung, Echtzeit-Kollaboration
|
||||
- **"Team Create":** Echtzeit-Workspace-Sync, Live-Script-Editing
|
||||
- Third-Party-Sync-Tools: Argon (VS Code Extension), RbxSync (MCP Integration)
|
||||
- Batch Commits fuer Collaborative Editing
|
||||
|
||||
## 1.10 Der 73-Stunden-Ausfall (Oktober 2021)
|
||||
|
||||
- **Root Cause:** Consul Streaming Feature unter hoher Read/Write-Last + pathologisches BoltDB-Performance-Problem
|
||||
- Ein einzelner Consul-Cluster fuer mehrere Workloads verstaerkte den Impact
|
||||
- Monitoring-Systeme hingen vom selben fehlerhaften System ab -- keine Sichtbarkeit
|
||||
- **Loesung:** HashiCorp + Roblox entwickelten BoltDB-Compaction-Prozess
|
||||
- **Konsequenz:** Zellulaere Infrastruktur-Initiative + zusaetzliches Data Center
|
||||
|
||||
---
|
||||
|
||||
# Teil 2: Wie man es heute mit modernster Technik bauen wuerde
|
||||
|
||||
## 2.1 Engine: Bevy (Rust) + wgpu -> WebGPU/WASM
|
||||
|
||||
### Warum nicht Roblox' Ansatz kopieren?
|
||||
|
||||
Roblox' Custom C++-Engine ist ein 20 Jahre altes Projekt mit enormem technischen Schulden. Heute wuerde man **Rust** waehlen:
|
||||
|
||||
### Empfehlung: Bevy ECS + wgpu
|
||||
|
||||
**Bevy** (Rust-basierte ECS Game Engine, 0.15+ ab Ende 2025):
|
||||
|
||||
| Vorteil | Detail |
|
||||
| ------------------- | ------------------------------------------------------------------------------- |
|
||||
| **Rust-Sicherheit** | Keine Use-After-Free, keine Buffer Overflows, keine Data Races |
|
||||
| **ECS-Architektur** | Entity Component System trennt Daten von Logik -- macht Sandboxing natuerlicher |
|
||||
| **WebGPU-Output** | Kompiliert nativ zu WASM/WebGPU via wgpu |
|
||||
| **Cross-Platform** | Eine Codebase fuer Browser, Desktop, Mobile |
|
||||
| **Performance** | Rust-Level Performance ohne GC-Pausen |
|
||||
|
||||
**wgpu** ist die Rendering-Abstraktion (benutzt auch von Firefox) und unterstuetzt:
|
||||
|
||||
- WebGPU im Browser
|
||||
- Vulkan auf Linux/Android
|
||||
- Metal auf macOS/iOS
|
||||
- DirectX 12 auf Windows
|
||||
|
||||
### Browser-Native: Der entscheidende Unterschied
|
||||
|
||||
Roblox erfordert einen nativen Client-Download. Mit **WebGPU + WASM** koennte eine neue Plattform **direkt im Browser laufen**:
|
||||
|
||||
- **WebGPU:** Shipped in Chrome 113 (2023), Firefox 141 (2025), Safari 18.2 -- ~70-75% Abdeckung
|
||||
- **WASM-Performance:** Innerhalb von 10-20% nativer Performance fuer Compute-gebundene Arbeit
|
||||
- **Zero-Install:** Kein Download, kein App Store -- massiv niedrigere Einstiegshuerde
|
||||
|
||||
### Alternativen und warum nicht
|
||||
|
||||
| Option | Bewertung |
|
||||
| -------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------ |
|
||||
| **Godot 4** | Maturer Editor, aber Architektur nicht fuer UGC-Sandboxing designed. WebGPU-Export experimentell. Gut fuers Prototyping, riskant fuer Produktion |
|
||||
| **Custom Engine auf wgpu** | Maximale Kontrolle, maximaler Aufwand. Nur gerechtfertigt, wenn Bevy's Abstraktionen limitierend werden |
|
||||
| **Unity/Unreal** | Zu schwer, zu teuer, Lizenzprobleme, nicht fuer UGC-Sandboxing gebaut |
|
||||
|
||||
## 2.2 User-Scripting: WASM-Sandbox + TypeScript
|
||||
|
||||
### Roblox' Ansatz: Luau
|
||||
|
||||
Luau funktioniert hervorragend fuer seinen Zweck, ist aber eine Nischen-Sprache. Die Community nutzt bereits **roblox-ts** (TypeScript-zu-Luau-Transpiler).
|
||||
|
||||
### Moderner Ansatz: Duale Architektur
|
||||
|
||||
**Client-Side: WASM Sandbox (Wasmtime)**
|
||||
|
||||
- **Hardware-Level-Sandboxing:** Lineare Speicherisolation -- User-Code kann die Sandbox physisch nicht verlassen
|
||||
- **Fuel-basiertes Execution Metering:** Instruktionslimits pro Frame verhindern Endlosschleifen
|
||||
- **WASI Preview 2 (Component Model):** Typisierte Host-Guest-Interfaces
|
||||
- User schreiben **TypeScript**, das zu WASM kompiliert wird. Kompilationslatenz: Sekunden -> "Compile and Hot-Reload"-Workflow
|
||||
- **Extism** als Framework fuer WASM-Plugin-Systeme
|
||||
|
||||
**Server-Side: V8 Isolates (Deno/workerd)**
|
||||
|
||||
- Das Cloudflare-Workers-Modell: Startup in ~5ms, vertrautes JS-Oekosystem
|
||||
- Fuer server-autoritative Logik, Matchmaking, Economy-Regeln
|
||||
- V8 ist gross (~30 MB), hoehrer Memory-Overhead als WASM, unvorhersehbare GC-Pausen -- deshalb nur serverseitig
|
||||
|
||||
**Visual Scripting Layer**
|
||||
|
||||
- Knotenbasiert (wie Unreal Blueprints), kompiliert zu WASM
|
||||
- Table Stakes fuer einen Roblox-Konkurrenten -- Kinder muessen Games ohne Code bauen koennen
|
||||
|
||||
### Sicherheitsmodell
|
||||
|
||||
```
|
||||
User-Code (TypeScript)
|
||||
|
|
||||
v
|
||||
WASM-Compilation
|
||||
|
|
||||
v
|
||||
Wasmtime Sandbox
|
||||
- Lineare Speicherisolation
|
||||
- Fuel Metering (Instructions/Frame)
|
||||
- Capability-based API Access (deklarierte Permissions)
|
||||
- Kein Dateisystem/Netzwerk-Zugriff
|
||||
```
|
||||
|
||||
## 2.3 Server-Infrastruktur: Von Fly.io zu Agones
|
||||
|
||||
### Roblox' Ansatz
|
||||
|
||||
- Eigene Data Centers + HashiCorp Nomad
|
||||
- 135.000+ Server, zellulaere Architektur
|
||||
- Funktioniert, aber enormer Ops-Aufwand
|
||||
|
||||
### Moderner Ansatz: Gestaffelt
|
||||
|
||||
**Tier 1 -- Leichtgewicht (<10 Spieler, einfache Logik):**
|
||||
|
||||
- **Fly.io Machines:** Spin-up in ~300ms, 30+ Regionen, Pay-per-Second
|
||||
- Volle Linux-Container (Rust/Go-Server direkt)
|
||||
|
||||
**Tier 2 -- Medium (10-100 Spieler, Physik):**
|
||||
|
||||
- **Agones** auf Kubernetes (GKE/EKS)
|
||||
- Google/CNCF-Standard fuer Game-Server-Orchestrierung
|
||||
- Handles: Allocation, Scaling, Health, Fleet Management
|
||||
- **Quilkin:** Agones-Companion-Proxy fuer UDP Load Balancing, Telemetrie, Traffic Shaping
|
||||
- Spot/Preemptible Instances fuer Kosteneffizienz
|
||||
|
||||
**Tier 3 -- Heavy (100+ Spieler):**
|
||||
|
||||
- Bare Metal oder Reserved Instances
|
||||
- SpatialOS-aehnliche Entity-Distribution ueber mehrere Prozesse
|
||||
- Fuer Massively-Multiplayer-Experiences
|
||||
|
||||
**Weitere Komponenten:**
|
||||
|
||||
| Komponente | Technologie |
|
||||
| ----------------- | ---------------------------------------------------- |
|
||||
| Matchmaking | **Open Match** (Google, Open Source) oder custom |
|
||||
| Fleet Management | Agones GameServerAllocation + Fly.io Machines API |
|
||||
| State Persistence | Checkpoint zu Object Storage (S3/R2) alle N Sekunden |
|
||||
| Global Routing | Anycast oder Latency-based DNS |
|
||||
|
||||
### Warum nicht Cloudflare Durable Objects?
|
||||
|
||||
- Starke Konsistenz innerhalb eines einzelnen Objekts
|
||||
- Limitiert auf ~128 MB Memory, Single-threaded JS
|
||||
- **Nicht geeignet** fuer physik-intensive Game-Server
|
||||
|
||||
## 2.4 Networking: WebTransport (QUIC)
|
||||
|
||||
### Roblox' Ansatz
|
||||
|
||||
Custom binary protocol ueber TCP/UDP mit proprietaerem Replication-System.
|
||||
|
||||
### Moderner Ansatz: WebTransport
|
||||
|
||||
**WebTransport ueber HTTP/3 (QUIC)** ist der klare Gewinner:
|
||||
|
||||
| Feature | Vorteil |
|
||||
| ------------------------ | ----------------------------------------- |
|
||||
| **Unreliable Datagrams** | Positionsupdates (kein Retransmit noetig) |
|
||||
| **Reliable Streams** | Chat, Inventar, Transaktionen |
|
||||
| **Multiplexed** | Kein Head-of-Line-Blocking (TCP-Problem) |
|
||||
| **0-RTT Connection** | Sofortige Wiederverbindung |
|
||||
|
||||
- Unterstuetzt in Chrome, Edge, Firefox (seit 114), Safari 18.2
|
||||
- **QUIC-Library:** Quinn (Rust) -- integriert gut mit Bevy/Rust
|
||||
|
||||
### State-Synchronisation
|
||||
|
||||
- **Server-autoritativ mit Client-Side-Prediction** (Standard)
|
||||
- **Interest Management:** Spatial Hashing oder Grid-based AOI (Area of Interest) Culling
|
||||
- **ECS-Component-Level-Replikation:** Transform bei 20 Hz unreliable, Health bei Aenderung reliable
|
||||
- **CRDTs:** Nur fuer Collaborative/Creative Tools (Building, geteilte Inventare) -- NICHT fuer Physik/Kampf/Economy
|
||||
- **Rollback Netcode:** Nur fuer kompetitive Fighting Games relevant; Standard Server-Auth + Prediction reicht
|
||||
|
||||
### Fallbacks
|
||||
|
||||
- **WebSocket:** Reliable-only, TCP, Head-of-Line-Blocking -> nur als Fallback
|
||||
- **WebRTC:** Nur fuer Voice Chat (Spatial Audio), zu komplex fuer Client-Server-Game-Traffic
|
||||
|
||||
## 2.5 Datenbank & Economy: TigerBeetle + PostgreSQL + Dragonfly
|
||||
|
||||
### Roblox' Ansatz
|
||||
|
||||
DataStoreService (proprietaer, bekannte Zuverlaessigkeitsprobleme) + MemoryStoreService.
|
||||
|
||||
### Moderner Ansatz: Spezialisierte Datenbanken
|
||||
|
||||
**Virtuelle Economy: TigerBeetle**
|
||||
|
||||
- **Purpose-built fuer Financial Accounting**
|
||||
- 1 Mio.+ Transfers/Sekunde auf einem einzelnen Node mit strikter Serialisierbarkeit
|
||||
- **Double-Entry-Bookkeeping** ins Datenmodell eingebaut (Debit + Credit pro Transfer)
|
||||
- Keine "verlorenen" Robux, keine Race Conditions, keine Negative-Balance-Exploits
|
||||
|
||||
**Account-Struktur:**
|
||||
|
||||
```
|
||||
Platform Treasury ("Zentralbank")
|
||||
|
|
||||
+-- User Wallets
|
||||
| +-- Earned Currency (durch Spielen verdient)
|
||||
| +-- Purchased Currency (gekauft)
|
||||
|
|
||||
+-- Game Revenue Accounts (pro veroeffentlichtem Spiel)
|
||||
|
|
||||
+-- Marketplace Escrow (Treuhand fuer Handel)
|
||||
|
|
||||
+-- Creator Payout Pending (ausstehende Auszahlungen)
|
||||
```
|
||||
|
||||
Automatischer Revenue Split: 70% Creator / 30% Plattform (konfigurierbar) -- deutlich fairer als Roblox' ~24,5%.
|
||||
|
||||
**User-Daten: PostgreSQL (Supabase)**
|
||||
|
||||
- Bewaeherter Stack, kann spaeter zu **CockroachDB** migrieren fuer Multi-Region Write Consistency
|
||||
- CockroachDB: Distributed SQL, Postgres-kompatibles Wire Protocol
|
||||
|
||||
**Real-Time State: Dragonfly (Redis-kompatibel)**
|
||||
|
||||
- Multi-threaded, speicher-effizienter als Redis
|
||||
- Fuer: Player Presence, Matchmaking Queues, Leaderboards, Sessions, Pub/Sub
|
||||
- Alternative: **KeyDB** (Multi-threaded Redis Fork, jetzt Teil von Snap)
|
||||
|
||||
**Asset Storage: Cloudflare R2**
|
||||
|
||||
- S3-kompatibel, **null Egress-Kosten**, globale Distribution
|
||||
- Content-Addressable Storage wie Roblox (Hash-basiert, Deduplizierung)
|
||||
|
||||
## 2.6 KI-Integration: Was heute moeglich ist
|
||||
|
||||
### LLM-gesteuerte NPCs
|
||||
|
||||
- **Kleine Modelle (1-7B Parameter):** Phi-3, Gemma 2, Llama 3.x bei 1-3B
|
||||
- Inferenz: <100ms auf modernen GPUs
|
||||
- **Structured Generation** (Outlines, LMFE): Beschraenkt Output auf valide Game-Actions/Schemas
|
||||
- **Server-Side Batch Inference:** vLLM oder TensorRT-LLM. Ein A100 handhabt hunderte NPC-Konversationen
|
||||
- **Memory:** Vector Store (Qdrant, Weaviate) pro NPC + RAG ueber Game-Lore/Regeln
|
||||
- **Kosten-Realitaet:** LLM-NPCs sind teuer im Scale. Reservieren fuer Schluesselcharaktere -- Hintergrund-NPCs nutzen Behavior Trees
|
||||
|
||||
### KI-Asset-Generierung
|
||||
|
||||
| Asset-Typ | Technologie | Status |
|
||||
| ---------------------- | ------------------------------------- | -------------------------------------------------- |
|
||||
| **3D-Modelle** | Meshy, Tripo, Rodin Gen-2 | Text/Bild -> Mesh in Sekunden, Game-ready Low-Poly |
|
||||
| **Single Image -> 3D** | InstantMesh / LRM-based | <10 Sekunden |
|
||||
| **Texturen** | Stable Diffusion / FLUX | PBR-Textur aus Text, sehr ausgereift |
|
||||
| **Animationen** | Motion Diffusion Models (MDM, MoMask) | Text -> Animation, braucht noch Cleanup |
|
||||
|
||||
**Automatische Pipeline:**
|
||||
|
||||
```
|
||||
KI generiert Mesh -> Auto-Retopology -> Auto-UV -> Auto-LOD -> Asset Store
|
||||
```
|
||||
|
||||
### KI-gestuetzte Spielerstellung
|
||||
|
||||
- **Natural Language -> Game Logic:** Claude/GPT-4-Klasse-Modelle mit gutem API-Kontext -- heute schon viable
|
||||
- **LLM-Autocomplete** im Scripting-IDE
|
||||
- **KI-Playtesting:** RL-Agenten erkennen automatisch Softlocks, Exploits, Balance-Probleme
|
||||
|
||||
### KI-Moderation
|
||||
|
||||
- **Multi-modale Modelle** (Vision + Text) fuer Asset-, Chat- und Game-Content-Scanning
|
||||
- **Behavioral ML** fuer Griefing-, Exploit- und Botting-Erkennung
|
||||
- **Echtzeit-Voice:** Whisper-Transkription -> Text-Toxicity-Classification (~1-2 Sekunden Latenz)
|
||||
|
||||
## 2.7 3D/Grafik-Innovationen
|
||||
|
||||
### Gaussian Splatting: Die Killer-UGC-Feature
|
||||
|
||||
- **Echtzeit-Rendering bei 60fps** auf Desktop-GPUs (2025-2026)
|
||||
- **Killer-Feature fuer UGC:** Handy-Photogrammetrie-Scan -> Import als Game-Welt
|
||||
- WebGPU kann via Compute Shaders rendern -- Open-Source WebGPU-Splat-Renderer existieren
|
||||
- **Limitation:** View-dependent, interagiert schlecht mit traditionellem Lighting/Physik/Animation
|
||||
- **Empfehlung: Hybrid Rendering** -- Mesh-basierte Charaktere + Gaussian-Splat-Umgebungen
|
||||
|
||||
### Neural Rendering
|
||||
|
||||
- **Neural Texture Compression:** Hoehere Qualitaet als BC7 bei gleicher Bitrate (shipped in NVIDIA Ada GPUs)
|
||||
- **Neural LOD:** Perzeptuell bessere Mesh-Decimation. Research-to-Production-Stadium
|
||||
|
||||
### Nanite-aehnliche Geometry Virtualization
|
||||
|
||||
- Erfordert tiefes GPU-Wissen und Jahre Entwicklung -- nicht praktikabel fuer ein Startup
|
||||
- **Praktische Alternative:** Aggressive LOD-Generierung + **Meshlet-basiertes Rendering** (Meshes in ~64-128 Triangle-Cluster teilen, GPU-per-Cluster-Culling)
|
||||
- Machbar mit WebGPU Compute Shaders: **80% des Nutzens bei 10% des Aufwands**
|
||||
|
||||
### Ray Tracing auf Mobile
|
||||
|
||||
- Hardware-RT existiert auf Qualcomm Adreno, ARM Mali (limitiert auf Schatten/Reflektionen)
|
||||
- WebGPU hat **noch keine Ray-Tracing-APIs** (Stand frueh 2026)
|
||||
- Timeline: WebGPU RT experimentell in Chrome Ende 2026-2027. Praktisches Mobile-Browser-RT: 2027-2028
|
||||
- **Fuer jetzt:** Screen-Space Reflections und Ambient Occlusion via WebGPU Compute
|
||||
|
||||
---
|
||||
|
||||
# Teil 3: Was neue Technik ermoeglicht, das Roblox nicht kann
|
||||
|
||||
## 3.1 Zero-Install Browser-Experience
|
||||
|
||||
Roblox erfordert einen nativen Client-Download. Mit WebGPU + WASM koennte eine neue Plattform **komplett im Browser laufen** -- kein Download, kein App Store, kein Google/Apple-Gatekeeping. Ein Link reicht.
|
||||
|
||||
**Impact:** Massiv niedrigere Akquisitionskosten, sofortiger Zugang, einfacheres Teilen ("Schau dir mein Spiel an" -> ein Klick).
|
||||
|
||||
## 3.2 Groessere Welten
|
||||
|
||||
- WebGPU + WASM-Streaming-World-Loading mit Interest Management -> **effektiv unbegrenzte Weltgroesse**
|
||||
- Prozedurale Generierung auf GPU via WebGPU Compute Shaders
|
||||
- Roblox ist auf relativ kleine Welten limitiert
|
||||
|
||||
## 3.3 Mehr Spieler pro Instanz
|
||||
|
||||
| Ansatz | Spieler |
|
||||
| --------------------------------------- | -------------------------- |
|
||||
| Roblox heute | 200 (Standard), 700 (Beta) |
|
||||
| Interest Management + ECS Replikation | 200-500 machbar |
|
||||
| SpatialOS-aehnliche Entity-Distribution | 1.000+ (komplex) |
|
||||
|
||||
## 3.4 Cross-Game-Inventare
|
||||
|
||||
Roblox hat nur Avatar-Items, die plattformweit funktionieren. Eine neue Plattform koennte:
|
||||
|
||||
- Plattform-Level Item-Archetypes definieren (Cosmetic Skin, Currency, Badge/Achievement)
|
||||
- Games gewaehren diese; Games waehlen, welche Plattform-Items sie anerkennen
|
||||
- TigerBeetle fuer Cross-Game-Currency; PostgreSQL-Metadaten-Registry fuer Item-Definitionen
|
||||
- **Kein Blockchain noetig** -- Double-Entry-Bookkeeping mit TigerBeetle ist zuverlaessiger und schneller
|
||||
|
||||
## 3.5 User-generierte KI-Verhaltensweisen
|
||||
|
||||
Roblox-NPCs nutzen simple Skripte. Neu moeglich:
|
||||
|
||||
- Creator definieren NPC-Persoenlichkeit und Wissen in Natural Language
|
||||
- LLM generiert dynamische Dialoge und Reaktionen
|
||||
- NPCs, die sich an individuelle Spieler erinnern (Vector Store pro NPC)
|
||||
- KI-gesteuerte Quests, die sich an den Spielstil anpassen
|
||||
|
||||
## 3.6 Voice-gesteuerte Spielerstellung
|
||||
|
||||
- **Whisper/Deepgram STT + LLM:** "Mach die Burg groesser und fuege einen Graben hinzu" -> modifiziert die Spielwelt in Echtzeit
|
||||
- Kinder koennten Games **durch Sprechen** erstellen, nicht durch Code oder GUI
|
||||
- WebRTC fuer Echtzeit-Spatial-Audio Voice Chat
|
||||
|
||||
## 3.7 Spatial Computing / AR
|
||||
|
||||
- **WebXR + WebGPU** ermoeglicht AR/VR UGC-Games im Browser (Meta Quest Browser unterstuetzt beides)
|
||||
- **Handy + LiDAR Scan -> Gaussian Splats -> editierbare Game-Welten**
|
||||
- Das eigene Kinderzimmer wird zum Game-Level
|
||||
- Apple Vision Pro: "UGC-Spiele ueberlagert auf deinem Raum" -- kleiner Markt, Technologie bereit
|
||||
|
||||
## 3.8 On-Device KI
|
||||
|
||||
- **WebNN** (Web Neural Network API) shipped in Chrome -- ML-Modelle auf Device-GPUs/NPUs im Browser
|
||||
- **ONNX Runtime Web** mit WebGPU-Backend laesst Phi-3 Mini interaktiv im Browser laufen
|
||||
- Use Cases: Style Transfer, On-Device NPC Inference, Client-Side Moderation, Gesture Recognition
|
||||
- **Kein Server-Roundtrip noetig** fuer bestimmte KI-Features
|
||||
|
||||
## 3.9 Fairere Creator-Economy
|
||||
|
||||
Roblox: ~24,5% fuer Entwickler. Mit moderner Infrastruktur:
|
||||
|
||||
- **70/30 Split** (Creator/Plattform) wird moeglich weil:
|
||||
- Browser-Distribution eliminiert App-Store-Fees (Apple 30%, Google 30%)
|
||||
- Edge Computing und WASM-Sandbox reduzieren Server-Kosten pro Instanz
|
||||
- TigerBeetle fuer die Economy ist kosteneffizienter als proprietaere Systeme
|
||||
- Transparente Echtzeit-Einnahmen-Dashboards statt monatelanger DevEx-Prozesse
|
||||
|
||||
---
|
||||
|
||||
# Empfohlener Technology Stack (Zusammenfassung)
|
||||
|
||||
| Layer | Technologie | Begruendung |
|
||||
| -------------------- | ------------------------------------ | ----------------------------------------- |
|
||||
| **Rendering** | Bevy + wgpu -> WebGPU/WASM | Rust-Sicherheit, ECS, Browser + Native |
|
||||
| **Client Scripting** | WASM Sandbox (TS -> WASM) | Hardware-Level-Isolation, Fuel Metering |
|
||||
| **Server Scripting** | V8 Isolates (Deno) | Fast Startup, JS-Oekosystem |
|
||||
| **Game Server** | Rust auf Fly.io -> Agones/K8s | Start einfach, skaliere zu Orchestrierung |
|
||||
| **Networking** | WebTransport (QUIC) + WS Fallback | Unreliable Datagrams + Reliable Streams |
|
||||
| **QUIC Library** | Quinn (Rust) | Native Rust, Async |
|
||||
| **User Data** | PostgreSQL (Supabase) -> CockroachDB | Start bewaehrt, skaliere spaeter |
|
||||
| **Economy** | TigerBeetle | Double-Entry-Bookkeeping, 1M+ TPS |
|
||||
| **Real-Time State** | Dragonfly (Redis-kompatibel) | Multi-threaded, speicher-effizient |
|
||||
| **Asset Storage** | Cloudflare R2 | Null Egress, S3-kompatibel |
|
||||
| **KI NPCs** | Phi-3, Gemma (kleine LLMs) + vLLM | Kosteneffizient, Structured Output |
|
||||
| **KI Assets** | Meshy/Tripo + Custom Pipeline | Text/Bild -> Game-ready 3D |
|
||||
| **KI Code Gen** | Claude API im Editor | Natural Language -> Scripts |
|
||||
| **Voice Chat** | WebRTC (LiveKit) | Spatial Audio, skalierbar |
|
||||
| **Moderation** | Multi-modale KI + Behavioral ML | Trust & Safety ist existenziell |
|
||||
| **3D Scanning** | Gaussian Splatting Import | Handy -> Game-Welt |
|
||||
|
||||
---
|
||||
|
||||
# Empfohlene Build-Phasen (3-5 Jahre, 15-30 Engineers)
|
||||
|
||||
### Phase 1: Core Engine (6-9 Monate)
|
||||
|
||||
Bevy + wgpu Rendering im Browser via WASM/WebGPU. Basic Multiplayer mit WebTransport.
|
||||
|
||||
### Phase 2: Scripting Sandbox (3-6 Monate)
|
||||
|
||||
WASM TypeScript-Scripting. User erstellen einfache interaktive Experiences.
|
||||
|
||||
### Phase 3: Creation Tools (6-9 Monate)
|
||||
|
||||
In-Browser World Editor mit Visual Scripting + Code Editor + KI Code-Generierung.
|
||||
|
||||
### Phase 4: Economy (3-4 Monate)
|
||||
|
||||
TigerBeetle fuer virtuelle Waehrung. Marketplace fuer User-erstellte Assets.
|
||||
|
||||
### Phase 5: Social + Scale (6-9 Monate)
|
||||
|
||||
Matchmaking, Friends, Voice Chat, Moderation. Migration zu Agones.
|
||||
|
||||
### Phase 6: KI Features (6-12 Monate)
|
||||
|
||||
Asset-Generierungs-Pipeline, LLM-NPCs, Advanced Moderation.
|
||||
|
||||
### Phase 7: Advanced Graphics (laufend)
|
||||
|
||||
Gaussian Splatting Import, Meshlet Rendering, Spatial Computing.
|
||||
|
||||
---
|
||||
|
||||
> **Entscheidendes Insight:** "Distribution (Browser-nativ, Zero Install) und Creation Tools (einfaches Bauen von Games) sind wichtiger als Grafikqualitaet. Roblox' Grafik ist bescheiden -- sein Creation-Oekosystem hat es zu einem $40B+ Unternehmen gemacht."
|
||||
|
||||
---
|
||||
|
||||
_Recherche-Datum: 28. Maerz 2026_
|
||||
57
apps/manavoxel/CLAUDE.md
Normal file
57
apps/manavoxel/CLAUDE.md
Normal file
|
|
@ -0,0 +1,57 @@
|
|||
# ManaVoxel Project Guide
|
||||
|
||||
## Overview
|
||||
|
||||
**ManaVoxel** is a 2D top-down pixel platform where players create detailed miniature worlds, program items with behaviors, and share them — all in the browser.
|
||||
|
||||
| App | Port | URL |
|
||||
|-----|------|-----|
|
||||
| Web App | 5195 | http://localhost:5195 |
|
||||
|
||||
## Project Structure
|
||||
|
||||
```
|
||||
apps/manavoxel/
|
||||
├── apps/
|
||||
│ └── web/ # SvelteKit + PixiJS client (@manavoxel/web)
|
||||
├── packages/
|
||||
│ └── shared/ # Shared types (@manavoxel/shared)
|
||||
├── package.json
|
||||
└── CLAUDE.md
|
||||
```
|
||||
|
||||
## Commands
|
||||
|
||||
```bash
|
||||
# From monorepo root
|
||||
pnpm dev:manavoxel:web # Start web app (port 5195)
|
||||
|
||||
# From apps/manavoxel
|
||||
pnpm dev # Start all apps
|
||||
pnpm dev:web # Start web only
|
||||
```
|
||||
|
||||
## Technology Stack
|
||||
|
||||
| Layer | Technology |
|
||||
|-------|------------|
|
||||
| **Rendering** | PixiJS 8 (WebGL) |
|
||||
| **UI** | SvelteKit 2, Svelte 5 (runes), Tailwind CSS 4 |
|
||||
| **Local-First** | Dexie.js via @manacore/local-store |
|
||||
| **Auth** | Mana Core Auth (JWT) |
|
||||
| **i18n** | svelte-i18n (DE, EN, FR, ES, IT) |
|
||||
|
||||
## Zoom Levels
|
||||
|
||||
| Level | 1 Pixel = | Use |
|
||||
|-------|-----------|-----|
|
||||
| Street | 10cm | Walking, interaction, combat |
|
||||
| Interior | 5cm | Exploring rooms, furniture |
|
||||
| Detail | 1cm | Item/character sprite editing |
|
||||
|
||||
## Core Concepts
|
||||
|
||||
- **Areas**: Streets (10cm) and interiors (5cm) are separate pixel grids connected by portals
|
||||
- **Items**: Pixel sprites (1cm) with properties (sliders) and behaviors (trigger-actions)
|
||||
- **Floors**: Interiors have multiple floors, connected by stairs
|
||||
- **Local-First**: Everything works offline via Dexie.js, syncs via mana-sync
|
||||
57
apps/manavoxel/apps/web/package.json
Normal file
57
apps/manavoxel/apps/web/package.json
Normal file
|
|
@ -0,0 +1,57 @@
|
|||
{
|
||||
"name": "@manavoxel/web",
|
||||
"version": "1.0.0",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite dev",
|
||||
"build": "vite build",
|
||||
"preview": "vite preview",
|
||||
"prepare": "svelte-kit sync || echo ''",
|
||||
"check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
|
||||
"check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch",
|
||||
"lint": "eslint .",
|
||||
"format": "prettier --write .",
|
||||
"type-check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
|
||||
"test": "vitest run",
|
||||
"test:watch": "vitest"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@manacore/shared-pwa": "workspace:*",
|
||||
"@manacore/shared-vite-config": "workspace:*",
|
||||
"@sveltejs/adapter-node": "^5.0.0",
|
||||
"@sveltejs/kit": "^2.47.1",
|
||||
"@sveltejs/vite-plugin-svelte": "^5.0.0",
|
||||
"@tailwindcss/vite": "^4.1.7",
|
||||
"@types/node": "^20.0.0",
|
||||
"@vite-pwa/sveltekit": "^1.1.0",
|
||||
"prettier": "^3.1.1",
|
||||
"prettier-plugin-svelte": "^3.1.2",
|
||||
"svelte": "^5.41.0",
|
||||
"svelte-check": "^4.3.3",
|
||||
"tailwindcss": "^4.1.7",
|
||||
"tslib": "^2.4.1",
|
||||
"typescript": "^5.9.3",
|
||||
"vite": "^6.0.0",
|
||||
"vitest": "^4.1.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"@manacore/local-store": "workspace:*",
|
||||
"@manacore/shared-auth": "workspace:*",
|
||||
"@manacore/shared-auth-stores": "workspace:*",
|
||||
"@manacore/shared-auth-ui": "workspace:*",
|
||||
"@manacore/shared-error-tracking": "workspace:*",
|
||||
"@manacore/shared-i18n": "workspace:*",
|
||||
"@manacore/shared-icons": "workspace:*",
|
||||
"@manacore/shared-stores": "workspace:*",
|
||||
"@manacore/shared-tailwind": "workspace:*",
|
||||
"@manacore/shared-theme": "workspace:*",
|
||||
"@manacore/shared-theme-ui": "workspace:*",
|
||||
"@manacore/shared-types": "workspace:*",
|
||||
"@manacore/shared-ui": "workspace:*",
|
||||
"@manacore/shared-utils": "workspace:*",
|
||||
"@manavoxel/shared": "workspace:*",
|
||||
"pixi.js": "^8.17.1",
|
||||
"svelte-i18n": "^4.0.1"
|
||||
}
|
||||
}
|
||||
22
apps/manavoxel/apps/web/src/app.css
Normal file
22
apps/manavoxel/apps/web/src/app.css
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
@import 'tailwindcss';
|
||||
@import '@manacore/shared-tailwind';
|
||||
|
||||
/* ManaVoxel specific styles */
|
||||
:root {
|
||||
--mv-accent: #10b981;
|
||||
--mv-accent-hover: #059669;
|
||||
--mv-grid: rgba(255, 255, 255, 0.05);
|
||||
}
|
||||
|
||||
/* Prevent text selection during gameplay */
|
||||
.game-canvas {
|
||||
user-select: none;
|
||||
-webkit-user-select: none;
|
||||
touch-action: none;
|
||||
}
|
||||
|
||||
/* Pixel-perfect rendering for the canvas */
|
||||
canvas {
|
||||
image-rendering: pixelated;
|
||||
image-rendering: crisp-edges;
|
||||
}
|
||||
14
apps/manavoxel/apps/web/src/app.html
Normal file
14
apps/manavoxel/apps/web/src/app.html
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
<!doctype html>
|
||||
<html lang="de">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<link rel="icon" href="%sveltekit.assets%/favicon.png" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no" />
|
||||
<meta name="theme-color" content="#10b981" />
|
||||
<meta name="description" content="ManaVoxel - Create and program detailed pixel worlds in your browser" />
|
||||
%sveltekit.head%
|
||||
</head>
|
||||
<body data-sveltekit-preload-data="hover">
|
||||
<div style="display: contents">%sveltekit.body%</div>
|
||||
</body>
|
||||
</html>
|
||||
201
apps/manavoxel/apps/web/src/lib/editor/tools.ts
Normal file
201
apps/manavoxel/apps/web/src/lib/editor/tools.ts
Normal file
|
|
@ -0,0 +1,201 @@
|
|||
import type { TilemapRenderer } from '$lib/engine/tilemap';
|
||||
import { MATERIAL_AIR } from '@manavoxel/shared';
|
||||
|
||||
// ─── Undo/Redo System ───────────────────────────────────────
|
||||
|
||||
export interface PixelChange {
|
||||
x: number;
|
||||
y: number;
|
||||
oldMaterial: number;
|
||||
newMaterial: number;
|
||||
}
|
||||
|
||||
export class UndoStack {
|
||||
private _undoStack: PixelChange[][] = [];
|
||||
private _redoStack: PixelChange[][] = [];
|
||||
private _currentBatch: PixelChange[] = [];
|
||||
private _maxSize = 50;
|
||||
|
||||
/** Start collecting changes for a single user action */
|
||||
beginBatch() {
|
||||
this._currentBatch = [];
|
||||
}
|
||||
|
||||
/** Record a single pixel change within the current batch */
|
||||
record(x: number, y: number, oldMaterial: number, newMaterial: number) {
|
||||
if (oldMaterial === newMaterial) return;
|
||||
this._currentBatch.push({ x, y, oldMaterial, newMaterial });
|
||||
}
|
||||
|
||||
/** Commit the current batch as one undo-able action */
|
||||
commitBatch() {
|
||||
if (this._currentBatch.length === 0) return;
|
||||
this._undoStack.push(this._currentBatch);
|
||||
if (this._undoStack.length > this._maxSize) {
|
||||
this._undoStack.shift();
|
||||
}
|
||||
this._redoStack = []; // Clear redo on new action
|
||||
this._currentBatch = [];
|
||||
}
|
||||
|
||||
get canUndo() {
|
||||
return this._undoStack.length > 0;
|
||||
}
|
||||
get canRedo() {
|
||||
return this._redoStack.length > 0;
|
||||
}
|
||||
|
||||
undo(tilemap: TilemapRenderer) {
|
||||
const batch = this._undoStack.pop();
|
||||
if (!batch) return;
|
||||
for (let i = batch.length - 1; i >= 0; i--) {
|
||||
const c = batch[i];
|
||||
tilemap.setPixel(c.x, c.y, c.oldMaterial);
|
||||
}
|
||||
this._redoStack.push(batch);
|
||||
}
|
||||
|
||||
redo(tilemap: TilemapRenderer) {
|
||||
const batch = this._redoStack.pop();
|
||||
if (!batch) return;
|
||||
for (const c of batch) {
|
||||
tilemap.setPixel(c.x, c.y, c.newMaterial);
|
||||
}
|
||||
this._undoStack.push(batch);
|
||||
}
|
||||
}
|
||||
|
||||
// ─── Editor Tools ───────────────────────────────────────────
|
||||
|
||||
export type ToolType = 'brush' | 'eraser' | 'fill' | 'pipette' | 'box' | 'line';
|
||||
|
||||
/**
|
||||
* Place a single pixel (or brush area), recording to undo stack.
|
||||
*/
|
||||
export function brushStroke(
|
||||
tilemap: TilemapRenderer,
|
||||
undo: UndoStack,
|
||||
cx: number,
|
||||
cy: number,
|
||||
material: number,
|
||||
size: number
|
||||
) {
|
||||
const radius = Math.floor(size / 2);
|
||||
for (let dy = -radius; dy <= radius; dy++) {
|
||||
for (let dx = -radius; dx <= radius; dx++) {
|
||||
const x = cx + dx;
|
||||
const y = cy + dy;
|
||||
if (x < 0 || x >= tilemap.worldWidth || y < 0 || y >= tilemap.worldHeight) continue;
|
||||
const old = tilemap.getPixel(x, y);
|
||||
undo.record(x, y, old, material);
|
||||
tilemap.setPixel(x, y, material);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Flood fill from a starting position.
|
||||
* Replaces all connected pixels of the same material with the new material.
|
||||
*/
|
||||
export function floodFill(
|
||||
tilemap: TilemapRenderer,
|
||||
undo: UndoStack,
|
||||
startX: number,
|
||||
startY: number,
|
||||
fillMaterial: number
|
||||
) {
|
||||
const targetMaterial = tilemap.getPixel(startX, startY);
|
||||
if (targetMaterial === fillMaterial) return;
|
||||
|
||||
const stack: [number, number][] = [[startX, startY]];
|
||||
const visited = new Set<string>();
|
||||
const maxIterations = 50_000; // Safety limit
|
||||
let iterations = 0;
|
||||
|
||||
while (stack.length > 0 && iterations < maxIterations) {
|
||||
const [x, y] = stack.pop()!;
|
||||
const key = `${x},${y}`;
|
||||
if (visited.has(key)) continue;
|
||||
visited.add(key);
|
||||
|
||||
if (x < 0 || x >= tilemap.worldWidth || y < 0 || y >= tilemap.worldHeight) continue;
|
||||
if (tilemap.getPixel(x, y) !== targetMaterial) continue;
|
||||
|
||||
undo.record(x, y, targetMaterial, fillMaterial);
|
||||
tilemap.setPixel(x, y, fillMaterial);
|
||||
|
||||
stack.push([x + 1, y], [x - 1, y], [x, y + 1], [x, y - 1]);
|
||||
iterations++;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Pipette: Pick the material at a position.
|
||||
* Returns the material ID.
|
||||
*/
|
||||
export function pipette(tilemap: TilemapRenderer, x: number, y: number): number {
|
||||
return tilemap.getPixel(x, y);
|
||||
}
|
||||
|
||||
/**
|
||||
* Draw a filled rectangle from corner to corner.
|
||||
*/
|
||||
export function boxFill(
|
||||
tilemap: TilemapRenderer,
|
||||
undo: UndoStack,
|
||||
x1: number,
|
||||
y1: number,
|
||||
x2: number,
|
||||
y2: number,
|
||||
material: number
|
||||
) {
|
||||
const minX = Math.max(0, Math.min(x1, x2));
|
||||
const maxX = Math.min(tilemap.worldWidth - 1, Math.max(x1, x2));
|
||||
const minY = Math.max(0, Math.min(y1, y2));
|
||||
const maxY = Math.min(tilemap.worldHeight - 1, Math.max(y1, y2));
|
||||
|
||||
for (let y = minY; y <= maxY; y++) {
|
||||
for (let x = minX; x <= maxX; x++) {
|
||||
const old = tilemap.getPixel(x, y);
|
||||
undo.record(x, y, old, material);
|
||||
tilemap.setPixel(x, y, material);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Draw a line from (x1,y1) to (x2,y2) using Bresenham's algorithm.
|
||||
*/
|
||||
export function lineDraw(
|
||||
tilemap: TilemapRenderer,
|
||||
undo: UndoStack,
|
||||
x1: number,
|
||||
y1: number,
|
||||
x2: number,
|
||||
y2: number,
|
||||
material: number,
|
||||
brushSize: number
|
||||
) {
|
||||
let dx = Math.abs(x2 - x1);
|
||||
let dy = -Math.abs(y2 - y1);
|
||||
const sx = x1 < x2 ? 1 : -1;
|
||||
const sy = y1 < y2 ? 1 : -1;
|
||||
let err = dx + dy;
|
||||
|
||||
let cx = x1;
|
||||
let cy = y1;
|
||||
|
||||
while (true) {
|
||||
brushStroke(tilemap, undo, cx, cy, material, brushSize);
|
||||
if (cx === x2 && cy === y2) break;
|
||||
const e2 = 2 * err;
|
||||
if (e2 >= dy) {
|
||||
err += dy;
|
||||
cx += sx;
|
||||
}
|
||||
if (e2 <= dx) {
|
||||
err += dx;
|
||||
cy += sy;
|
||||
}
|
||||
}
|
||||
}
|
||||
63
apps/manavoxel/apps/web/src/lib/engine/camera.ts
Normal file
63
apps/manavoxel/apps/web/src/lib/engine/camera.ts
Normal file
|
|
@ -0,0 +1,63 @@
|
|||
import type { Container } from 'pixi.js';
|
||||
|
||||
export class Camera {
|
||||
private _container: Container;
|
||||
private _x = 0;
|
||||
private _y = 0;
|
||||
private _scale = 2; // 2x zoom by default (each 10cm pixel = 2 screen pixels)
|
||||
private _minScale = 0.5;
|
||||
private _maxScale = 8;
|
||||
|
||||
get x() {
|
||||
return this._x;
|
||||
}
|
||||
get y() {
|
||||
return this._y;
|
||||
}
|
||||
get scale() {
|
||||
return this._scale;
|
||||
}
|
||||
|
||||
constructor(container: Container) {
|
||||
this._container = container;
|
||||
}
|
||||
|
||||
setPosition(x: number, y: number) {
|
||||
this._x = x;
|
||||
this._y = y;
|
||||
}
|
||||
|
||||
move(dx: number, dy: number) {
|
||||
this._x += dx / this._scale;
|
||||
this._y += dy / this._scale;
|
||||
}
|
||||
|
||||
zoom(factor: number) {
|
||||
const newScale = this._scale * factor;
|
||||
this._scale = Math.max(this._minScale, Math.min(this._maxScale, newScale));
|
||||
}
|
||||
|
||||
setScale(scale: number) {
|
||||
this._scale = Math.max(this._minScale, Math.min(this._maxScale, scale));
|
||||
}
|
||||
|
||||
/** Convert screen coordinates to world coordinates */
|
||||
screenToWorld(
|
||||
screenX: number,
|
||||
screenY: number,
|
||||
screenWidth: number,
|
||||
screenHeight: number
|
||||
): { x: number; y: number } {
|
||||
return {
|
||||
x: (screenX - screenWidth / 2) / this._scale + this._x,
|
||||
y: (screenY - screenHeight / 2) / this._scale + this._y,
|
||||
};
|
||||
}
|
||||
|
||||
/** Apply camera transform to the world container */
|
||||
update(screenWidth: number, screenHeight: number) {
|
||||
this._container.x = screenWidth / 2 - this._x * this._scale;
|
||||
this._container.y = screenHeight / 2 - this._y * this._scale;
|
||||
this._container.scale.set(this._scale);
|
||||
}
|
||||
}
|
||||
229
apps/manavoxel/apps/web/src/lib/engine/game.ts
Normal file
229
apps/manavoxel/apps/web/src/lib/engine/game.ts
Normal file
|
|
@ -0,0 +1,229 @@
|
|||
import { Application, Container } from 'pixi.js';
|
||||
import { Camera } from './camera';
|
||||
import { InputManager } from './input';
|
||||
import { TilemapRenderer } from './tilemap';
|
||||
import { Player } from './player';
|
||||
import { UndoStack, brushStroke, floodFill, pipette, type ToolType } from '$lib/editor/tools';
|
||||
import { DEFAULT_MATERIALS, MATERIAL_AIR, type Material } from '@manavoxel/shared';
|
||||
|
||||
export class GameEngine {
|
||||
app: Application;
|
||||
camera: Camera;
|
||||
input: InputManager;
|
||||
tilemap: TilemapRenderer;
|
||||
player: Player | null = null;
|
||||
undo: UndoStack;
|
||||
|
||||
private _container: HTMLDivElement;
|
||||
private _worldContainer: Container;
|
||||
private _initialized = false;
|
||||
|
||||
// Editor state
|
||||
private _editing = false;
|
||||
private _selectedMaterial = 1;
|
||||
private _activeTool: ToolType = 'brush';
|
||||
private _brushSize = 1;
|
||||
private _palette: Material[] = DEFAULT_MATERIALS;
|
||||
private _painting = false; // tracks whether we're in a continuous paint stroke
|
||||
|
||||
// Callbacks for UI reactivity
|
||||
onStateChange: (() => void) | null = null;
|
||||
|
||||
get isEditing() {
|
||||
return this._editing;
|
||||
}
|
||||
get selectedMaterial() {
|
||||
return this._selectedMaterial;
|
||||
}
|
||||
get activeTool() {
|
||||
return this._activeTool;
|
||||
}
|
||||
get brushSize() {
|
||||
return this._brushSize;
|
||||
}
|
||||
get palette() {
|
||||
return this._palette;
|
||||
}
|
||||
|
||||
constructor(container: HTMLDivElement) {
|
||||
this._container = container;
|
||||
this.app = new Application();
|
||||
this._worldContainer = new Container();
|
||||
this.undo = new UndoStack();
|
||||
|
||||
this.camera = new Camera(this._worldContainer);
|
||||
this.input = new InputManager(container);
|
||||
this.tilemap = new TilemapRenderer(this._worldContainer, this._palette);
|
||||
|
||||
this._init();
|
||||
}
|
||||
|
||||
private async _init() {
|
||||
await this.app.init({
|
||||
resizeTo: this._container,
|
||||
background: '#1a1a2e',
|
||||
antialias: false,
|
||||
resolution: window.devicePixelRatio || 1,
|
||||
autoDensity: true,
|
||||
});
|
||||
|
||||
this._container.appendChild(this.app.canvas);
|
||||
this.app.stage.addChild(this._worldContainer);
|
||||
|
||||
// Generate demo world
|
||||
this.tilemap.generateFlatWorld(500, 300);
|
||||
|
||||
// Spawn player in an open area
|
||||
this.player = new Player(this._worldContainer, this.tilemap, 60, 160);
|
||||
|
||||
// Center camera on player
|
||||
this.camera.setPosition(this.player.worldX, this.player.worldY);
|
||||
|
||||
// Game loop
|
||||
this.app.ticker.add((ticker) => this._update(ticker.deltaTime));
|
||||
|
||||
this._initialized = true;
|
||||
this.onStateChange?.();
|
||||
}
|
||||
|
||||
private _update(_dt: number) {
|
||||
if (!this._initialized) return;
|
||||
|
||||
if (this._editing) {
|
||||
this._updateEditor();
|
||||
} else {
|
||||
this._updateGame();
|
||||
}
|
||||
|
||||
// Zoom
|
||||
const scrollDelta = this.input.consumeScroll();
|
||||
if (scrollDelta !== 0) {
|
||||
this.camera.zoom(scrollDelta > 0 ? 0.9 : 1.1);
|
||||
}
|
||||
|
||||
// Undo/Redo (Ctrl+Z / Ctrl+Y)
|
||||
if (this.input.isKeyDown('KeyZ') && this.input.isKeyDown('ControlLeft')) {
|
||||
if (this.input.isKeyDown('ShiftLeft')) {
|
||||
this.undo.redo(this.tilemap);
|
||||
} else {
|
||||
this.undo.undo(this.tilemap);
|
||||
}
|
||||
}
|
||||
if (this.input.isKeyDown('KeyY') && this.input.isKeyDown('ControlLeft')) {
|
||||
this.undo.redo(this.tilemap);
|
||||
}
|
||||
|
||||
this.camera.update(this.app.screen.width, this.app.screen.height);
|
||||
}
|
||||
|
||||
private _updateGame() {
|
||||
// Player movement
|
||||
let dx = 0;
|
||||
let dy = 0;
|
||||
if (this.input.isKeyDown('KeyW') || this.input.isKeyDown('ArrowUp')) dy = -1;
|
||||
if (this.input.isKeyDown('KeyS') || this.input.isKeyDown('ArrowDown')) dy = 1;
|
||||
if (this.input.isKeyDown('KeyA') || this.input.isKeyDown('ArrowLeft')) dx = -1;
|
||||
if (this.input.isKeyDown('KeyD') || this.input.isKeyDown('ArrowRight')) dx = 1;
|
||||
|
||||
if (this.player) {
|
||||
this.player.move(dx, dy);
|
||||
// Camera follows player smoothly
|
||||
const lerpSpeed = 0.1;
|
||||
const cx = this.camera.x + (this.player.worldX - this.camera.x) * lerpSpeed;
|
||||
const cy = this.camera.y + (this.player.worldY - this.camera.y) * lerpSpeed;
|
||||
this.camera.setPosition(cx, cy);
|
||||
}
|
||||
}
|
||||
|
||||
private _updateEditor() {
|
||||
// Camera pan with WASD in editor mode
|
||||
const moveSpeed = 4;
|
||||
if (this.input.isKeyDown('KeyW') || this.input.isKeyDown('ArrowUp'))
|
||||
this.camera.move(0, -moveSpeed);
|
||||
if (this.input.isKeyDown('KeyS') || this.input.isKeyDown('ArrowDown'))
|
||||
this.camera.move(0, moveSpeed);
|
||||
if (this.input.isKeyDown('KeyA') || this.input.isKeyDown('ArrowLeft'))
|
||||
this.camera.move(-moveSpeed, 0);
|
||||
if (this.input.isKeyDown('KeyD') || this.input.isKeyDown('ArrowRight'))
|
||||
this.camera.move(moveSpeed, 0);
|
||||
|
||||
// Get world position under cursor
|
||||
const worldPos = this.camera.screenToWorld(
|
||||
this.input.mouseX,
|
||||
this.input.mouseY,
|
||||
this.app.screen.width,
|
||||
this.app.screen.height
|
||||
);
|
||||
const tileX = Math.floor(worldPos.x / this.tilemap.tileSize);
|
||||
const tileY = Math.floor(worldPos.y / this.tilemap.tileSize);
|
||||
|
||||
// Handle mouse actions
|
||||
if (this.input.isMouseDown) {
|
||||
const material =
|
||||
this.input.mouseButton === 2 || this._activeTool === 'eraser'
|
||||
? MATERIAL_AIR
|
||||
: this._selectedMaterial;
|
||||
|
||||
if (!this._painting) {
|
||||
// Start a new paint stroke
|
||||
this._painting = true;
|
||||
this.undo.beginBatch();
|
||||
}
|
||||
|
||||
switch (this._activeTool) {
|
||||
case 'brush':
|
||||
case 'eraser':
|
||||
brushStroke(this.tilemap, this.undo, tileX, tileY, material, this._brushSize);
|
||||
break;
|
||||
case 'fill':
|
||||
// Fill only on initial click (not drag)
|
||||
if (this.input.justPressed) {
|
||||
floodFill(this.tilemap, this.undo, tileX, tileY, material);
|
||||
}
|
||||
break;
|
||||
case 'pipette':
|
||||
if (this.input.justPressed) {
|
||||
const picked = pipette(this.tilemap, tileX, tileY);
|
||||
if (picked !== MATERIAL_AIR) {
|
||||
this._selectedMaterial = picked;
|
||||
this._activeTool = 'brush'; // Switch back to brush after pick
|
||||
this.onStateChange?.();
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
} else if (this._painting) {
|
||||
// Mouse released: commit the undo batch
|
||||
this._painting = false;
|
||||
this.undo.commitBatch();
|
||||
}
|
||||
}
|
||||
|
||||
// ─── Public API for UI ──────────────────────────────────
|
||||
|
||||
toggleEditor() {
|
||||
this._editing = !this._editing;
|
||||
this.onStateChange?.();
|
||||
}
|
||||
|
||||
setMaterial(materialId: number) {
|
||||
this._selectedMaterial = materialId;
|
||||
this.onStateChange?.();
|
||||
}
|
||||
|
||||
setTool(tool: ToolType) {
|
||||
this._activeTool = tool;
|
||||
this.onStateChange?.();
|
||||
}
|
||||
|
||||
setBrushSize(size: number) {
|
||||
this._brushSize = Math.max(1, Math.min(9, size));
|
||||
this.onStateChange?.();
|
||||
}
|
||||
|
||||
destroy() {
|
||||
this.player?.destroy();
|
||||
this.input.destroy();
|
||||
this.app.destroy(true);
|
||||
}
|
||||
}
|
||||
95
apps/manavoxel/apps/web/src/lib/engine/input.ts
Normal file
95
apps/manavoxel/apps/web/src/lib/engine/input.ts
Normal file
|
|
@ -0,0 +1,95 @@
|
|||
export class InputManager {
|
||||
private _keys = new Set<string>();
|
||||
private _mouseDown = false;
|
||||
private _mouseButton = 0;
|
||||
private _mouseX = 0;
|
||||
private _mouseY = 0;
|
||||
private _scrollAccumulator = 0;
|
||||
private _justPressed = false;
|
||||
private _element: HTMLElement;
|
||||
|
||||
private _onKeyDown: (e: KeyboardEvent) => void;
|
||||
private _onKeyUp: (e: KeyboardEvent) => void;
|
||||
private _onMouseDown: (e: MouseEvent) => void;
|
||||
private _onMouseUp: (e: MouseEvent) => void;
|
||||
private _onMouseMove: (e: MouseEvent) => void;
|
||||
private _onWheel: (e: WheelEvent) => void;
|
||||
private _onContextMenu: (e: Event) => void;
|
||||
|
||||
get isMouseDown() {
|
||||
return this._mouseDown;
|
||||
}
|
||||
get mouseButton() {
|
||||
return this._mouseButton;
|
||||
}
|
||||
get mouseX() {
|
||||
return this._mouseX;
|
||||
}
|
||||
get mouseY() {
|
||||
return this._mouseY;
|
||||
}
|
||||
/** True only on the first frame the mouse is pressed */
|
||||
get justPressed() {
|
||||
const val = this._justPressed;
|
||||
this._justPressed = false;
|
||||
return val;
|
||||
}
|
||||
|
||||
constructor(element: HTMLElement) {
|
||||
this._element = element;
|
||||
|
||||
this._onKeyDown = (e) => {
|
||||
this._keys.add(e.code);
|
||||
};
|
||||
this._onKeyUp = (e) => {
|
||||
this._keys.delete(e.code);
|
||||
};
|
||||
this._onMouseDown = (e) => {
|
||||
this._mouseDown = true;
|
||||
this._mouseButton = e.button;
|
||||
this._justPressed = true;
|
||||
};
|
||||
this._onMouseUp = () => {
|
||||
this._mouseDown = false;
|
||||
};
|
||||
this._onMouseMove = (e) => {
|
||||
this._mouseX = e.clientX;
|
||||
this._mouseY = e.clientY;
|
||||
};
|
||||
this._onWheel = (e) => {
|
||||
e.preventDefault();
|
||||
this._scrollAccumulator += e.deltaY;
|
||||
};
|
||||
this._onContextMenu = (e) => {
|
||||
e.preventDefault(); // Disable right-click menu
|
||||
};
|
||||
|
||||
window.addEventListener('keydown', this._onKeyDown);
|
||||
window.addEventListener('keyup', this._onKeyUp);
|
||||
element.addEventListener('mousedown', this._onMouseDown);
|
||||
window.addEventListener('mouseup', this._onMouseUp);
|
||||
window.addEventListener('mousemove', this._onMouseMove);
|
||||
element.addEventListener('wheel', this._onWheel, { passive: false });
|
||||
element.addEventListener('contextmenu', this._onContextMenu);
|
||||
}
|
||||
|
||||
isKeyDown(code: string): boolean {
|
||||
return this._keys.has(code);
|
||||
}
|
||||
|
||||
consumeScroll(): number {
|
||||
const val = this._scrollAccumulator;
|
||||
this._scrollAccumulator = 0;
|
||||
return val;
|
||||
}
|
||||
|
||||
destroy() {
|
||||
window.removeEventListener('keydown', this._onKeyDown);
|
||||
window.removeEventListener('keyup', this._onKeyUp);
|
||||
this._element.removeEventListener('mousedown', this._onMouseDown);
|
||||
window.removeEventListener('mouseup', this._onMouseUp);
|
||||
window.removeEventListener('mousemove', this._onMouseMove);
|
||||
this._element.removeEventListener('wheel', this._onWheel);
|
||||
this._element.removeEventListener('contextmenu', this._onContextMenu);
|
||||
}
|
||||
}
|
||||
164
apps/manavoxel/apps/web/src/lib/engine/player.ts
Normal file
164
apps/manavoxel/apps/web/src/lib/engine/player.ts
Normal file
|
|
@ -0,0 +1,164 @@
|
|||
import { Container, Graphics } from 'pixi.js';
|
||||
import type { TilemapRenderer } from './tilemap';
|
||||
|
||||
const PLAYER_WIDTH = 6; // pixels (60cm at 10cm/pixel)
|
||||
const PLAYER_HEIGHT = 8; // pixels (80cm)
|
||||
const PLAYER_SPEED = 1.5; // pixels per frame
|
||||
const PLAYER_COLOR = '#4FC3F7';
|
||||
|
||||
export class Player {
|
||||
x: number;
|
||||
y: number;
|
||||
direction = 2; // 0=up, 1=right, 2=down, 3=left
|
||||
hp = 100;
|
||||
maxHp = 100;
|
||||
|
||||
private _sprite: Container;
|
||||
private _body: Graphics;
|
||||
private _dirIndicator: Graphics;
|
||||
private _tilemap: TilemapRenderer;
|
||||
|
||||
get worldX() {
|
||||
return this.x * this._tilemap.tileSize;
|
||||
}
|
||||
get worldY() {
|
||||
return this.y * this._tilemap.tileSize;
|
||||
}
|
||||
|
||||
constructor(worldContainer: Container, tilemap: TilemapRenderer, startX: number, startY: number) {
|
||||
this._tilemap = tilemap;
|
||||
this.x = startX;
|
||||
this.y = startY;
|
||||
|
||||
this._sprite = new Container();
|
||||
worldContainer.addChild(this._sprite);
|
||||
|
||||
// Body rectangle
|
||||
this._body = new Graphics();
|
||||
this._body.roundRect(
|
||||
0,
|
||||
0,
|
||||
PLAYER_WIDTH * tilemap.tileSize,
|
||||
PLAYER_HEIGHT * tilemap.tileSize,
|
||||
2
|
||||
);
|
||||
this._body.fill(PLAYER_COLOR);
|
||||
this._sprite.addChild(this._body);
|
||||
|
||||
// Direction indicator (small triangle)
|
||||
this._dirIndicator = new Graphics();
|
||||
this._sprite.addChild(this._dirIndicator);
|
||||
|
||||
this._updateSpritePosition();
|
||||
this._updateDirectionIndicator();
|
||||
}
|
||||
|
||||
/**
|
||||
* Move the player by input direction with collision detection.
|
||||
* Returns true if the player actually moved.
|
||||
*/
|
||||
move(dx: number, dy: number): boolean {
|
||||
if (dx === 0 && dy === 0) return false;
|
||||
|
||||
// Normalize diagonal movement
|
||||
if (dx !== 0 && dy !== 0) {
|
||||
const len = Math.sqrt(dx * dx + dy * dy);
|
||||
dx = (dx / len) * PLAYER_SPEED;
|
||||
dy = (dy / len) * PLAYER_SPEED;
|
||||
} else {
|
||||
dx *= PLAYER_SPEED;
|
||||
dy *= PLAYER_SPEED;
|
||||
}
|
||||
|
||||
// Update direction
|
||||
if (Math.abs(dx) > Math.abs(dy)) {
|
||||
this.direction = dx > 0 ? 1 : 3;
|
||||
} else if (dy !== 0) {
|
||||
this.direction = dy > 0 ? 2 : 0;
|
||||
}
|
||||
|
||||
// Try X movement
|
||||
const newX = this.x + dx;
|
||||
if (!this._collides(newX, this.y)) {
|
||||
this.x = newX;
|
||||
}
|
||||
|
||||
// Try Y movement
|
||||
const newY = this.y + dy;
|
||||
if (!this._collides(this.x, newY)) {
|
||||
this.y = newY;
|
||||
}
|
||||
|
||||
this._updateSpritePosition();
|
||||
this._updateDirectionIndicator();
|
||||
return true;
|
||||
}
|
||||
|
||||
/** Check if the player hitbox collides with solid tiles at position (px, py) */
|
||||
private _collides(px: number, py: number): boolean {
|
||||
// Check corners and midpoints of the player hitbox
|
||||
const margin = 0.1; // Small margin to avoid getting stuck
|
||||
const left = px + margin;
|
||||
const right = px + PLAYER_WIDTH - margin;
|
||||
const top = py + margin;
|
||||
const bottom = py + PLAYER_HEIGHT - margin;
|
||||
const midX = px + PLAYER_WIDTH / 2;
|
||||
const midY = py + PLAYER_HEIGHT / 2;
|
||||
|
||||
// Check 8 points around the hitbox
|
||||
return (
|
||||
this._tilemap.isSolid(Math.floor(left), Math.floor(top)) ||
|
||||
this._tilemap.isSolid(Math.floor(right), Math.floor(top)) ||
|
||||
this._tilemap.isSolid(Math.floor(left), Math.floor(bottom)) ||
|
||||
this._tilemap.isSolid(Math.floor(right), Math.floor(bottom)) ||
|
||||
this._tilemap.isSolid(Math.floor(midX), Math.floor(top)) ||
|
||||
this._tilemap.isSolid(Math.floor(midX), Math.floor(bottom)) ||
|
||||
this._tilemap.isSolid(Math.floor(left), Math.floor(midY)) ||
|
||||
this._tilemap.isSolid(Math.floor(right), Math.floor(midY))
|
||||
);
|
||||
}
|
||||
|
||||
private _updateSpritePosition() {
|
||||
this._sprite.x = this.x * this._tilemap.tileSize;
|
||||
this._sprite.y = this.y * this._tilemap.tileSize;
|
||||
}
|
||||
|
||||
private _updateDirectionIndicator() {
|
||||
const g = this._dirIndicator;
|
||||
const ts = this._tilemap.tileSize;
|
||||
const w = PLAYER_WIDTH * ts;
|
||||
const h = PLAYER_HEIGHT * ts;
|
||||
const s = 4; // triangle size
|
||||
|
||||
g.clear();
|
||||
|
||||
switch (this.direction) {
|
||||
case 0: // up
|
||||
g.moveTo(w / 2, -s);
|
||||
g.lineTo(w / 2 - s, 0);
|
||||
g.lineTo(w / 2 + s, 0);
|
||||
break;
|
||||
case 1: // right
|
||||
g.moveTo(w + s, h / 2);
|
||||
g.lineTo(w, h / 2 - s);
|
||||
g.lineTo(w, h / 2 + s);
|
||||
break;
|
||||
case 2: // down
|
||||
g.moveTo(w / 2, h + s);
|
||||
g.lineTo(w / 2 - s, h);
|
||||
g.lineTo(w / 2 + s, h);
|
||||
break;
|
||||
case 3: // left
|
||||
g.moveTo(-s, h / 2);
|
||||
g.lineTo(0, h / 2 - s);
|
||||
g.lineTo(0, h / 2 + s);
|
||||
break;
|
||||
}
|
||||
g.closePath();
|
||||
g.fill('#ffffff');
|
||||
}
|
||||
|
||||
destroy() {
|
||||
this._sprite.destroy({ children: true });
|
||||
}
|
||||
}
|
||||
201
apps/manavoxel/apps/web/src/lib/engine/tilemap.ts
Normal file
201
apps/manavoxel/apps/web/src/lib/engine/tilemap.ts
Normal file
|
|
@ -0,0 +1,201 @@
|
|||
import { Container, Graphics } from 'pixi.js';
|
||||
import { CHUNK_SIZE, MATERIAL_AIR, type Material } from '@manavoxel/shared';
|
||||
|
||||
/**
|
||||
* Chunk-based tilemap renderer.
|
||||
* Each chunk is a 32×32 grid of pixels rendered as a single Graphics object.
|
||||
* Only chunks in view are rendered. Chunks are re-drawn only when dirty.
|
||||
*/
|
||||
|
||||
interface Chunk {
|
||||
cx: number;
|
||||
cy: number;
|
||||
pixels: Uint16Array; // CHUNK_SIZE * CHUNK_SIZE
|
||||
graphics: Graphics;
|
||||
dirty: boolean;
|
||||
}
|
||||
|
||||
export class TilemapRenderer {
|
||||
readonly tileSize = 8; // Screen pixels per world pixel (at 1x zoom)
|
||||
private _container: Container;
|
||||
private _palette: Material[];
|
||||
private _chunks = new Map<string, Chunk>();
|
||||
private _worldWidth = 0;
|
||||
private _worldHeight = 0;
|
||||
|
||||
get worldWidth() {
|
||||
return this._worldWidth;
|
||||
}
|
||||
get worldHeight() {
|
||||
return this._worldHeight;
|
||||
}
|
||||
|
||||
constructor(worldContainer: Container, palette: Material[]) {
|
||||
this._container = new Container();
|
||||
worldContainer.addChild(this._container);
|
||||
this._palette = palette;
|
||||
}
|
||||
|
||||
/** Generate a flat world with grass floor and stone borders */
|
||||
generateFlatWorld(width: number, height: number) {
|
||||
this._worldWidth = width;
|
||||
this._worldHeight = height;
|
||||
|
||||
for (let y = 0; y < height; y++) {
|
||||
for (let x = 0; x < width; x++) {
|
||||
let material = MATERIAL_AIR;
|
||||
|
||||
// Border walls
|
||||
if (x === 0 || x === width - 1 || y === 0 || y === height - 1) {
|
||||
material = 1; // Stone
|
||||
}
|
||||
// Grass floor (bottom third)
|
||||
else if (y > height * 0.7) {
|
||||
material = 3; // Grass
|
||||
}
|
||||
// Dirt under grass
|
||||
else if (y > height * 0.75) {
|
||||
material = 2; // Dirt
|
||||
}
|
||||
// Stone deep underground
|
||||
else if (y > height * 0.85) {
|
||||
material = 1; // Stone
|
||||
}
|
||||
// A few demo buildings
|
||||
else if (x >= 50 && x <= 70 && y >= 150 && y <= 180) {
|
||||
// Small stone house
|
||||
if (x === 50 || x === 70 || y === 150 || y === 180) {
|
||||
material = 8; // Brick
|
||||
} else if (y === 180 && x >= 58 && x <= 62) {
|
||||
material = MATERIAL_AIR; // Door opening
|
||||
} else if (y === 155 && (x === 55 || x === 65)) {
|
||||
material = 9; // Glass windows
|
||||
}
|
||||
}
|
||||
// A wooden platform
|
||||
else if (x >= 100 && x <= 130 && y === 170) {
|
||||
material = 5; // Plank
|
||||
}
|
||||
// Some trees (simple: trunk + leaves)
|
||||
else if (x === 200 && y >= 160 && y <= 170) {
|
||||
material = 4; // Wood trunk
|
||||
} else if (
|
||||
x >= 196 &&
|
||||
x <= 204 &&
|
||||
y >= 155 &&
|
||||
y <= 162 &&
|
||||
Math.abs(x - 200) + Math.abs(y - 158) <= 5
|
||||
) {
|
||||
material = 13; // Leaves
|
||||
}
|
||||
|
||||
if (material !== MATERIAL_AIR) {
|
||||
this._setPixelRaw(x, y, material);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Mark all chunks dirty for initial render
|
||||
for (const chunk of this._chunks.values()) {
|
||||
chunk.dirty = true;
|
||||
}
|
||||
this._renderDirtyChunks();
|
||||
}
|
||||
|
||||
/** Set a pixel and mark chunk dirty */
|
||||
setPixel(x: number, y: number, material: number) {
|
||||
if (x < 0 || x >= this._worldWidth || y < 0 || y >= this._worldHeight) return;
|
||||
this._setPixelRaw(x, y, material);
|
||||
|
||||
const key = this._chunkKey(Math.floor(x / CHUNK_SIZE), Math.floor(y / CHUNK_SIZE));
|
||||
const chunk = this._chunks.get(key);
|
||||
if (chunk) {
|
||||
chunk.dirty = true;
|
||||
this._renderChunk(chunk);
|
||||
}
|
||||
}
|
||||
|
||||
/** Get pixel material at world position */
|
||||
getPixel(x: number, y: number): number {
|
||||
const cx = Math.floor(x / CHUNK_SIZE);
|
||||
const cy = Math.floor(y / CHUNK_SIZE);
|
||||
const chunk = this._chunks.get(this._chunkKey(cx, cy));
|
||||
if (!chunk) return MATERIAL_AIR;
|
||||
|
||||
const lx = x - cx * CHUNK_SIZE;
|
||||
const ly = y - cy * CHUNK_SIZE;
|
||||
return chunk.pixels[ly * CHUNK_SIZE + lx];
|
||||
}
|
||||
|
||||
/** Check if a world pixel is solid (for collision) */
|
||||
isSolid(x: number, y: number): boolean {
|
||||
const mat = this.getPixel(x, y);
|
||||
return this._palette[mat]?.solid ?? false;
|
||||
}
|
||||
|
||||
private _setPixelRaw(x: number, y: number, material: number) {
|
||||
const cx = Math.floor(x / CHUNK_SIZE);
|
||||
const cy = Math.floor(y / CHUNK_SIZE);
|
||||
const key = this._chunkKey(cx, cy);
|
||||
|
||||
let chunk = this._chunks.get(key);
|
||||
if (!chunk) {
|
||||
chunk = this._createChunk(cx, cy);
|
||||
this._chunks.set(key, chunk);
|
||||
}
|
||||
|
||||
const lx = x - cx * CHUNK_SIZE;
|
||||
const ly = y - cy * CHUNK_SIZE;
|
||||
chunk.pixels[ly * CHUNK_SIZE + lx] = material;
|
||||
chunk.dirty = true;
|
||||
}
|
||||
|
||||
private _createChunk(cx: number, cy: number): Chunk {
|
||||
const graphics = new Graphics();
|
||||
graphics.x = cx * CHUNK_SIZE * this.tileSize;
|
||||
graphics.y = cy * CHUNK_SIZE * this.tileSize;
|
||||
this._container.addChild(graphics);
|
||||
|
||||
return {
|
||||
cx,
|
||||
cy,
|
||||
pixels: new Uint16Array(CHUNK_SIZE * CHUNK_SIZE),
|
||||
graphics,
|
||||
dirty: true,
|
||||
};
|
||||
}
|
||||
|
||||
private _renderDirtyChunks() {
|
||||
for (const chunk of this._chunks.values()) {
|
||||
if (chunk.dirty) {
|
||||
this._renderChunk(chunk);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private _renderChunk(chunk: Chunk) {
|
||||
const g = chunk.graphics;
|
||||
g.clear();
|
||||
|
||||
const ts = this.tileSize;
|
||||
|
||||
for (let ly = 0; ly < CHUNK_SIZE; ly++) {
|
||||
for (let lx = 0; lx < CHUNK_SIZE; lx++) {
|
||||
const mat = chunk.pixels[ly * CHUNK_SIZE + lx];
|
||||
if (mat === MATERIAL_AIR) continue;
|
||||
|
||||
const material = this._palette[mat];
|
||||
if (!material) continue;
|
||||
|
||||
g.rect(lx * ts, ly * ts, ts, ts);
|
||||
g.fill(material.color);
|
||||
}
|
||||
}
|
||||
|
||||
chunk.dirty = false;
|
||||
}
|
||||
|
||||
private _chunkKey(cx: number, cy: number): string {
|
||||
return `${cx},${cy}`;
|
||||
}
|
||||
}
|
||||
7
apps/manavoxel/apps/web/src/routes/+layout.svelte
Normal file
7
apps/manavoxel/apps/web/src/routes/+layout.svelte
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
<script lang="ts">
|
||||
import '../app.css';
|
||||
|
||||
let { children } = $props();
|
||||
</script>
|
||||
|
||||
{@render children()}
|
||||
2
apps/manavoxel/apps/web/src/routes/+layout.ts
Normal file
2
apps/manavoxel/apps/web/src/routes/+layout.ts
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
// Disable SSR — all data is local-first (IndexedDB via Dexie.js)
|
||||
export const ssr = false;
|
||||
205
apps/manavoxel/apps/web/src/routes/+page.svelte
Normal file
205
apps/manavoxel/apps/web/src/routes/+page.svelte
Normal file
|
|
@ -0,0 +1,205 @@
|
|||
<script lang="ts">
|
||||
import { onMount } from 'svelte';
|
||||
import { GameEngine } from '$lib/engine/game';
|
||||
import { DEFAULT_MATERIALS, MATERIAL_AIR } from '@manavoxel/shared';
|
||||
import type { ToolType } from '$lib/editor/tools';
|
||||
|
||||
let canvasContainer: HTMLDivElement;
|
||||
let engine: GameEngine | null = $state(null);
|
||||
let isEditing = $state(false);
|
||||
let selectedMaterial = $state(1);
|
||||
let activeTool = $state<ToolType>('brush');
|
||||
let brushSize = $state(1);
|
||||
|
||||
const tools: { id: ToolType; label: string; key: string }[] = [
|
||||
{ id: 'brush', label: 'Brush', key: 'B' },
|
||||
{ id: 'eraser', label: 'Eraser', key: 'E' },
|
||||
{ id: 'fill', label: 'Fill', key: 'G' },
|
||||
{ id: 'pipette', label: 'Pick', key: 'I' },
|
||||
];
|
||||
|
||||
const materials = DEFAULT_MATERIALS.filter((m) => m.id !== MATERIAL_AIR);
|
||||
|
||||
onMount(() => {
|
||||
const e = new GameEngine(canvasContainer);
|
||||
engine = e;
|
||||
|
||||
e.onStateChange = () => {
|
||||
isEditing = e.isEditing;
|
||||
selectedMaterial = e.selectedMaterial;
|
||||
activeTool = e.activeTool;
|
||||
brushSize = e.brushSize;
|
||||
};
|
||||
|
||||
// Keyboard shortcuts
|
||||
const onKey = (ev: KeyboardEvent) => {
|
||||
if (ev.target instanceof HTMLInputElement) return;
|
||||
switch (ev.key.toLowerCase()) {
|
||||
case 'tab':
|
||||
ev.preventDefault();
|
||||
e.toggleEditor();
|
||||
break;
|
||||
case 'b':
|
||||
e.setTool('brush');
|
||||
break;
|
||||
case 'e':
|
||||
e.setTool('eraser');
|
||||
break;
|
||||
case 'g':
|
||||
e.setTool('fill');
|
||||
break;
|
||||
case 'i':
|
||||
e.setTool('pipette');
|
||||
break;
|
||||
case '[':
|
||||
e.setBrushSize(e.brushSize - 2);
|
||||
break;
|
||||
case ']':
|
||||
e.setBrushSize(e.brushSize + 2);
|
||||
break;
|
||||
}
|
||||
// Number keys 1-9 select materials
|
||||
const num = parseInt(ev.key);
|
||||
if (num >= 1 && num <= 9 && num <= materials.length) {
|
||||
e.setMaterial(materials[num - 1].id);
|
||||
}
|
||||
};
|
||||
window.addEventListener('keydown', onKey);
|
||||
|
||||
return () => {
|
||||
window.removeEventListener('keydown', onKey);
|
||||
e.destroy();
|
||||
};
|
||||
});
|
||||
</script>
|
||||
|
||||
<div class="relative h-screen w-screen overflow-hidden bg-gray-900">
|
||||
<!-- PixiJS Canvas -->
|
||||
<div bind:this={canvasContainer} class="game-canvas h-full w-full"></div>
|
||||
|
||||
<!-- HUD Overlay -->
|
||||
<div class="pointer-events-none absolute inset-0">
|
||||
<!-- Top bar -->
|
||||
<div class="pointer-events-auto flex items-center justify-between p-3">
|
||||
<div class="flex items-center gap-3">
|
||||
<div
|
||||
class="rounded-lg bg-gray-800/80 px-3 py-1.5 text-sm font-medium text-white backdrop-blur"
|
||||
>
|
||||
ManaVoxel
|
||||
</div>
|
||||
{#if !isEditing && engine?.player}
|
||||
<div class="rounded-lg bg-gray-800/80 px-3 py-1.5 text-xs text-gray-300 backdrop-blur">
|
||||
HP: {engine.player.hp}/{engine.player.maxHp}
|
||||
</div>
|
||||
{/if}
|
||||
{#if isEditing}
|
||||
<div class="rounded-lg bg-emerald-600/80 px-2 py-1 text-xs text-white backdrop-blur">
|
||||
EDITOR
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
<div class="flex gap-2">
|
||||
<button
|
||||
class="rounded-lg px-3 py-1.5 text-sm text-white backdrop-blur transition {isEditing
|
||||
? 'bg-emerald-600/80 hover:bg-emerald-500/80'
|
||||
: 'bg-gray-800/80 hover:bg-gray-700/80'}"
|
||||
onclick={() => engine?.toggleEditor()}
|
||||
>
|
||||
{isEditing ? 'Play' : 'Edit'}
|
||||
<span class="ml-1 text-xs text-gray-400">Tab</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Editor Tools (left side) -->
|
||||
{#if isEditing}
|
||||
<div class="pointer-events-auto absolute left-3 top-16 flex flex-col gap-1">
|
||||
{#each tools as tool}
|
||||
<button
|
||||
class="flex items-center gap-2 rounded-lg px-3 py-1.5 text-xs text-white backdrop-blur transition {activeTool ===
|
||||
tool.id
|
||||
? 'bg-emerald-600/80'
|
||||
: 'bg-gray-800/80 hover:bg-gray-700/80'}"
|
||||
onclick={() => engine?.setTool(tool.id)}
|
||||
>
|
||||
{tool.label}
|
||||
<span class="text-gray-500">{tool.key}</span>
|
||||
</button>
|
||||
{/each}
|
||||
|
||||
<!-- Brush size -->
|
||||
<div class="mt-2 rounded-lg bg-gray-800/80 px-3 py-2 text-xs text-white backdrop-blur">
|
||||
<div class="mb-1 text-gray-400">Size: {brushSize}px</div>
|
||||
<div class="flex gap-1">
|
||||
{#each [1, 3, 5, 7] as size}
|
||||
<button
|
||||
class="rounded px-2 py-0.5 transition {brushSize === size
|
||||
? 'bg-emerald-600'
|
||||
: 'bg-gray-700 hover:bg-gray-600'}"
|
||||
onclick={() => engine?.setBrushSize(size)}
|
||||
>
|
||||
{size}
|
||||
</button>
|
||||
{/each}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Undo/Redo -->
|
||||
<div class="mt-2 flex gap-1">
|
||||
<button
|
||||
class="rounded-lg bg-gray-800/80 px-3 py-1.5 text-xs text-white backdrop-blur hover:bg-gray-700/80 disabled:opacity-30"
|
||||
disabled={!engine?.undo.canUndo}
|
||||
onclick={() => engine?.undo.undo(engine.tilemap)}
|
||||
>
|
||||
Undo
|
||||
</button>
|
||||
<button
|
||||
class="rounded-lg bg-gray-800/80 px-3 py-1.5 text-xs text-white backdrop-blur hover:bg-gray-700/80 disabled:opacity-30"
|
||||
disabled={!engine?.undo.canRedo}
|
||||
onclick={() => engine?.undo.redo(engine.tilemap)}
|
||||
>
|
||||
Redo
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<!-- Material Palette (bottom) -->
|
||||
{#if isEditing}
|
||||
<div class="pointer-events-auto absolute bottom-4 left-1/2 -translate-x-1/2">
|
||||
<div class="flex gap-1 rounded-lg bg-gray-800/90 p-2 backdrop-blur">
|
||||
{#each materials as mat, i}
|
||||
<button
|
||||
class="group relative h-8 w-8 rounded border-2 transition-transform hover:scale-110 {selectedMaterial ===
|
||||
mat.id
|
||||
? 'border-white scale-110'
|
||||
: 'border-transparent'}"
|
||||
style="background-color: {mat.color}"
|
||||
onclick={() => engine?.setMaterial(mat.id)}
|
||||
title="{mat.name} ({i + 1})"
|
||||
>
|
||||
{#if i < 9}
|
||||
<span
|
||||
class="absolute -top-4 left-1/2 -translate-x-1/2 text-[10px] text-gray-500 opacity-0 group-hover:opacity-100"
|
||||
>
|
||||
{i + 1}
|
||||
</span>
|
||||
{/if}
|
||||
</button>
|
||||
{/each}
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<!-- Controls hint (bottom left) -->
|
||||
<div class="pointer-events-auto absolute bottom-4 left-4">
|
||||
<div class="rounded-lg bg-gray-800/60 px-3 py-1.5 text-[10px] text-gray-500 backdrop-blur">
|
||||
{#if isEditing}
|
||||
WASD: Pan | Scroll: Zoom | LClick: Place | RClick: Erase | 1-9: Material
|
||||
{:else}
|
||||
WASD: Move | Scroll: Zoom | Tab: Editor
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
14
apps/manavoxel/apps/web/svelte.config.js
Normal file
14
apps/manavoxel/apps/web/svelte.config.js
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
import adapter from '@sveltejs/adapter-node';
|
||||
import { vitePreprocess } from '@sveltejs/vite-plugin-svelte';
|
||||
|
||||
/** @type {import('@sveltejs/kit').Config} */
|
||||
const config = {
|
||||
preprocess: vitePreprocess(),
|
||||
kit: {
|
||||
adapter: adapter({
|
||||
out: 'build',
|
||||
}),
|
||||
},
|
||||
};
|
||||
|
||||
export default config;
|
||||
14
apps/manavoxel/apps/web/tsconfig.json
Normal file
14
apps/manavoxel/apps/web/tsconfig.json
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
{
|
||||
"extends": "./.svelte-kit/tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"allowJs": true,
|
||||
"checkJs": true,
|
||||
"esModuleInterop": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"resolveJsonModule": true,
|
||||
"skipLibCheck": true,
|
||||
"sourceMap": true,
|
||||
"strict": true,
|
||||
"moduleResolution": "bundler"
|
||||
}
|
||||
}
|
||||
55
apps/manavoxel/apps/web/vite.config.ts
Normal file
55
apps/manavoxel/apps/web/vite.config.ts
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
/// <reference types="vitest/config" />
|
||||
import { sveltekit } from '@sveltejs/kit/vite';
|
||||
import tailwindcss from '@tailwindcss/vite';
|
||||
import { defineConfig } from 'vite';
|
||||
import { SvelteKitPWA } from '@vite-pwa/sveltekit';
|
||||
import { createPWAConfig } from '@manacore/shared-pwa';
|
||||
import { MANACORE_SHARED_PACKAGES, getBuildDefines } from '@manacore/shared-vite-config';
|
||||
|
||||
export default defineConfig({
|
||||
plugins: [
|
||||
tailwindcss(),
|
||||
sveltekit(),
|
||||
SvelteKitPWA(
|
||||
createPWAConfig({
|
||||
name: 'ManaVoxel - Pixel Worlds',
|
||||
shortName: 'ManaVoxel',
|
||||
description: 'Create and program detailed pixel worlds in your browser',
|
||||
themeColor: '#10b981',
|
||||
devEnabled: false,
|
||||
shortcuts: [
|
||||
{
|
||||
name: 'My Worlds',
|
||||
short_name: 'Worlds',
|
||||
description: 'Open your worlds',
|
||||
url: '/worlds',
|
||||
},
|
||||
{
|
||||
name: 'Discover',
|
||||
short_name: 'Discover',
|
||||
description: 'Discover community worlds',
|
||||
url: '/worlds?tab=discover',
|
||||
},
|
||||
],
|
||||
})
|
||||
),
|
||||
],
|
||||
server: {
|
||||
port: 5195,
|
||||
strictPort: true,
|
||||
},
|
||||
ssr: {
|
||||
noExternal: [...MANACORE_SHARED_PACKAGES, '@manavoxel/shared'],
|
||||
},
|
||||
optimizeDeps: {
|
||||
exclude: [...MANACORE_SHARED_PACKAGES, '@manavoxel/shared'],
|
||||
},
|
||||
test: {
|
||||
environment: 'jsdom',
|
||||
include: ['src/**/*.test.ts'],
|
||||
globals: true,
|
||||
},
|
||||
define: {
|
||||
...getBuildDefines(),
|
||||
},
|
||||
});
|
||||
14
apps/manavoxel/package.json
Normal file
14
apps/manavoxel/package.json
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
{
|
||||
"name": "manavoxel",
|
||||
"version": "1.0.0",
|
||||
"private": true,
|
||||
"description": "ManaVoxel - 2D Pixel Platform for creating and programming miniature worlds",
|
||||
"scripts": {
|
||||
"dev": "pnpm run --filter=@manavoxel/* --parallel dev",
|
||||
"dev:web": "pnpm --filter @manavoxel/web dev"
|
||||
},
|
||||
"devDependencies": {
|
||||
"typescript": "^5.9.3"
|
||||
},
|
||||
"packageManager": "pnpm@9.15.0"
|
||||
}
|
||||
14
apps/manavoxel/packages/shared/package.json
Normal file
14
apps/manavoxel/packages/shared/package.json
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
{
|
||||
"name": "@manavoxel/shared",
|
||||
"version": "1.0.0",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"main": "src/index.ts",
|
||||
"types": "src/index.ts",
|
||||
"scripts": {
|
||||
"type-check": "tsc --noEmit"
|
||||
},
|
||||
"devDependencies": {
|
||||
"typescript": "^5.9.3"
|
||||
}
|
||||
}
|
||||
1
apps/manavoxel/packages/shared/src/index.ts
Normal file
1
apps/manavoxel/packages/shared/src/index.ts
Normal file
|
|
@ -0,0 +1 @@
|
|||
export * from './types';
|
||||
204
apps/manavoxel/packages/shared/src/types.ts
Normal file
204
apps/manavoxel/packages/shared/src/types.ts
Normal file
|
|
@ -0,0 +1,204 @@
|
|||
// ─── Materials ───────────────────────────────────────────────
|
||||
|
||||
export interface Material {
|
||||
id: number;
|
||||
name: string;
|
||||
color: string; // Hex
|
||||
solid: boolean;
|
||||
transparent: boolean;
|
||||
emissive: boolean;
|
||||
}
|
||||
|
||||
export const MATERIAL_AIR = 0;
|
||||
|
||||
export const DEFAULT_MATERIALS: Material[] = [
|
||||
{ id: 0, name: 'Air', color: '#000000', solid: false, transparent: true, emissive: false },
|
||||
{ id: 1, name: 'Stone', color: '#808080', solid: true, transparent: false, emissive: false },
|
||||
{ id: 2, name: 'Dirt', color: '#8B6914', solid: true, transparent: false, emissive: false },
|
||||
{ id: 3, name: 'Grass', color: '#4CAF50', solid: true, transparent: false, emissive: false },
|
||||
{ id: 4, name: 'Wood', color: '#A0522D', solid: true, transparent: false, emissive: false },
|
||||
{ id: 5, name: 'Plank', color: '#DEB887', solid: true, transparent: false, emissive: false },
|
||||
{ id: 6, name: 'Sand', color: '#F4E3B2', solid: true, transparent: false, emissive: false },
|
||||
{ id: 7, name: 'Water', color: '#4FC3F7', solid: false, transparent: true, emissive: false },
|
||||
{ id: 8, name: 'Brick', color: '#B71C1C', solid: true, transparent: false, emissive: false },
|
||||
{ id: 9, name: 'Glass', color: '#E0F7FA', solid: true, transparent: true, emissive: false },
|
||||
{ id: 10, name: 'Torch', color: '#FFD54F', solid: false, transparent: true, emissive: true },
|
||||
{ id: 11, name: 'Metal', color: '#B0BEC5', solid: true, transparent: false, emissive: false },
|
||||
{ id: 12, name: 'Cobble', color: '#9E9E9E', solid: true, transparent: false, emissive: false },
|
||||
{ id: 13, name: 'Leaf', color: '#2E7D32', solid: true, transparent: false, emissive: false },
|
||||
{ id: 14, name: 'Roof', color: '#5D4037', solid: true, transparent: false, emissive: false },
|
||||
{ id: 15, name: 'Snow', color: '#FAFAFA', solid: true, transparent: false, emissive: false },
|
||||
];
|
||||
|
||||
// ─── Pixel Grid (per chunk) ─────────────────────────────────
|
||||
|
||||
export const CHUNK_SIZE = 32;
|
||||
|
||||
// ─── Areas ──────────────────────────────────────────────────
|
||||
|
||||
export type AreaType = 'street' | 'interior';
|
||||
|
||||
export interface PortalDef {
|
||||
id: string;
|
||||
x: number;
|
||||
y: number;
|
||||
floor: number;
|
||||
targetAreaId: string;
|
||||
targetX: number;
|
||||
targetY: number;
|
||||
targetFloor: number;
|
||||
requiresKey?: string; // Item ID
|
||||
}
|
||||
|
||||
export interface EntityDef {
|
||||
id: string;
|
||||
type: 'npc' | 'item' | 'light' | 'spawn';
|
||||
x: number;
|
||||
y: number;
|
||||
floor: number;
|
||||
spriteId?: string;
|
||||
properties?: Record<string, unknown>;
|
||||
}
|
||||
|
||||
export interface Area {
|
||||
id: string;
|
||||
worldId: string;
|
||||
name: string;
|
||||
type: AreaType;
|
||||
resolution: number; // 0.10 (street) or 0.05 (interior)
|
||||
width: number; // in pixels
|
||||
height: number;
|
||||
floors: number;
|
||||
pixelData: Uint8Array; // RLE compressed: floors × height × width
|
||||
palette: Material[];
|
||||
entities: EntityDef[];
|
||||
portals: PortalDef[];
|
||||
spawnPoint: { x: number; y: number; floor: number };
|
||||
createdAt?: string;
|
||||
updatedAt?: string;
|
||||
deletedAt?: string;
|
||||
}
|
||||
|
||||
// ─── Worlds ─────────────────────────────────────────────────
|
||||
|
||||
export interface World {
|
||||
id: string;
|
||||
creatorId: string;
|
||||
name: string;
|
||||
description: string;
|
||||
isPublished: boolean;
|
||||
playCount: number;
|
||||
startAreaId: string;
|
||||
settings: Record<string, unknown>;
|
||||
createdAt?: string;
|
||||
updatedAt?: string;
|
||||
deletedAt?: string;
|
||||
}
|
||||
|
||||
// ─── Items ──────────────────────────────────────────────────
|
||||
|
||||
export type Rarity = 'common' | 'uncommon' | 'rare' | 'epic' | 'legendary';
|
||||
export type ElementType = 'neutral' | 'fire' | 'ice' | 'poison' | 'lightning';
|
||||
|
||||
export interface ItemProperties {
|
||||
damage: number;
|
||||
range: number;
|
||||
speed: number;
|
||||
durabilityMax: number;
|
||||
durabilityCurrent: number;
|
||||
element: ElementType;
|
||||
rarity: Rarity;
|
||||
sound: string;
|
||||
particle: string;
|
||||
}
|
||||
|
||||
export interface TriggerAction {
|
||||
trigger: { type: string; params: Record<string, unknown> };
|
||||
conditions?: { type: string; params: Record<string, unknown> }[];
|
||||
actions: { type: string; params: Record<string, unknown> }[];
|
||||
}
|
||||
|
||||
export interface Item {
|
||||
id: string;
|
||||
creatorId: string;
|
||||
name: string;
|
||||
description: string;
|
||||
spriteData: Uint8Array; // Raw pixel data (RGBA or indexed)
|
||||
spriteWidth: number;
|
||||
spriteHeight: number;
|
||||
animationFrames: number;
|
||||
resolution: number; // 0.01 for detail items
|
||||
properties: ItemProperties;
|
||||
behavior: TriggerAction[];
|
||||
script?: string;
|
||||
wasmBinary?: Uint8Array;
|
||||
rarity: Rarity;
|
||||
capabilities: string[];
|
||||
isPublished: boolean;
|
||||
createdAt?: string;
|
||||
updatedAt?: string;
|
||||
deletedAt?: string;
|
||||
}
|
||||
|
||||
// ─── Inventory ──────────────────────────────────────────────
|
||||
|
||||
export interface InventorySlot {
|
||||
id: string;
|
||||
playerId: string;
|
||||
itemId: string;
|
||||
slot: number;
|
||||
quantity: number;
|
||||
instanceData: Record<string, unknown>; // durability state etc.
|
||||
createdAt?: string;
|
||||
updatedAt?: string;
|
||||
deletedAt?: string;
|
||||
}
|
||||
|
||||
// ─── Network Protocol ───────────────────────────────────────
|
||||
|
||||
export type ClientMessage =
|
||||
| { type: 'join'; worldId: string; areaId: string }
|
||||
| { type: 'move'; x: number; y: number; direction: number }
|
||||
| { type: 'setPixel'; x: number; y: number; floor: number; material: number }
|
||||
| { type: 'useItem'; itemId: string; targetX: number; targetY: number }
|
||||
| { type: 'enterPortal'; portalId: string }
|
||||
| { type: 'chat'; message: string }
|
||||
| { type: 'ping' };
|
||||
|
||||
export type ServerMessage =
|
||||
| { type: 'welcome'; playerId: string; areaState: Area; players: PlayerState[] }
|
||||
| { type: 'playerJoin'; player: PlayerState }
|
||||
| { type: 'playerLeave'; playerId: string }
|
||||
| { type: 'playerMove'; playerId: string; x: number; y: number; direction: number }
|
||||
| {
|
||||
type: 'pixelChanged';
|
||||
x: number;
|
||||
y: number;
|
||||
floor: number;
|
||||
material: number;
|
||||
playerId: string;
|
||||
}
|
||||
| { type: 'itemUsed'; playerId: string; itemId: string; effects: Effect[] }
|
||||
| { type: 'areaTransition'; areaId: string; areaState: Area; players: PlayerState[] }
|
||||
| { type: 'chat'; playerId: string; name: string; message: string }
|
||||
| { type: 'error'; message: string }
|
||||
| { type: 'pong' };
|
||||
|
||||
export interface PlayerState {
|
||||
id: string;
|
||||
name: string;
|
||||
x: number;
|
||||
y: number;
|
||||
floor: number;
|
||||
direction: number; // 0=up, 1=right, 2=down, 3=left
|
||||
heldItemId?: string;
|
||||
hp: number;
|
||||
maxHp: number;
|
||||
}
|
||||
|
||||
export interface Effect {
|
||||
type: 'damage' | 'heal' | 'particle' | 'sound' | 'pixelDestroy';
|
||||
x: number;
|
||||
y: number;
|
||||
params: Record<string, unknown>;
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue