Pocket Planet Wiki

Technische Architektur

Engine-Entscheidung, Module, Schnittstellen, Speichern und Tests.

Konzeptphase. Interne Planungsfassung, Stand 04.11.2026. Pocket Planet ist noch in Entwicklung – Inhalte, Zahlen und Abläufe können sich ändern.

11.1 Engine-Entscheidung: Godot 4.7 vs. Unity 6

KriteriumGodot 4.7 (GDScript)Unity 6 (C#)Gewinner für uns
Android[Fakt] Compatibility ab Android 7.0, Mobile/Forward+ ab 9.0 (Docs); AAB-Export, Plugin-System v2 (AAR)sehr ausgereiftGleichstand
iOSExport vorhanden; Metal-Pfad laut Docs ab iOS 16, Compatibility per OpenGL ES; Plugin-Ökosystem dünnersehr ausgereiftUnity
3D-Grafik mobilFür stilisierte Grafik ausreichend; keine High-End-Features im Compatibility-RendererURP stärker, mehr Out-of-the-box-EffekteUnity, aber für unseren Stil nicht entscheidend
Leistung auf alten GerätenKleine Laufzeit, schneller Start, Compatibility = breiteste Abdeckunggut, größere BuildsGodot leicht vorn
AnimationAnimationPlayer, Tweens, Shader – reicht für unseren Plan vollständig (09)Timeline, AnimatorGleichstand für unseren Bedarf
Native SDKsAndroid-Plugins einfach (Studio hat schon Analytics-Plugin); iOS-Plugins aufwendigerRiesiges ÖkosystemUnity
Werbe-SDK (eigenes)Port nötig – betrifft jede Engine, da nur Web/RN-Adapter existierenPort nötigGleichstand
BenachrichtigungenCommunity-Plugins (MIT) Android/iOS (13)Mobile Notifications Package (offiziell)Unity
Entwicklungsaufwand für uns6 Godot-Spiele im Studio, Muster, Skripte, Tests, Radio, Analytics vorhandenkein Unity-Know-how im Studio [Annahme]Godot klar
Build-Prozesslokal, ohne Konto, schnell (Studio-Skripte vorhanden)Unity Hub, Lizenz-LoginGodot
WartbarkeitMIT, keine Laufzeitgebühren, Projektdateien textbasiert (gut für Coding-Agenten)proprietär, Editor-Binärdaten teils schlechter für AgentenGodot
Kosten/Lizenz0 €Personal kostenlos bis zu einer Umsatzgrenze [Annahme: Stand Unity 6]Godot

Entscheidung [Vorschlag]: Godot 4.7.2 mit GDScript. Unity gewinnt bei iOS-Ökosystem und High-End-Grafik. Beides ist für ein stilisiertes, Android-first Cozy-Spiel nicht entscheidend. Godot gewinnt bei dem, was für ein kleines Team zählt: vorhandenes Wissen und Bausteine, Kosten, agentenfreundliche Textformate, schnelle lokale Builds.

Renderer: Compatibility statt Mobile

Compatibility (OpenGL ES 3)Mobile (Vulkan)
Geräteab Android 7, auch alte GPUsab Android 9 mit stabilen Vulkan-Treibern
Studio-Erfahrungalle 6 Godot-Spielekeine
Funktionenkeine SDFGI/SSAO/Volumetrie, begrenzte Lichter pro Objektmehr Effekte, bessere Partikel/Lichter
Für unseren Stilausreichend: Licht steckt in eigenen ShadernMehrwert gering

Entscheidung: Compatibility für alle Stufen. Alle Shader werden so geschrieben, dass sie auch im Mobile-Renderer laufen. Ein späterer Wechsel (z. B. „Hoch“-Stufe auf Vulkan) bleibt möglich, verdoppelt aber die QA – daher erst nach dem MVP prüfen.

11.2 Projektstruktur

project.godot · export_presets.cfg
scenes/            main.tscn, planet.tscn, ui/*.tscn
scripts/
  autoload/        game.gd (Einstellungen, Profil) · bus.gd (Signale) · sfx.gd (Audio+Radio, aus Bellmoor)
                   loc.gd (Sprache, aus Bellmoor) · analytics.gd · notify.gd · ads.gd · quality.gd
  sim/             rng.gd · grid.gd · planet_state.gd · climate.gd · hydrology.gd · terrain.gd · biomes.gd
                   vegetation.gd · life.gd · civ.gd · events.gd · chronicle.gd · actions.gd · simulator.gd
                   offline.gd · names.gd
  meta/            energy.gd · progression.gd · challenges.gd · achievements.gd · galaxy.gd · scenarios.gd
  services/        save.gd · time_keeper.gd
  view/            planet_view.gd · props_view.gd · fx_view.gd · camera_rig.gd · input_router.gd · night_lights.gd
  ui/              hud.gd · tool_ring.gd · info_sheet.gd · evolution_card.gd · chronicle_view.gd · settings.gd …
shaders/           planet_surface.gdshader · ocean · atmosphere · clouds · prop_foliage · prop_creature · prop_building · fx_lava
data/              balance.json · biomes.json · events.json · evolution_cards.json · challenges.json · scenarios/*.json
                   names/<lang>.json · chronicle_templates/<lang>.json
assets/            models/ · textures/ · audio/ · icons/ · fonts/ · ui/
locale/            strings.csv → *.translation
addons/            pp_analytics (aus Bellmoor) · notification-Plugins · werbemodul (Port)
tests/             test_*.gd (headless) · ui_*.gd (mit Fenster) · bench_*.gd
tools/             export-wiki.mjs · tripo.mjs · audio-prep.mjs · import-gpt-art.mjs · blender/*.py
artifacts/         KI-Rohdaten (git-ignoriert)
docs/konzept/      diese Dokumente

11.3 Schichten und Abhängigkeiten

             ┌──────────── UI (scripts/ui) ─────────────┐
             │  liest Zustand, sendet Aktionen          │
             ▼                                          │
     View (scripts/view)  ◄── Bus-Signale ──  Meta (energy, progression, challenges)
             │ liest PlanetState (read-only)            │
             ▼                                          ▼
     ┌──────────────── Sim (scripts/sim) ─────────────────────┐
     │  reine RefCounted-Klassen · kein Node · kein Rendering │
     └─────────────────────────────────────────────────────────┘
             ▲
     Services (save, time_keeper) · Autoloads (sfx, ads, notify, analytics, quality)

Regeln (für Coding-Agenten verbindlich, kommen in CLAUDE.md):

  1. scripts/sim/ importiert nichts aus view/ui/autoload. Tests laufen headless.
  2. Jede Zustandsänderung durch den Spieler ist eine Aktion (actions.gd, Command-Muster): validate(state) → cost → apply(state). Dadurch: Rückgängig (5 s), Wiederholbarkeit in Tests, Energieprüfung an einer Stelle.
  3. View schreibt nie in den Zustand. Sie liest PlanetState und TickReport.dirty_regions.
  4. Zahlen fürs Balancing stehen in data/*.json, nie im Code.
  5. Zufall nur über rng.gd (PCG32, benannte Ströme).

11.4 Module – Verantwortung und Schnittstellen

ModulVerantwortungWichtigste Schnittstelle
sim/simulator.gdZyklus-Orchestrierung, Thread-Übergabe, Doppelpufferstep(n: int) -> TickReport · queue(action) · snapshot() -> PackedByteArray
sim/grid.gdIkosaeder-Raster, Nachbarn, Lat/Lon, Ankerpunkteneighbors(r) -> PackedInt32Array · region_at(dir: Vector3) -> int
sim/climate.gdTemperatur, Eis, CO₂update(state, dt_cycles)
sim/hydrology.gdFeuchte, Meeresspiegel, Flüsseupdate(state, dt) · rivers(state)
sim/terrain.gdHöhenänderungen, Erosion, Vulkaneraise(state, regions, amount)
sim/biomes.gdBiom-Klassifikationclassify(state, regions)
sim/life.gdArten, Populationen, Mutation, Kartenupdate(state, dt) · pending_cards() · resolve_card(id, option)
sim/civ.gdKulturen, Stufen, Ereignisseupdate(state, dt) · inspire(civ, kind)
sim/events.gdEreignistabelle, Vorwarnung, Wirkungupdate(state) · pending() · resolve(id, choice)
sim/chronicle.gdProtokoll, Verdichtunglog(kind, regions, params, weight) · entries(from, to)
sim/offline.gdMakroschritte, Vorausberechnungcatch_up(state, steps) -> ReturnSummary · forecast(state, steps) -> Array[ForecastHit]
meta/energy.gdRegeneration, Kosten, Überladungcan_pay(cost) · pay(cost) · tick(seconds, health)
meta/progression.gdEpochen, Freischaltungencheck(state) -> Array[Unlock]
meta/challenges.gdTages-/Wochenziele, Fortschrittroll_daily(date, state) · progress(state)
meta/galaxy.gdPlanetenplätze, Wechselopen(planet_id) · create(scenario_id)
services/save.gdJSON + Binärblock, Versionen, atomarsave_planet(id, data) · load_planet(id) · migrate(v)
services/time_keeper.gdZeitstempel, Rücksprung-Schutzelapsed_since_pause() -> int
view/planet_view.gdMesh, Datentexturen, Shader-Uniformsapply(state, dirty) · set_quality(tier)
view/props_view.gdMultiMeshes je Asset/LODapply(state, dirty)
view/input_router.gdGesten → Kamera oder WerkzeugSignale tap(region), brush(regions), pinch(f)
autoload/bus.gdentkoppelte Signalespecies_born, epoch_reached, event_announced, card_pending, energy_changed …
autoload/sfx.gdAudio-Busse, Pool, Radioplay(name) · tune(i) · set_radio(on) · duck(on)
autoload/ads.gdAdManager-Schnittstelle (14)event(name) · show_rewarded(placement, cb)
autoload/notify.gdlokale Benachrichtigungen (13)schedule_from_forecast(hits) · cancel_all()
autoload/quality.gdStufenwahl, adaptive Absenkungtier · Signal tier_changed
autoload/analytics.gdFirebase (nur Opt-in)event(name, params)

11.5 Speichersystem

  • Profil: user://profile.json – Einstellungen, Sternenstaub, Achievements, Planetenliste, Zustimmungen, Zeitstempel.
  • Planet: user://planets/<id>.save – JSON-Kopf (Version, Epoche, Zyklus, Seed, Arten, Kulturen, Ziele) + Regionsfelder als PackedByteArray, gesamt per FileAccess.open_compressed (zstd).
  • Atomar: schreiben nach <id>.tmp, dann umbenennen; die vorige Datei bleibt als <id>.bak. Beim Laden: Prüfsumme (CRC32 im Kopf); bei Fehler .bak.
  • Wann: Epochenwechsel, nach jeder Evolutionskarte/Ereignis-Entscheidung, alle 60 s, bei NOTIFICATION_APPLICATION_PAUSED und WM_CLOSE_REQUEST, vor Eigenwerbung.
  • Version & Migration: save_version im Kopf, migrate() je Versionssprung, Test mit gespeicherten Beispielständen (tests/fixtures/saves/).
  • Kein Cloud-Save im MVP. Phase 3: Android Backup (Auto Backup) prüfen – kostet nichts, braucht nur android:allowBackup + Ausschlussregeln.

11.6 Tests & Debugging

  • Headless-Tests (tests/test_*.gd, Runner tools/godot-test.ps1 nach Studio-Muster): Determinismus, Offline-Äquivalenz, Aktionen (Kosten/Validierung), Biom-Klassifikation, Populationsgrenzen, Speichern/Laden/Migration, Zielfortschritt, Zeitsicherheit.
  • Bot-Balancing (tests/bot_balance.gd): Ein Bot spielt mit einfachen Regeln 30 Tage simulierte Zeit (aktive Sitzungen + Offline-Lücken) und protokolliert Epochen-Zeitpunkte und Energie-Engpässe → Abgleich mit der Zielkurve in 06 §6.6.
  • Benchmarks (tests/bench_*.gd): Zyklus, Makroschritt, Szenen-Kamerafahrt → JSON nach artifacts/bench/.
  • Debug-Overlay (nur Debug-Builds, 3-Finger-Tipp): FPS, Frametime-Kurve, Draw Calls, Vertices, Sim-ms, Stufe, Zyklus, Seed; Datentextur-Ansichten (Temperatur/Feuchte/Biom/Population als Falschfarben).
  • Testzustände (Studio-Muster test_unlock.gd): -- --epoch=5 --seed=42 --energy=999 setzt beliebige Startzustände, auf Android/iOS wirkungslos.
  • Capture-Modus (Studio-Muster --capture): rendert Store- und Wiki-Screens deterministisch.

11.7 Plan B für die Rechenleistung

Falls GDScript die Budgets aus 12 verfehlt (Messung ab T-06), wird nur sim/ (ohne Schnittstellenänderung) als GDExtension in C++ neu geschrieben; Tests bleiben gleich und prüfen Gleichheit über Zustands-Hashes. Aufwand [Schätzung] 2–3 Wochen. Vorher ausschöpfen: Packed-Arrays, Schleifen ohne Dictionaries, Teilaktualisierung (dirty), Worker-Thread.