Files
g-world/docs/ARCHITECTURE.md

6.2 KiB

Архитектура

Документ описывает, как устроен проект: какие есть слои, кто кому имеет право звонить и как данные ходят по системе. Структура папок — прямое отражение этих слоёв, см. README.

Слои и правила зависимостей

Стрелка «→» читается как «может знать о». Всё, что не указано, — запрещено.

main ──→ autoload ──→ core
  │
  ├───→ ui ────────→ core, data
  ├───→ world ─────→ core, data, entities
  ├───→ systems ───→ core, data
  ├───→ entities ──→ core, data
  └───→ debug ─────→ (всё, только для разработки)
Слой Зачем Чего в нём быть не должно
core Утилиты, базовые типы, математика сетки Ссылок на конкретные сцены и геймплей
data Описания контента: схемы (Resource) и их экземпляры (.tres) Логики поведения — только данные
systems Симуляция мира: время, климат, погода, экономика Прямого доступа к нодам сцены
world Визуализация и взаимодействие с убежищем Правил симуляции — они в systems
entities Жители, предметы; их состояние и поведение Знания о конкретном UI
ui Экраны, HUD, контролы Изменения состояния мира напрямую
debug Дев-консоль, оверлеи, читы Кода, от которого зависит релизная логика

Ключевое правило: ui и world не меняют состояние мира сами — они шлют намерение (сигнал/вызов системы), а изменение делает systems. Обратно systems не дёргает ноды: он сообщает об изменении сигналом.

Данные и рендер разделены

Симуляция не должна зависеть от того, нарисовано ли что-то на экране.

  • Данные (systems, data) — чистая логика: считаются от delta, полностью сериализуемы, тестируются без дерева сцены.
  • Рендер (world, ui) — читает данные и отображает, подписавшись на сигналы.

Практический критерий: любой класс из systems должен работать в юнит-тесте, где никакой сцены нет.

Обмен сообщениями

Три способа, в порядке предпочтения:

  1. Сигнал вверх, вызов вниз. Родитель знает о детях и вызывает их методы; ребёнок о родителе не знает и только испускает сигналы.
  2. Ссылка на систему, полученную из автозагрузки — когда нужен ответ здесь и сейчас (запрос состояния, валидация постройки).
  3. Глобальная шина (EventBus в autoload) — только для событий, которые слушают несколько несвязанных слоёв (например, «время перескочило», «погода сменилась»). Не превращать её в свалку: каждый сигнал документируем.

Контент — это ресурсы, а не код

Новая комната, предмет или тип породы добавляются как .tres в data/tables/ по схеме из data/resources/, без правки кода. Если для добавления контента приходится менять .gd — схема неполная, чинить надо схему.

Автозагрузки

Автозагрузка — дорогая вещь: она живёт всю сессию и её видно отовсюду. Заводим только для того, что действительно глобально и одно на игру. Каждая новая автозагрузка регистрируется через Project Settings → Autoload и упоминается здесь.

Сейчас в проекте:

Автозагрузка Что делает
EventBus (src/autoload/event_bus.gd) Глобальная шина событий, пока пустая
_mcp_game_helper Служебная, часть плагина godot_ai; к игре отношения не имеет

Точка входа

src/main/main.tscn — главная сцена проекта:

Main (Node2D)          # main.gd: только сборка, никаких игровых правил
├── World (Node2D)     # убежище: порода, сетка, комнаты, жители
└── UI (CanvasLayer)   # интерфейс поверх мира, не зависит от камеры

World и UI разделены слоями сразу, чтобы интерфейс не ездил вместе с камерой убежища и не масштабировался вместе с зумом.

Решения

Существенные развилки (выбор подхода к сохранению, к сетке, к тайм-степу) фиксируем как ADR в adr/ — короткая заметка «контекст → решение → последствия». Это дешевле, чем через полгода восстанавливать мотивацию по коду.