O projekcie
SimSandbox to eksperymentalny silnik symulacji inspirowany Songs of Syx – izometryczną gierką symulacyjną z niesamowitym systemem AI agentów. Buduję go od zera, by zrozumieć architekturu silników gry typu sim, gdzie emergencja rodzi się z prostych zasad.
Obecnie w simie są agenci z bazowymi potrzebami (głód), system ekonomii (farmerzy, produkcja żywności), zbieractwo surowców (drzewa, kamień) i system budowania budynków. Wszystko w własnym bitset ECS napędzanym przez deterministyczną pętlę 30 Hz na osobnym wątku.
Architektura
Kluczowa decyzja projektowa: symulacja i render na osobnych wątkach.
┌──────────────┐ ◄───── ┌──────────────┐
│ Render Thread│ │ Sim Thread │
│ (MonoGame) │ snapshot │ (30 Hz fixed)│
│ 60-144 Hz │───────────►│ │
└──────────────┘ └──────────────┘
▲ │
│ reads ┌─┴──────────┐
└──── snapshot ──────────│ World (ECS)│
│ + TileMap │
│ + Pathfinder│
└──────────────┘
Kluczowe decyzje:
-
Fixed timestep 30 Hz – sim tickuje w stałym interwale, render jedzie swoim tempem. Komunikacja przez double-buffered snapshot – atomowy swap referencji pod lockiem, render reader bez locków.
-
Bitset ECS w ~200 liniach – zero zależności. Każdy komponent =
T[]indeksowany przez entity index +BitArrayokreślający kto co ma. Proste do zrozumienia, cache-friendly w liniowych iteracjach. -
Generation counter w EntityId – bronienie się przed use-after-free gdy slot jest reużywany.
-
Save versioning od dnia 1 – pierwsze pole pliku to
int version. Bump wersji + funkcja migracji przy każdej zmianie struktury. -
Komponenty jako
struct + [MemoryPackable]– zero alokacji w hot loopie, automatyczna binarna serializacja, kopiowanie po wartości.
Co już działa
- Bitset ECS z generation counters, entity create/destroy, query z 1-4 komponentami
- System pathfindingu – A* z
PriorityQueue<T, P>i zakazem corner cuttingu, path cache w komponencie ECS - Proceduralna generacja mapy – 4 typy tile’ów (Trawa/Podłoga/Ściana/Drzwi), random rect + flood-fill walidacja
- Budynek i system budowania – dom 4×4, magazyn 6×6, farma 4×4, planowanie budowy i auto-place
- AI agentów – spawn w domach (2/dome), wander z bias 70% dom / 30% mapa
- FSM głodu i ekonomii – Idle → GoingToStorage → Eating → ReturningHome; farmerzy produkują jedzenie (GoingToWork → Working → DeliveringFood), Hunger jako interrupt
- Magazyn z pojemnością – 50/100 start, konsumpcja per Eating, transport przez farmera
- Visual – sprite’y pixel-art 16×16, czerwona kropka nad głodnym agentem, HUD z paskami żywności i statystykami
- Save/load z wersjonowaniem – binarny format MemoryPack, 5 wersji, fail-fast na starsze
Czego jeszcze nie ma
- Multithreaded sim (AI phase wciąż single-threaded)
- Flow fields, hierarchical pathfinding
- Sen, pragnienie, zabawa – tylko głód na razie
- Game over przez śmierć z głodu (scope kolejnej fazy)
- Tryb budowania / pause / speed control / bitmap UI
- Łańcuchy produkcji wieloetapowe (drewno, kamień)
Stack technologiczny
- .NET 8 + MonoGame 3.8
- MemoryPack – szybka binarna serializacja przez source generators
- Własny bitset ECS – ~200 linii, zero zależności
- A* z
.NET 6+ PriorityQueue+ path cache w komponencie ECS - Pixel-art przez MGCB content pipeline
- IM-GUI – własna, lekka implementacja HUD
Następne kroki
- Multithreaded AI phase –
Parallel.Forna slice agentów, lock-collect-apply pattern - Flow fields – BFS z magazynu, gradient na każdy tile, O(N) zamiast A* per agent
- Utility AI – scoring
Need.Hunger² + Need.Thirst + Need.Fun, emergencja za free - System surowców – drzewa, kamień, łańcuchy produkcji (drewno → budynek → więcej farmerów)
Dlaczego to robię
Songs of Syx to jedna z najlepszych gier symulacyjnych, jakie istniały, a jej źródła nigdy nie zostały opublikowane. Chciałbym zrozumieć, jak autor zbudował ten system – bo proste zasady + duża liczba agentów = magia. SimSandbox to mój sposób na przybliżenie się do tego zrozumienia.