Всё что нужно знать про ECS

Моя цель - предложение широкого ассортимента товаров и услуг на постоянно высоком качестве обслуживания по самым выгодным ценам.

Прежде чем перейти к статье, хочу вам представить, экономическую онлайн игру Brave Knights, в которой вы можете играть и зарабатывать. Регистируйтесь, играйте и зарабатывайте!

Привет, Хабр! В этой статье я расскажу всё, что знаю про Entity-Component-System и попытаюсь развеять различные предубеждения об этом подходе. Здесь вы найдете много слов о преимуществах и недостатках ECS, об особенностях этого подхода, о том как с ним подружиться, о потенциальных граблях, о полезных практиках, а также в отдельном разделе коротко посмотрим на ECS фреймворки для Unity/C#.

Я совсем не дизайнер, поэтому все картинки в статье честно украдены
Я совсем не дизайнер, поэтому все картинки в статье честно украдены

Статья очень неспешно собиралась в течении двух лет, из-за чего вышла очень большой. Она хорошо подойдет для тех, кто хочет/начинает знакомиться с ECS. Люди же вкусившие ECS я надеюсь тоже смогут подчеркнуть для себя что-то новое. Если же вы делаете игры на любом отличном от C# языке, статья всё равно может быть вам полезна. Здесь не будет примеров кода и ни слова про историю паттерна, только мои опыт, рассуждения и наблюдения, а также опыт других ECS-фанатиков, за что им всем отдельное огромное спасибо :)

Содержание

Что такое ECS

Entity-Component-System - это архитектурный паттерн, созданный специально для разработки игр, он отлично подходит для описания динамического виртуального мира. Из-за его особенностей, некоторые считают его чуть ли не новой парадигмой программирования, это скорее не так, но мозг перестраивать скорее всего потребуется.
ECS возводит в абсолют принцип Composition Over Inheritance(композиция важнее наследования) и может являться частным примером Data Oriented Design(ориентированного на данные дизайна, далее DOD), однако это уже зависит от интерпретации паттерна конкретной реализацией.

Расшифруем название этого паттерна:

Как вы уже поняли из описанного выше: ECS строго отделяет данные от логики. Поведение объекта определяется не интерфейсам/контрактами/публичным API, как мы привыкли в классическом объектно-ориентированном программировании(далее ООП), а присвоенными объекту свойствами с данными + существующей отдельно логикой обработки. В ECS данные определяют всё - это и есть главное свойство, которое выделяет его на фоне других подходов к разработке: всё есть данные. И свойства объекта, и его характеристики, и даже события - всё это просто данные существующие в ECS-мире. Логика же является просто конвейерной обработкой всех этих данных. В некотором приближении, ECS можно сравнить с базой данных, которая обрабатывается каждый кадр потоком обработчиков написанных в стиле процедурного программирования :D

Entity-Component

Стоит отдельно проговорить, что нередко Entity-Component-System путают с очень близким архитектурным паттерном Entity-Component(иногда пишут как Entity-Component System, далее EC), но это большое заблуждение.
EC вы скорее всего уже встречали в различных движках, таких как Unigine или Unity(но их новый DOTS уже ECS). Главное отличие, как можно понять из названия - отсутствие выделенных под логику систем. В EC-подходе в компоненте хранятся и данные, и логика, а для изменения данных наружу торчит API. Компонент в таком случае уже не просто свойство объекта, а полноценное поведение, которое мы можем добавить нашей сущности. Каждый компонент, будучи отдельным объектом со своим API и ожидаемым поведением, зачастую сам обрабатывает или изменяет свои данные по чьей-либо просьбе.
Фактически, EC - классическое ООП с хорошей модульностью и сильным уклоном в сторону Composition Over Inheritance. Правда никто не запрещает сделать компоненты с чистой логикой и, как итог, получить опыт аналогичный ECS. Но вернемся к ECS…

Зачем ECS

Наверняка, на этом месте у вас уже возник вопрос: “А зачем мне этот ваш ECS вообще нужен? Какая от него польза?”. И чтобы помочь вам определиться читать ли вообще статью дальше, я расскажу чем лично мне так полюбился ECS. В дальнейшем раздел Pros and Cons раскроет подробнее и хорошие, и плохие стороны этого подхода, чтобы вы смогли окончательно определиться нужен ли вам ECS.

Лично я люблю ECS за то, что…

С ECS ты просто садишься и делаешь игру, а не воюешь с архитектурой проекта. Нет нужды строить большие и “красивые” иерархии, продумывать кучу связей и париться про “X же не должен знать про Y”. При этом принципы ECS защищают тебя(не на 100%, ессесно) от безвыходной ситуации, в которую заводит плохая архитектура, когда дальнейшее развитие проекта становится очень болезненным. И даже если всё таки что-то пошло не так - рефакторинг в ECS совсем не проблема. И это, на мой взгляд, самое кайфовое в ECS.

Код на ECS получается простым и понятным. Не нужно ползать по куче вызовов среди кучи классов, чтобы понять чем занимается конкретная система, всё видно сразу, особенно если грамотно разбивать фичу на системы, системы на методы и не переусложнять код. Вдобавок, ECS сильно упрощает профилирование: сразу видно какая логика(система) сколько времени кадра отнимает, не нужно искать источник лагов в глубине вызовов.

Очень легко манипулировать логикой. Добавление новой логики практически безболезненно - просто вставляешь новую систему в нужное место, не боясь напрямую повлиять на остальной код(стоит отметить, что возможно косвенное влияние через данные). Можно без каких-либо проблем использовать общую логику(системы) между клиентом и сервером, при сохранении используемых данных(компонентов), конечно. Можно легко переписывать системы, заменяя старые системы на отрефакторенные, при этом без какого-либо влияния на остальной код, не понравится результат - просто снова включаешь старую систему и выключаешь новую. Аналогичным механизмом можно легко устраивать A/B тесты.

Всё крутится вокруг данных. На поверку это оказывается дико удобно. С помощью прямого манипулирования данными на сущностях открываются широчайшие возможности для комбинаторики. Можно с помощью данных формировать сущность во что угодно: от камеры, которая убивает по касанию, до нематериального контейнера с конфигами. А если фреймворк предлагает инструментарий для просмотра данных на сущностях, то можно в любой момент изучить данные и их динамику на совершенно любой сущности без необходимости запуска дебаггера, чтобы заглянуть в память.

Как работать с ECS

Здесь я простыми словами, максимально абстрактно и без привязки к языку программирования опишу как проходит процесс разработки с использованием ECS на самом простом примере. Если у вас уже есть хоть какой-то опыт работы с ECS, то можете перейти сразу к следующему разделу :)

Задача: создать объект, который двигается в направлении заданного вектора движения.

Первым делом определим данные, необходимые нам для работы. Для нашей задачи потребуются позиция объекта и задаваемый вектор движения. На языке ECS это будут:

Следующим шагом опишем логику. Создаём систему MovementSystem. В главном методе системы, в зависимости от реализации это может быть Run()/Execute()/Update() или что-либо другое, получаем все сущности в ECS, у которых есть PositionComponent и MovementComponent. Как именно это можно сделать зависит от фреймворка, но зачастую это похоже на своеобразный SQL-запрос вида GetAllEntities().With<PositionComponent>().With<MovementComponent>().
Запускаем цикл по полученным сущностям и для каждой производим изменение позиции: positionComponent.position += movementComponent.velocity. Можно добавить *deltaTime, если вы не хотите зависеть от частоты вызова системы.

Ну и наконец, мы просто создаем сущность(или даже 10 штук) с двумя нашими компонентами, задаем вектор движения отличный от нуля и теперь при каждом вызове системы MovementSystem(вне зависимости от того где и когда мы ее вызовем) наш объект будет менять позицию в направлении заданного вектора движения. Задача выполнена! :)
Зачастую системы так или иначе встраиваются в GameLoop проекта и дёргаются каждый кадр самим движком, но можно это делать и руками, и любым другим способом, тк это просто вызов метода. 

Посмотрим какие дополнительные возможности для разработки мы получили помимо решения основной задачи:

Если нам захочется, то мы ещё и любую другую сущность сможем заставить двигаться, просто повесив на неё PositionComponent и MovementComponent, при создании игр это бывает крайне полезно.

Pros and cons

В этом разделе мы обсудим чем хорош ECS и чем он плох. Часть описанных ниже особенностей имеют две стороны медали: они одновременно и приносят пользу разработке, и доставляют дискомфорт, создавая ограничения, которые иногда приходится обходить.

Преимущества

Недостатки

Ошибки новичка

В этом разделе я поведаю об ошибках, которые более опытный я заметил за собой в своём старом коде и которые мешали мне в разработке, а также об ошибках, которые допускают многие новички когда только начинают осваивать ECS. Надеюсь это поможет обойти часть граблей стороной.

Good Practices

Сверху вы можете почитать о том как точно НЕ стоит делать при разработке на ECS, а теперь с примерами поговорим о полезных практиках и просто советах как можно делать.

Фреймворки для Unity/C#

Выбор новичка

Если вы новичок, предположу, что первым делом вы думали начать ковырять Unity DOTS, но я хочу вас притормозить: Unity DOTS не самый хороший вариант для начинающих, ибо он очень большой и сложный, а документации и опытных людей на нём пока не так много, как хотелось бы, к тому же он плохо совместим со старым Unity-кодом (это всё может измениться с момента написания статьи).

Если вы за минимализм, предпочитаете всё делать из кода(не редактора) или пишете на движке отличном от Unity, то для вас лучшими вариантами начать будут LeoECS или LeoECSLite. Первый вариант с более простым синтаксисом и в статусе LTS(только багфиксы), идеальный вариант для старта. Второй вариант менее юзер-френдли, но быстрее работает и активно развивается. Оба являются максимально простыми Engine-Agnostic фреймворками на C#, то есть подойдут для какого угодно использования. У них живое комьюнити, которое делает полезные расширяющие функционал модули(например, интеграция с Unity) и которое всегда может помочь советами.

Если же вы любите Unity Editor и уже привыкли вешать компоненты прямо на GameObjects, то вам лучше всего подойдет Morpeh с простым API, максимально плотной интеграцией в Unity Editor(при этом может работать и вне Unity) и удобной работой с монобехами. Коммьюнити у него пока небольшое, но автор всегда готов ответить на вопросы в ECS-чатике. Morpeh - моя личная рекомендация для начинающих. Он прост и удобен, всё нужное для работы с Unity в нём есть из коробки, просто устанавливаешь и работаешь. Главный его недостаток - он требует платный Odin Inspector для полноценной работы в Unity(но этот недостаток в будущем должен уйти).

С чем я работал

А теперь краткий обзор Unity/C# фреймворков, с которыми я познакомился лично и какие плюсы/минусы я в них заметил. Стоит подметить, что всё описанное ниже может измениться с момента публикации статьи, так что лучше проверять фреймворки самому, а не верить рандомному штурмовику на слово.

Entitas

https://github.com/sschmid/Entitas-CSharp
https://assetstore.unity.com/packages/tools/game-toolkits/entitas-87638
Самый старый для Unity/C# и до сих пор самый популярный. В вакансиях с ECS чаще всего встречается именно он.

Плюсы:

Минусы: 

LeoECS

https://github.com/Leopotam/ecs
https://github.com/Leopotam/ecslite
Минималистичный и простой в освоении Engine-Agnostic фреймворк с открытым исходным кодом. Второй по звёздочкам на Github(после Entitas) и один из самых быстрых по производительности. LeoEcsLite - своего рода вторая версия фреймворка, ещё быстрее и минималистичнее, но с чуть менее удобным API.

Плюсы:

Минусы: 

DOTS (Unity ECS)

https://unity.com/dots
Думаю не нуждается в представлении. DOTS скорее не фреймворк, а полноценная платформа(технологический стак) с Unity ECS в качестве фреймворка внутри. Хочу подметить, что ниже описан несколько устаревший опыт. Допускаю, что актуальный DOTS мог избавиться от описанных ниже проблем.

Плюсы:

Минусы:

Morpeh

https://github.com/scellecs/morpeh
Фреймворк с девизом “вставил и поехал”. Прост в освоении, быстрый, имеет много полезных фич из коробки и удобную интеграцию с Unity. Мой личный фаворит в плане удобства разработки под Unity.

Плюсы:

Минусы: 

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

Здесь я перечислю другие достойные внимания фреймворки, с которыми мне пока не довелось поработать лично, но с которыми я хотел бы познакомиться поближе.

Возможно в будущем я сделаю полноценную обзорную статью по фреймворкам для Unity/C#, но это будет уже совсем другая история.

Итог

Как вы могли заметить по большому списку недостатков - ECS не является серебряной пулей. У этого архитектурного решения, как и у любого другого, есть свои преимущества и свои недостатки, с которыми придется мириться, если выбирать разработку с использованием этого архитектурного паттерна. Так что выбор использовать ECS в своих проектах или нет - исключительно за вами, но я настойчиво рекомендую как минимум попробовать сделать небольшой проект на ECS, чтобы понять по душе вам такой подход или нет.

Чтобы вам было проще определиться, поделюсь с вами ссылками на русскоязычные доклады, которые мне кажутся достойными внимания:

Еще рекомендую заглянуть в этот англоязычный репозиторий, где есть ответы на множество вопросов связанных с ECS, есть ссылки на англоязычные доклады, список фреймворков под разные языки, а также примеры игр и программ, которые используют ECS.

Так же крайне рекомендую ламповый чатик в Telegram, где вы найдете орду ECS-фанатиков, с которыми всегда сможете перетереть за ECS вне зависимости от языка и движка, получить ответы на интересующие вопросы(если они не будут уровня “не читал readme”), поспорить обо всяком и узнать много нового, присоединяйтесь :)
Попутно хочу ещё раз выразить благодарность участникам чата: без их дискуссий и обсуждений эта статья вышла бы куда более унылой и пустой.

С моей личной колокольни ECS выглядит отличным вариантом для создания игр. Разработка на нём, лично для меня, сплошное удовольствие: ты именно что разрабатываешь игру, а не пытаешься придумать как интегрировать новый код в старую систему ничего не сломав. Стоит иметь в виду, что на удобство ECS разработки сильно сказывается выбор фреймворка, поэтому пробуйте разные варианты и выбирайте тщательно.

Исходя из своего опыта, я склонен думать, что за ECS(или его вариацией) будущее разработки интерактивных развлечений. И не только потому что Unity Technologies(и, возможно, даже Epic) выбрали его основным направлением, но и просто потому, что ECS обладает выгодными в контексте разработки игр преимуществами. Да и в целом это практичный подход, который кажется неудобным на старте, но приносит свои плоды на дальней дистанции. Хороших и успешных игр вам!

Напоследок, ссылки откуда я угнал картинки, это всё англоязычные статьи про ECS:
https://devforum.roblox.com/t/ecs-lua-a-tiny-and-easy-to-use-ecs-entity-component-system-engine/841175
https://devlog.hexops.com/2022/lets-build-ecs-part-1/
https://github.com/a327ex/blog/issues/24
https://blog.lmorchard.com/2013/11/27/entity-component-system/
https://yos.io/2016/09/17/entity-component-systems/

Источник: https://habr.com/ru/post/665276/


Интересные статьи

Интересные статьи

Данная статья - это не научный прорыв, а лишь помощник быстрее понять как работает стандартный функционал в BitrixДавайте представим, что в разделе каталога у нас 150 запросов к БД. Вроде бы немного п...
Представляю вашему вниманию руководство по Sequelize. Sequelize — это ORM (Object-Relational Mapping — объектно-реляционное отображение или преобразование) для работы с такими СУБД (си...
Хабр, привет!На днях мне посчастливилось поучаствовать тут (обойдемся ссылкой на запись, без прямой рекламы :-)), где обсуждалась такая новая тема на рынке ИБ, как XDR. П...
Бизнес-смыслы появились в Битриксе в начале 2016 года, но мало кто понимает, как их правильно использовать для удобной настройки интернет-магазинов.
Если Вы используете в своих проектах инфоблоки 2.0 и таблицы InnoDB, то есть шанс в один прекрасный момент столкнуться с ошибкой MySQL «SQL Error (1118): Row size too large. The maximum row si...