Entity component system, основы
Entity Component System (ECS
) - это архитектурный паттерн, набирающий все большую популярность у разработчиков, желающих избежать ада ситуации “проще переписать все с нуля” в случае добавления или изменения игровых механик. Этот паттерн позволяет уменьшить связность между различными игромеханиками, что дает возможность добавлять / убирать / менять их независимо друг от друга и без потерь в производительности. Давайте разберем основы ECS.
ECS базируется на следующих 3 определениях: entity (сущность), component (компонент) и system (система). Собственно, акроним ECS и есть сокращение этих 3 слов по их первым буквам.
Сущности (Entities)
Сущность
- это любой объект в игре. Например, это может быть юнит игрока, кнопка в интерфейсе, событие с данными от одной системы к другой и т.п. Сущности сами по себе не имеют свойств и выступают контейнерами для компонентов. По аналогии из игрового движка unity - это GameObject
.
Компоненты (Components)
Основная идея ECS - переход от стандартной ООП-подхода Inheritance (Наследование)
к Composition over Inheritance (Композиция вместо Наследования)
. Это позволяет резко уменьшить количество боли в попытках смешивания различных поведений с необходимостью ломания родительского класса в некоторых случаях - вместо иерархии наследования мы разделяем данные на изолированные блоки (Компоненты
) и “набираем” из них как из кубиков нужную нам информацию об игровом объекте, добавляя компоненты на сущности:
Компоненты не должны иметь какой-то бизнес-логики по своей обработке и содержат только данные - это очень важно и является ключевой особенностью по расширению функционала в дальнейшем. И нет, это не означает, что ссылочные типы запрещены (которые тоже могут быть данными, сервисами или кешами других данных) или компоненты не могут содержать вспомогательной обслуживающей логики (это часто используется для корректной очистки/реинициализации полей, пулинга и т.п) - это рекомендация по отсутствию основной бизнес-логики в типах данных компонентов.
Системы (Systems)
Но мы не можем создать поведение только лишь с чистыми данными, нам нужно как-то обрабатывать их. В ECS эту роль берут на себя Системы
- блоки кода, которые каким-либо образом обрабатывают требуемый набор компонентов на сущностях.
Системы не содержат никаких локальных данных или ссылок на сущности или компоненты (за исключением кеширования для повышения производительности), они служат только для обработки потока отфильтрованных по их условиям сущностей и компонентов, висящих на этих сущностях. Да, это рекомендация, и системы могут держать в себе любые данные, но тогда корректность обработки и очистки этих данных пользователь берет на себя.
Сущности, компоненты, системы - зачем такие сложности? Такой подход (разделение логики и данных) позволяет комбинировать любое сложное поведение набором простых систем, каждая из которых будет обрабатывать только нужные этой конкретной системе данные на сущностях. В результате мы можем добавлять и удалять системы без изменений (или с минимальными изменениями) в других системах.
Может возникнуть вопрос - разве мы уже не имеем то же самое в Unity
? В чем отличие между GameObject / MonoBehaviour
и ECS
? Отличие есть: Unity
- это не ECS
, а EC
:
GameObject
=>ECS Entity
.MonoBehaviour
=>ECS Component
+ECS System
.
Если мы захотим создать 10000 объектов в игре с простым поведением (реализованным внутри MonoBehaviour
) - производительность будет не очень хорошей, об этом была статья от самих разработчиков Unity. Если мы отделим логику от данных и перенесем ее в Менеджеры
, как предложено в статье - мы по сути получим тот самый ECS
, но с рядом ограничений. Т.е если рассматривать движение от простого к сложному, это можно описать следующей цепочкой: MonoBehaviours -> Managers -> ECS
.
Решения, готовые к применению
Самое популярное решение для Unity
- это Entitas (существуют порты под другие движки и языки). очень большой и сложный проект, пытающийся упростить жизнь разработчика путем автоматической генерации большого количества кода различных оберток и вспомогательных классов для более простого управления данными. Это главная как положительная, так и отрицательная особенность этого проекта. В Runtime будет добавлено порядка 0.5Mb, генерация кода и интеграция в редактор - порядка 3Mb.
Разработчики Unity
сейчас активно разрабатывают свою версию ECS
, которую обещают выпустить в каком-то виде в этом году. Ну… как обычно, никаких четких сроков, да и юнитехи в принципе не способны выпустить ни одну подсистему и сказать, что она стабильная и готова к использованию. Мы должны подождать еще год или два после официального релиза, пока они пофиксят все критические баги. Это значит, что встроенного решения можно не ждать слишком скоро.
Постоянная генерация кода и дополнительный размер в 3.5Mb - меня это не устроило. К тому же я слишком ленив, чтобы подробно изучить как Entitas
работает внутри в деталях - я просто взял и написал свой собственный ECS фреймворк с нуля - минимальный размер кода, минимальное потребление памяти (нет постоянных аллокаций и сборок мусора) в Runtime. Возможно, я еще напишу несколько постов об этой ECS
в будущем.