182 lines
12 KiB
Markdown
182 lines
12 KiB
Markdown
# Архитектура mrgameeng
|
||
|
||
## Обзор
|
||
|
||
**mrgameeng** — кроссплатформенный 2D игровой движок на базе MonoGame 3.8.4
|
||
(DesktopGL: Windows, Linux, macOS), .NET 8, C#.
|
||
|
||
Ключевое архитектурное решение — **ECS-first**: движок плотно интегрирует
|
||
[Friflo.Engine.ECS](https://github.com/friflo/Friflo.Engine.ECS) (v3.6) как ядро
|
||
модели объектов. Это самая производительная ECS-библиотека в .NET по результатам
|
||
[Ecs.CSharp.Benchmark](https://github.com/Doraku/Ecs.CSharp.Benchmark): ноль аллокаций
|
||
в горячих путях, archetype-хранилище, встроенные системы, события и индексы.
|
||
|
||
## Принципы
|
||
|
||
1. **Данные отдельно от логики.** Всё состояние игры — в компонентах
|
||
(`struct : IComponent`), вся логика — в системах (`QuerySystem<T>`).
|
||
Никаких `Update()` у игровых объектов и иерархий наследования сущностей.
|
||
2. **Ноль аллокаций в кадре.** Системы, выполняющиеся каждый кадр, не должны
|
||
аллоцировать память. Проверяется бенчмарками и code review.
|
||
3. **Модульность.** Движок разбит на библиотеки по функциональным областям.
|
||
Игра подключает только то, что использует.
|
||
4. **Всё демонстрируется.** Каждая публичная возможность движка показана
|
||
в `MrGameEng.Sample` и покрыта тестами (где логика тестируема без GPU).
|
||
|
||
## Модули
|
||
|
||
| Библиотека | Ответственность |
|
||
|------------------------------|------------------------------------------------------------|
|
||
| `MrGameEng.Core` | Игровой цикл (хост над `Game`), `EntityStore`, `SystemRoot`, сцены, время, жизненный цикл |
|
||
| `MrGameEng.Graphics` | Собственный батчер-рендерер (см. «Рендеринг»), камера, спрайты, анимации, слои |
|
||
| `MrGameEng.Input` | Абстракция ввода: клавиатура, мышь, геймпад; action maps |
|
||
| `MrGameEng.Audio` | Звуковые эффекты и музыка |
|
||
| `MrGameEng.Assets` | Runtime-загрузка ресурсов без Content Pipeline, кэш, `AssetRef<T>` |
|
||
| `MrGameEng.Assets.Generator` | Roslyn incremental source generator: классы с типизированными хендлами ресурсов |
|
||
|
||
Планируемые модули (по мере развития): `Physics2D`, `Tilemap`, `UI`, `Particles`.
|
||
|
||
### Правило зависимостей
|
||
|
||
```
|
||
MrGameEng.Graphics ─┐
|
||
MrGameEng.Input ─┤
|
||
MrGameEng.Audio ─┼──► MrGameEng.Core ──► MonoGame.Framework.DesktopGL
|
||
MrGameEng.Assets ─┘ └──► Friflo.Engine.ECS
|
||
```
|
||
|
||
Модули зависят **только от `Core`** и никогда друг от друга. `Core` зависит только
|
||
от MonoGame и Friflo. Если двум модулям нужен общий тип — он переезжает в `Core`.
|
||
|
||
`MrGameEng.Assets.Generator` — особый случай: это анализатор (netstandard2.0),
|
||
он подключается к проекту игры как `Analyzer`, в рантайме не участвует и не зависит
|
||
от других модулей движка.
|
||
|
||
## Загрузка ресурсов (без Content Pipeline)
|
||
|
||
MGCB / Content Pipeline **не используется**. Все ресурсы лежат в папке `Assets/`
|
||
проекта игры как сырые файлы (копируются в output при сборке) и грузятся в рантайме:
|
||
|
||
| Тип | Форматы | Как грузим |
|
||
|----------------|-----------|---------------------------------------------------------|
|
||
| Текстуры | png, jpg | `Texture2D.FromFile` |
|
||
| Шрифты | ttf | FontStashSharp (растеризация в рантайме, динамический атлас) |
|
||
| Звуки | wav | `SoundEffect.FromFile` |
|
||
| Музыка | ogg | Стриминг через NVorbis + `DynamicSoundEffectInstance` |
|
||
| Шейдеры | fx | Компиляция `dotnet-mgfxc` на этапе сборки (MSBuild target), в рантайме грузится байткод `.mgfx` |
|
||
|
||
`AssetManager` (в `MrGameEng.Assets`) грузит ресурсы по типизированному хендлу
|
||
`AssetRef<T>` (путь + тип), кэширует по пути и владеет временем жизни (Dispose
|
||
при выгрузке сцены/игры).
|
||
|
||
### Кодогенератор хендлов
|
||
|
||
`MrGameEng.Assets.Generator` — Roslyn **incremental source generator**. Файлы из
|
||
`Assets/` передаются ему через `AdditionalFiles` (один glob в `.csproj` игры).
|
||
По дереву папок он генерирует статический класс с типизированными хендлами,
|
||
тип выводится из расширения файла:
|
||
|
||
```csharp
|
||
// Assets/Textures/player.png →
|
||
public static partial class GameAssets
|
||
{
|
||
public static class Textures
|
||
{
|
||
public static readonly AssetRef<Texture2D> Player = new("Textures/player.png");
|
||
}
|
||
}
|
||
|
||
// использование: Texture2D tex = assets.Load(GameAssets.Textures.Player);
|
||
```
|
||
|
||
Обращение к ресурсу по строковому пути в коде игры — запрещено соглашением;
|
||
строки существуют только внутри сгенерированного кода.
|
||
|
||
## Рендеринг
|
||
|
||
`SpriteBatch` в движке **не используется** — в `MrGameEng.Graphics` свой батчер,
|
||
спроектированный под ECS.
|
||
|
||
### Камера
|
||
|
||
Ортографическая 2D-камера — из коробки, отдельная сущность с компонентом `Camera`:
|
||
|
||
- Позиция, зум, поворот, опциональные границы мира (clamp).
|
||
- Строит ортографическую view-projection матрицу; виртуальное разрешение
|
||
с letterbox/scale под размер окна.
|
||
- Активная камера — одна на сцену. Утилиты `ScreenToWorld` / `WorldToScreen`.
|
||
|
||
### Слои (render layers)
|
||
|
||
- Слои регистрируются явно при настройке рендерера: имя + порядок отрисовки
|
||
(например, `Background → World → Foreground → UI`).
|
||
- У каждого слоя — **пространство**: `World` (через трансформ камеры) или
|
||
`Screen` (screen-space, без камеры — HUD, UI).
|
||
- Сортировка внутри слоя — по `float depth` спрайта; слой можно переключить
|
||
в режим **Y-sort** (depth = позиция Y, для top-down игр).
|
||
- Спрайт ссылается на слой компактным id (`LayerId`), не строкой.
|
||
|
||
### Culling
|
||
|
||
- Перед записью вершин AABB спрайта (с учётом rotation/scale) проверяется против
|
||
world-прямоугольника камеры; невидимые сущности не попадают в батчер.
|
||
- Screen-space слои не куллятся по камере (они всегда на экране).
|
||
- Culling — простой broad-phase в draw-системе, без пространственных структур;
|
||
если профилирование покажет, что на больших мирах его не хватает,
|
||
добавим spatial hash отдельным этапом.
|
||
|
||
### Батчер
|
||
|
||
- Draw-системы итерируют чанки Friflo (`Chunks<Position, Sprite, ...>`) и пишут
|
||
вершины напрямую в CPU-буфер батчера — без промежуточных списков и аллокаций.
|
||
- Порядок сортировки: **слой → depth (или Y) → текстура**; спрайты с одной
|
||
текстурой сливаются в один draw call (динамический vertex buffer + общий
|
||
quad index buffer).
|
||
- Текстурные атласы — первоклассный гражданин: `Sprite` хранит регион атласа,
|
||
спрайты одного атласа батчатся автоматически.
|
||
- Цель по производительности: ≥100k спрайтов при 60 FPS на среднем десктопе,
|
||
0 аллокаций на кадр. Контролируется бенчмарками (BenchmarkDotNet) и
|
||
стресс-сценой в Sample.
|
||
|
||
## Интеграция ECS
|
||
|
||
- На каждую сцену создаётся свой `EntityStore` (мир) и **два** `SystemRoot`:
|
||
`UpdateSystems` (логика) и `DrawSystems` (рендер). Порядок систем детерминирован
|
||
и задаётся явно при регистрации.
|
||
- Модули подключаются к сцене extension-методами из `OnLoad`:
|
||
`scene.UseRenderer2D()`, `scene.UseInput()`, `scene.UseSpriteAnimation()`,
|
||
`context.UseAssets()`, `context.UseAudio()`. Сервисы модулей живут в
|
||
`EngineContext.Services` (общие между сценами), системы — в сцене.
|
||
- Базовый трансформ — один компонент `Transform2D` (position + rotation + scale):
|
||
один массив на чанк дешевле трёх отдельных компонентов.
|
||
- Модули поставляют готовые компоненты и системы
|
||
(например, `Sprite` + `SpriteRenderSystem` из `Graphics`), игра добавляет свои.
|
||
|
||
## Кадр (frame pipeline)
|
||
|
||
```
|
||
Game.Update ──► Scene.Update ──► SystemRoot (Update-фаза): input → логика игры → анимации
|
||
Game.Draw ──► Scene.Draw ──► SystemRoot (Draw-фаза): камера → culling → запись вершин в батчер → flush (draw calls)
|
||
```
|
||
|
||
## Структура репозитория
|
||
|
||
```
|
||
MrGameEng.sln
|
||
src/ библиотеки движка (MrGameEng.*)
|
||
samples/ MrGameEng.Sample — демо всех возможностей
|
||
tests/ xUnit-тесты, по проекту на библиотеку
|
||
docs/ документация (русский)
|
||
```
|
||
|
||
## Зафиксированные версии
|
||
|
||
| Зависимость | Версия | Назначение |
|
||
|---------------------------------|----------|----------------------------------|
|
||
| .NET (target framework) | net8.0 | |
|
||
| MonoGame.Framework.DesktopGL | 3.8.4.1 | Базовый фреймворк |
|
||
| Friflo.Engine.ECS | 3.6.0 | ECS |
|
||
| FontStashSharp.MonoGame | 1.5.6 | Шрифты (ttf) в рантайме |
|
||
| NVorbis | 0.10.5 | Декодирование ogg |
|
||
| dotnet-mgfxc (dotnet tool) | 3.8.4.1 | Компиляция шейдеров при сборке |
|