Konzeptphase. Interne Planungsfassung, Stand 04.11.2026. Pocket Planet ist noch in Entwicklung – Inhalte, Zahlen und Abläufe können sich ändern.
Ziel: spielerisch glaubwürdig, nachvollziehbar, deterministisch, billig. Kein Teil der Simulation kennt Nodes oder Grafik. Alles läuft als reine GDScript-Klassen (RefCounted) und ist headless testbar (11).
3.1 Raster: 642 Regionen
[Vorschlag] Geodätisches Raster aus einem Ikosaeder, 3-mal unterteilt: 642 Regionen (630 Sechsecke + 12 Fünfecke), je Region 5–6 Nachbarn.
| Kandidat | Regionen | Bewertung |
|---|---|---|
| Unterteilung 2 | 162 | zu grob, Küstenlinien eckig |
| Unterteilung 3 | 642 | Kontinente und Inseln gut formbar, Simulation sehr billig |
| Unterteilung 4 | 2562 | schönere Details, aber 4× Rechenzeit offline |
Die Darstellung ist feiner als das Raster: Das Planeten-Mesh hat 10 242 (niedrig) bzw. 40 962 (hoch) Vertices, jede Vertex kennt ihre Region (UV2). Übergänge zwischen Regionen werden im Shader weich gemischt (07).
3.2 Weltzustand (Datenlayout)
Alle Regionsfelder sind PackedFloat32Array/PackedByteArray mit 642 Einträgen (Structure of Arrays). Das ist cachefreundlich, schnell zu speichern und direkt in eine Datentextur zu kopieren.
| Feld | Typ | Bereich | Bedeutung |
|---|---|---|---|
height | f32 | −4 … +6 Stufen | Höhe relativ zum Meeresspiegel 0 |
temp | f32 | °C, −40 … +60 | berechnet |
moist | f32 | 0 … 1 | Feuchte |
veg | f32 | 0 … 1 | Vegetationsdecke |
ice | f32 | 0 … 1 | Eisbedeckung |
minerals | u8 | 0 … 3 | Rohstoffe (Vulkane, Gesteinsmeteoriten, Gebirge) |
biome | u8 | Enum | abgeleitet, s. 3.5 |
fertility | f32 | 0 … 1 | Bodenfruchtbarkeit (Vulkanasche, Flussdelta) |
river | u8 | 0/1 | Fluss fließt hindurch |
protected_until | i32 | Zyklus | Schutzgebiet |
pollution | f32 | 0 … 1 | ab Epoche 6 |
dirty | u8 | Bit | Darstellung muss aktualisieren |
Global: cycle, epoch, sea_level, base_temp_offset (Spieler-Regler), co2 (0–2, 1 = Normal), atmosphere (0–1, Epoche 1–2), oxygen (0–1), seed (u64), rng_state.
Speicherbedarf: 642 × ~40 Byte ≈ 26 KB pro Planet, komprimiert ≈ 8 KB.
3.3 Zyklus-Reihenfolge
step(cycle):
1 apply_queued_actions() # Spieleraktionen dieses Zyklus (Command-Liste)
2 climate.update() # Temperatur, Eis
3 hydrology.update() # Feuchte, Flüsse, Meeresspiegel
4 terrain.update() # laufende Verformungen, Erosion (alle 10 Zyklen)
5 biomes.classify() # nur Regionen mit Änderung > Schwelle
6 vegetation.update()
7 life.update() # Populationen; alle 20 Zyklen: Mutation, Wanderung
8 civ.update() # alle 5 Zyklen: Wachstum; alle 10: Ereignisprüfung
9 events.update() # geplante/ausgelöste Ereignisse
10 progression.check() # Epochen- und Zielbedingungen
11 chronicle.flush() # neue Einträge
→ TickReport { events[], dirty_regions[], kpis }
Budget [Vorschlag]: ≤ 4 ms pro Zyklus auf dem schwachen Referenzgerät (Samsung Galaxy A13, 12), gemessen ab T-06. Da ein Zyklus aktiv nur alle 2 s (Zeitraffer 0,67 s) läuft, sind das < 1 % der Rechenzeit. Die Simulation läuft trotzdem im WorkerThreadPool, damit kein Frame stockt; die Darstellung liest nur abgeschlossene Zustände (Doppelpuffer).
3.4 Klima
Temperatur je Region:
temp = T_breite(lat) # +28 °C Äquator … −22 °C Pol, Kosinusverlauf
+ base_temp_offset # Klima-Regler des Spielers (±10)
+ 6 · (co2 − 1) # Treibhaus: Vulkane +, Pflanzen −, Industrie +
− 4 · max(height, 0) # Höhenkälte je Stufe
+ ozean_daempfung # Küsten ±3 °C näher an 12 °C
− 8 · global_ice_fraction # Eis-Albedo-Rückkopplung (Eiszeit-Gefahr!)
+ ereignis_offsets # Aschewinter, Hitzewelle …
Zusätzlich wird temp je Zyklus nur zu 20 % an den Zielwert angenähert (Trägheit). So sieht der Spieler Klimawandel kommen, statt dass er springt.
- Eis:
ice→ 1 bei temp < −5 °C, → 0 bei temp > 2 °C (je 5 % pro Zyklus). - CO₂-Haushalt: +0,02 je aktivem Vulkan und Zyklus, −0,001 × globale Vegetation, +pollution (Epoche 6). Klemmt auf 0,3–2,0.
- Eiszeit-Kipppunkt: Steigt
global_ice_fractionüber 0,35, verstärkt die Rückkopplung sich selbst → Ereignis „Eiszeit“ (§3.6). Das ist bewusst ein spielbarer Kipppunkt, den die Vorschau ankündigt („Achtung: Eis nähert sich dem Kipppunkt“).
3.5 Wasser, Wetter, Biome
Feuchte: Grundwert aus Breitenzonen (Tropen 0,8 · Wendekreise 0,25 · gemäßigt 0,6 · polar 0,3), dazu Nähe zum Ozean (+0,2 bei Küste), Regenschatten (Westwind-Modell: Region hinter Gebirge −0,3) und Spielerregen (zerfällt 3 % pro Zyklus).
Meeresspiegel: Wassermenge water (global) wird auf die Senken verteilt: sea_level = Höhe, bei der das Volumen der Regionen darunter water entspricht (Bisektion, 12 Schritte, nur bei Änderung). Eis bindet Wasser → Meeresspiegel sinkt in Eiszeiten (Landbrücken! → Artenwanderung).
Flüsse [Vorschlag]: Keine Fluidsimulation. Regionen mit moist > 0,55 und Gefälle leiten zum tiefsten Nachbarn; erreicht eine Kette den Ozean, wird sie als Fluss markiert. Neuberechnung alle 10 Zyklen. Flussregionen: fertility +0,2, Siedlungsbonus.
Biome (Whittaker-Tabelle, Höhe vor Klima):
| Biom | Bedingung (vereinfacht) | MVP | Farbe (Basis) |
|---|---|---|---|
| Tiefsee | height ≤ −2 | ✓ | #1E4E8C |
| Flachwasser/Küste | −2 < height ≤ 0 | ✓ | #3FA7D6 |
| Eis | ice > 0,6 | ✓ | #EAF4FA |
| Tundra | temp < 2 | ✓ | #9DB4A0 |
| Gebirge | height ≥ 4 | ✓ | #8A7F78 |
| Wüste | moist < 0,25 | ✓ | #E3C27A |
| Grasland/Savanne | moist 0,25–0,5 | ✓ | #B9CF6A |
| Wald | moist 0,5–0,75, temp 2–24 | ✓ | #4F9A58 |
| Regenwald | moist > 0,75, temp > 24 | Phase 3 | #2E7D4F |
| Sumpf | moist > 0,85, height ≤ 1 | Phase 3 | #5E7F5A |
| Korallenriff | Flachwasser, temp > 22 | Phase 3 | #FF9E80 |
| Vulkanland | aktiver Vulkan / frische Lava | ✓ (Zustand) | #3A2A28 + Glut |
Vegetation: veg nähert sich der Biom-Kapazität (Wald 0,9, Grasland 0,6, Wüste 0,08 …) mit 4 %/Zyklus, × fertility-Bonus. Gebremst durch Pflanzenfresserdichte (04) und Siedlungsfläche (05).
3.6 Ereignisse
Ereignisse sind Daten (data/events.json): Bedingung, Wahrscheinlichkeit pro Prüfung, Vorwarnzeit, Wirkung, Chronik-Text.
| Ereignis | Bedingung | Vorwarnung | Wirkung | Häufigkeit (Richtwert) |
|---|---|---|---|---|
| Eiszeit | global_ice > 0,35 | 15 Zyklen | −6 °C, 60 Zyklen | selten, durch Spieler auslösbar |
| Hitzewelle | co2 > 1,4 | 10 | +4 °C regional, Dürre | mittel |
| Dürre | Region moist < 0,3 + Siedlung | 8 | Vegetation −50 % | mittel |
| Überschwemmung | Flussregion + Spielerregen ≥ 3 | 5 | Siedlung in Senke beschädigt | selten |
| Meteorschauer | zufällig, ab Epoche 3 | 20 | 1–3 Einschläge | sehr selten (≤ 1/Woche Echtzeit) |
| Supervulkan | ≥ 3 aktive Vulkane in Nachbarschaft | 20 | Aschewinter −4 °C | selten |
| Seuche | Siedlungsdichte hoch, Stabilität < 40 | 10 | Bevölkerung −30 % | mittel (Epoche 5+) |
| Blütezeit (positiv) | Gesundheit > 80 für 30 Zyklen | – | Mutationen 2× schneller, +SE | belohnend |
Zufall: PCG32, initialisiert aus seed, ein eigener Strom pro Subsystem (climate, life, civ, events), damit eine Änderung am Lebensmodul die Ereignisfolge nicht verschiebt. Jede Ziehung ist reproduzierbar: gleicher Spielstand + gleiche Aktionen = gleiche Geschichte (Testbarkeit, Benachrichtigungen, Fehlersuche).
3.7 Offline-Fortschritt
Verfahren
- Beim Pausieren (
NOTIFICATION_APPLICATION_PAUSED) speichern: Weltzustand,last_seen_unix,max_seen_unix(höchster je gesehener Zeitstempel),last_seen_ticks_msec. - Beim Fortsetzen:
delta = now_unix − last_seen_unix. - Zeitsicherheit (ohne Server):
delta < 0odernow < max_seen_unix − 300(Uhr zurückgestellt) →delta = 0, Ereignisclock_rollbacklokal protokollieren. Nichts wird abgezogen – nur nichts gutgeschrieben.deltawird auf 12 h gedeckelt. Ein Vorstellen der Uhr bringt also höchstens 12 h Fortschritt und volle Energie – das, was ehrliches Warten auch bringt. Ein Anreiz zum Betrügen entsteht nicht, weil Energie ohnehin bei 100 gedeckelt ist und nichts verkauft wird.- Läuft die App nur kurz im Hintergrund (Prozess lebt), wird zusätzlich
Time.get_ticks_msec()verglichen (monoton, nicht verstellbar).
- Makroschritte:
n = floor(delta / 600 s), höchstens 72. Jeder Makroschritt = 75 Zyklen mit vergröbertem Verfahren:- Klima/Vegetation/Populationen: analytische Näherung über 75 Zyklen (logistische Lösung statt 75 Einzelschritte).
- Ereignisse: Prüfungen einmal pro Makroschritt mit hochgerechneter Wahrscheinlichkeit, aus einem eigenen RNG-Strom
offline(Seed = seed ⊕ Makroschritt-Index). - Mutationen: höchstens 1 pro Art und Makroschritt; Evolutionssprünge werden als anstehende Evolutionskarte gespeichert (max. 3), nicht automatisch entschieden.
- Schutzregel aus 02 §2.8: katastrophale Ereignisse werden zu anstehenden Ereignissen.
- Rest
delta mod 600 sgeht nicht verloren: Er wird alsoffline_carrygespeichert und beim nächsten Mal mitgezählt. - Ausführung im
WorkerThreadPool; währenddessen spielt die Rückkehr-Animation. Budget: 72 Makroschritte ≤ 1,5 s auf dem schwachen Referenzgerät (≈ 20 ms je Makroschritt). - Ergebnis:
ReturnSummary(Top-5-Ereignisse nach Gewicht, Kennzahlen vorher/nachher) → Rückkehr-Karte.
Warum feste Makroschritte?
Weil das Ergebnis dann nur von der Zahl der Makroschritte abhängt, nicht vom genauen Öffnungszeitpunkt. Damit kann das Spiel beim Pausieren eine Vorausberechnung machen (13): „Nach 6 Makroschritten entsteht ein Dorf“ → Benachrichtigung in 60 Minuten. Öffnet der Spieler die App später, rechnet sie exakt dieselben Schritte – die Benachrichtigung war wahr.
Fairness bei langer Abwesenheit
- Nach 12 h stoppt der Fortschritt, aber nichts verfällt: Energie voll, Tagesaufgaben neu, bis zu 3 anstehende Entscheidungen.
- Ab 3 Tagen Abwesenheit: „Willkommen zurück“-Geschenk (+50 SE Überladung, einmalig pro Woche) – ohne Strafe, ohne Druck.
3.8 Chronik (Ereignisprotokoll)
{ "c": 412, "k": "ice_age_start", "r": [118, 119, 140],
"p": { "cause": "ash_winter", "volcano": "Glutkamm", "species": ["sp_17"] },
"w": 80 }
cZyklus ·kEreignisart ·rRegionen ·pParameter ·wGewicht (für Zusammenfassungen).- Texte entstehen erst beim Anzeigen aus Vorlagen (
data/chronicle_templates/<lang>.json) – das Protokoll bleibt sprachneutral und klein. - Begrenzung: 2000 Einträge pro Planet; ältere niedrig gewichtete Einträge werden zu „Zeitalter“-Zusammenfassungen verdichtet.
3.9 Ortsnamen
Kontinente, Gebirge und Meere bekommen automatisch Namen aus Silbentabellen pro Sprache („Glutkamm“, „Nordmeer von Ilva“), sobald sie entstehen (Zusammenhangskomponenten im Raster, Prüfung alle 20 Zyklen). Der Spieler kann umbenennen. Namen machen die Chronik lebendig und kosten nichts.
3.10 Testbarkeit
tests/test_sim_determinism.gd: gleicher Seed + gleiche Aktionsliste → identischer Zustands-Hash nach 1000 Zyklen.tests/test_offline.gd: Vorausberechnung beim Pausieren = Nachrechnung beim Öffnen (Hash-Vergleich); Uhr zurück → kein Fortschritt; 48 h → genau 72 Makroschritte.tests/bench_sim.gd: Zyklus- und Makroschritt-Zeit, Ausgabe als JSON (Grenzwerte aus 12).