Перейти к основному содержимому

Обзор архитектуры

Это руководство для пользователей уровня Source, имеющих доступ к полному исходному коду MCE на C#.

Этот документ охватывает иерархию модулей, структуру сборок, паттерны внедрения зависимостей и ключевые системы времени выполнения, составляющие Monster Capture Engine.

Иерархия модулей

MCE организован в модули, следующие многослойной архитектуре:

Assets/
OpenMon/
Core/
Runtime/ # Ядро движка (ОБЯЗАТЕЛЬНО)
Initialization/ # Запуск движка, мастер первоначальной настройки
Monster/ # MonsterInstance, Roster, эволюция, разведение
MonsterDatabase/ # Определения ScriptableObject (виды, приёмы, предметы, способности)
Battle/ # BattleManager, 15+ модулей, конечный автомат, ИИ
Characters/ # MCECharacterController, PlayerCharacter, следовники
Actors/ # Система акторов, визуальный скриптинг CommandGraph
World/ # GridController, GlobalGridManager, тайлы, встречи
Saves/ # SavegameManager, SavableObject, GameVariables
Player/ # GlobalGameData, состояние игрока
UI/ # Все UI-контроллеры и представления
Quests/ # Система квестов (цели, награды, отслеживание)
SDK/ # Интерфейсы публичного API
GameFlow/ # Управление состоянием игры
Configuration/ # ScriptableObject конфигурации
Rendering/ # URP-шейдеры, время суток, погода
Audio/ # AudioManager, BGM/SE/ME
Localization/ # Многоязычная поддержка
Badges/ # Отслеживание значков
Input/ # Абстракция ввода
MonsterDex/ # Отслеживание декса (увидено/поймано)
Animation/ # Утилиты анимации
Shaders/ # Исходники шейдеров
Editor/ # Инструменты только для редактора
ArtGenerator/ # Генерация спрайтов ИИ
MonsterCreator/ # Мастер создания монстров
Tools/ # Обозреватель базы данных, редактор таблицы типов, валидаторы
Followers/ # Инструменты спрайтлистов следовников
Actors/ # Редактор CommandGraph
Callbacks/ # Утилиты обратных вызовов редактора

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

  1. Ядро Runtime не имеет внешних зависимостей от модулей Online или Importer.
  2. Онлайн-функции опциональны через символ скриптинга MCE_ONLINE.
  3. Код редактора строго отделён от Runtime через границы .asmdef.
  4. Интерфейсы SDK в Runtime/SDK/ -- публичная поверхность API для пользователей DLL-уровня.

Структура сборок

MCE использует Unity Assembly Definitions для обеспечения границ компиляции:

СборкаРасположениеНазначениеСсылки
OpenMon.MCE.RuntimeCore/Runtime/Основные игровые системыТолько базовые библиотеки
OpenMon.MCE.EditorCore/Editor/Инструменты редактораRuntime + UnityEditor
MCE.Online.RuntimeMCE_Online/Runtime/Сетевое взаимодействие NakamaRuntime + Nakama SDK
MCE.Online.EditorMCE_Online/Editor/Онлайн-инструменты редактораOnline Runtime + UnityEditor
MCE.EssentialsImporter.EditorMCE_EssentialsImporter/Editor/Конвейер импортаRuntime + KaitaiStruct
MCE.Runtime.TestsCore/Tests/Runtime/Тесты ядраRuntime + NUnit

Почему Assembly Definitions важны

  • Изоляция компиляции: Изменения кода Editor не перекомпилируют Runtime.
  • Контроль зависимостей: Компилятор не позволяет Runtime ссылаться на API Editor.
  • Исключение при сборке: Сборки Editor исключаются из сборок игрока.
  • Изоляция тестов: Тестовые сборки могут ссылаться на внутренние элементы, не раскрывая их в продакшене.

Внедрение зависимостей (Zenject)

MCE использует Zenject (Extenject) с паттерном инсталлеров для внедрения зависимостей.

Паттерн инсталлеров

Каждая подсистема имеет свой инсталлер, регистрирующий привязки:

public class BattleLauncherInstaller : MonoInstaller
{
[SerializeField] private BattleLauncher battleLauncherPrefab;

public override void InstallBindings()
{
Container.Bind<IBattleLauncher>()
.To<BattleLauncher>()
.FromComponentInNewPrefab(battleLauncherPrefab)
.AsSingle()
.Lazy();
}
}

Ключевые инсталлеры

ИнсталлерПривязывает
GameFlowInstallerОсновные сервисы игрового потока
PlayerCharacterInstallerPlayerCharacter, сервисы персонажа
BattleLauncherInstallerBattleLauncher, точка входа в бой
GridInstallerGridController, данные тайлов
SceneInfoInstallerSceneInfo, метаданные сцены
WorldDatabaseInstallerWorldDatabase, глобальные данные карт
TileDataInstallerДанные типов тайлов
GlobalGridManagerInstallerМежкартовая навигация
SavegameInstallerSavegameManager, сериализация
EvolutionManagerInstallerEvolutionManager

Соглашения

  • Привязки .AsSingle().Lazy() по соглашению (один экземпляр, создаётся при первом использовании).
  • [Inject] используется на методах Construct(), а не конструкторах.
  • Паттерн DialogManager.Factory используется для создания UI во время выполнения.
  • Инсталлеры области сцены обслуживают сервисы конкретной сцены.
  • Инсталлеры области проекта (в ProjectContext) обслуживают глобальные сервисы.

Точки внедрения

public class SomeManager : MonoBehaviour
{
// Внедрение через поле (для MonoBehaviour)
[Inject] private IMonsterDatabase database;

// Внедрение через метод (предпочтительно для явных зависимостей)
[Inject]
public void Construct(IBattleSystem battle, IPlayerData player)
{
this.battle = battle;
this.player = player;
}
}

Ключевые системы времени выполнения

Поток MCEInitialization

MCEInitialization -- первое, что выполняется при запуске игры:

1. Настройка экрана (разрешение, ориентация)
2. Загрузка баз данных (монстры, приёмы, предметы, способности)
3. Загрузка данных локализации (если удалённые)
4. Запуск мастера первоначальной настройки (если необходимо)
5. Инициализация подсистем (менеджер сохранений, аудио, ввод)
6. Сигнал готовности
7. Загрузка главного меню или продолжение сохранённой игры

Это MonoBehaviour на постоянном GameObject, переживающем загрузки сцен.

Система монстров

Модель данных монстров:

MonsterEntry (ScriptableObject - определение вида)
├── Базовые характеристики, типы, способности
├── DataByFormEntry[] (переопределения по формам)
├── EvolutionData[] (пути эволюции)
└── Списки приёмов (по уровню, TM, яичные, от наставника)

MonsterInstance (runtime - отдельный монстр)
├── Ссылка на вид (MonsterEntry)
├── Уровень, опыт
├── IVs[6], EVs[6]
├── Природа
├── Текущие HP, статус
├── MoveSlot[4] (с PP)
├── Дружба, данные OT
└── Индекс формы

Roster (отряд до 6 MonsterInstance)
MonsterStorage (ячейки ПК)

Система сражений

Подробности см. в Внутренние системы сражений.

Система мира

GlobalGridManager (межкартовая навигация)
└── GridController (сетка сцены)
├── Сетка TileData (столкновения, тип по тайлу)
├── Зоны EncounterTile
└── SceneInfo (метаданные)

MCECharacterController (перемещение)
├── PlayerCharacter (ввод + встречи)
└── ActorCharacter (ИИ-перемещение NPC)

Система сохранений

SavegameManager
├── MCESavesSerializer (чтение/запись JSON)
├── SavableObject[] (зарегистрированные сохраняемые объекты)
└── GameVariables (глобальное состояние)

Стиль кода

Кодовая база следует этим соглашениям:

  • 4-пробельный отступ, фигурные скобки на новой строке.
  • PascalCase для публичных типов, методов, свойств.
  • camelCase для приватных полей и локальных переменных.
  • [SerializeField] private вместо публичных полей.
  • Разделение Runtime/ и Editor/ за границами .asmdef.
  • Conventional Commits: feat:, fix:, docs:, refactor:.
Сериализуемые поля

Никогда не переименовывайте сериализуемые поля без плана миграции. Сцены и префабы зависят от имён сериализуемых полей. Используйте [FormerlySerializedAs("oldName")] для переименований.

Расширение движка

Добавление новой системы

  1. Создайте классы в Runtime/YourSystem/.
  2. Создайте инсталлер для внедрения зависимостей.
  3. Добавьте инсталлер в соответствующий контекст сцены или проекта.
  4. Если нужны данные сохранения, расширьте SavableObject.
  5. Если есть инструменты редактора, создайте классы в Editor/YourSystem/.

Модификация существующей системы

  1. Прочитайте существующий код и поймите зависимости модуля.
  2. Проверьте, можно ли выполнить изменение через конфигурацию или расширение, а не модификацию.
  3. При модификации следуйте существующим паттернам модуля.
  4. Обновите тесты для покрытия изменения.
  5. Задокументируйте изменение в сообщениях коммитов.

Соображения по производительности

  • Поиск в базе данных -- O(1) по номеру декса, O(n) по имени. Кэшируйте поиск по имени при частых вызовах.
  • Модули боя инициализируются один раз за бой, а не за ход. Создание модулей -- не горячий путь.
  • Тайловые карты имеют потолок производительности около 200x200 тайлов. Разделяйте большие карты.
  • Сериализация сохранений синхронна в основном потоке. Поддерживайте разумные размеры данных сохранения.
  • Выполнение CommandGraph основано на корутинах. Сложные графы с множеством узлов подходят для скриптинга событий, но не для пофреймовой логики.