Как начать использовать ECS в Unity

Как перестать бояться ECS?
Этот туториал предназначен для тех, кто давно хотел попробовать ECS в Unity, но не решался. Существует множество статей о реализации игровой логики с различными решениями ECS в Unity, однако мало кто объясняет, как разобраться в сути ECS на начальном этапе и начать применять этот паттерн на практике. Сложно вникнуть, когда туториал начинается с фразы «Давайте создадим систему», а вы не понимаете, что такое система, зачем она нужна и как она поможет в разработке игры. В этой статье мы в общих чертах разберем основы проектирования игры с использованием ECS, чтобы вы не получили негативный опыт и не разочаровались на стадии обучения.
Как работает типичная игра на Unity? У вас есть игровые объекты: игровой персонаж, враги и статичные объекты сцены. Для каждого из них вы пишете скрипты, которые управляют их поведением. Например, для персонажа это может быть CharacterController, а для врага — Enemy.cs. В этих скриптах в методе Update вы реализуете движение, а в полях описываете скорость, HP и т.д. Чтобы описать разное поведение для разных врагов, вы создаете скрипты по типу Enemy2.cs, Enemy3.cs и т.д., наследуя их (или нет) от Enemy.cs.
Этот подход хорошо работает для простых игр, но по мере усложнения вы начинаете путаться в связях между объектами, в том, где что создается и как контролировать тот или иной параметр. Чтобы избежать этого, вы пытаетесь структурировать логику и представление, применяя какой-нибудь паттерн, но еще больше запутываетесь, пытаясь понять, к какому типу сущности отнести игровой объект и визуальный эффект.
Чтобы избежать всех этих проблем, можно использовать ECS.
Что дает и как работает ECS?
ECS разделяет игровой мир на сущности, компоненты и системы. Помните CharacterController? В ECS он не будет содержать полей и Update. Поля станут компонентами, Update перейдет в систему, а CharacterController станет сущностью.
Примерно так выглядит игра на ECS:
Это упрощенная схема работы ECS, но она наглядно показывает, как сильно упрощается проектирование.
Давайте рассмотрим простой пример, используя абстрактный код, не прибегая к конкретному ECS-решению. Например, у вас есть игровой объект (сущность), который может летать. Как мы реализуем его логику в ECS?
Создадим компоненты (компоненты — это простые C# классы): Fly, Speed, Rigidbody.
- Компонент Fly не будет содержать значений, а будет использоваться только для фильтрации при поиске сущностей в системе (системы — это простые C# классы).
- Speed будет содержать поле Value = 10.
- Rigidbody будет содержать поле Rigidbody, в которое мы задаем ссылку на реальный Rigidbody при создании сущности.
Далее, в системе, создающей сущности, мы создаем сущность Bird и добавляем к ней компоненты Fly, Speed, Rigidbody.
Теперь, в системе, отвечающей за полет, мы можем найти нашу Bird, проверив:for (var e in entities) { if(e.hasFly) e.Rigidbody.AddForce(e.Speed.value); }
вот и все, мы получаем всех птиц которые могу в Fly и задаем им скорость в Update, так как наша система вызывается в Update (ну или FixedUpdate). Все ECS для Unity схожи, но незначительно отличаются в API, поэтому я старался не приводить конкретных примеров, а только помочь вникнуть в суть. Не стоит бояться пробовать, лучше потратить время на изучение ECS чем тратить больше времени на исправление проблем игры без продуманной архитектуры.