Привет хабр.

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

Набор мнений очень сильно варьируется, но главная мысль проста - вам следует попробовать, а вернее НЕТ, вам следует использовать ECS в вашей игре!
И если с первым я могу согласиться, то вот второе предлагаю обсудить.

Что такое ECS?

ЕCS расшифровывается как Entity Component System и является специализированной высокопроизводительной архитектурой, применяемой в играх. Название описывает основные компоненты:

  • Entity - сущности, являются логическими объектами системы, по факту являются идентификаторами объектов.

  • Component - компоненты, содержат свойства сущностей.

  • System - системы, содержат логику обработки компонентов.

Тут важны детали. В классическом подходе в Unity также существуют сущности (GameObjects), компоненты и, по заветам инкапсуляции, методы по их обработке. При желании их можно вынести в отдельные статичные методы и отделить данные от логики, но обычно так делать не рекомендуют и на то есть веские причины. Так в чем же разница?

Почему ECS?

Производительность.

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

Сравнение скорости разных операций в компьютере
Сравнение скорости разных операций в компьютере

Для решения этой проблемы используют кэш процессора - очень быструю (и дорогую) память, расположенную обычно прямо на кристалле процессора. Работа с кэшем по скорости сопоставима с другими операциями процессора. Таким образом, когда вы, скажем, хотите сложить два числа, в кэш загружается целый блок памяти в несколько килобайт. Данные в программе обычно расположены достаточно локализовано - работая с объектом, вы используете его поля, работая с функцией - кусочек стека, массивы расположены в памяти по порядку, то есть такой подход значительно ускоряет вычисления.
Но в какой то момент вам могут потребоваться данные из блока, не входящего в кэш. Это называется “Промахом кэша”. В этот момент процессор начнет простаивать, ожидая нужные данные из обычной памяти. Конечно это упрощенная схема, внутри современных процессоров какого волшебства только нет, но в целом, думаю, понятно. Вы даже можете легко проверить мои утверждения на классическом примере “Массив структур” vs “Структура Массивов”. Можно сделать массив из 100000 экземпляров структуры из 10 float полей и 10 массивов по 100000 float чисел, после чего возвести в квадрат числа в первом поле / первом массиве. С большой вероятностью массив окажется значительно быстрее.

Так вот. Классическое ООП - злейший враг локальности кеша.

Объекты создаются в случайных местах в памяти, что вызывает постоянные обращения к различным блокам памяти и очень не оптимально.

Постоянно используя ООП с его полиморфизмом, большим количеством объектов в памяти, виртуальными функциями и прочими удобствами, мы платим достаточно большую цену в терминах производительности.

Как работает?

ECS призван это решить, и именно для этого нужны системы, компоненты, и другой способ обращения к объектам.

Теперь за выделением памяти следит ECS, она располагает все компоненты в чанки - массивы с заранее выделенной памятью и одним типов компонентов.

Для адресации вместо ссылок в памяти мы используем сущности. По сути это идентификатор, прикрепленный к компоненту в памяти, чтобы понимать к чему он логически относится.
Ну и наконец мы используем системы для групповой обработки компонентов. Важно то, что система это не просто статическая функция для определенного компонента. Это статические функции для массивов, которые выполняют не одну операцию, а одну операцию над каждым элементом в массиве.
Группируя данные близко друг к другу и обрабатывая их за один проход мы максимально используем процессорный кэш, а также можем эффективно использовать векторные инструкции процессора.
Ура!

На самом деле не все так просто. Точнее просто для простых случаев. Дело в том, что умножить 2 массива матриц это действительно просто и быстро, но обычно нам не это нужно. А нужно нам умножить их только для четных объектов. Или для объектов с определенным компонентом. Или если в компоненте жизней меньше 0. И сделать еще 38 компонентов и 80 систем для различных аспектов их обработки и игровой логики.
Поэтому системы предоставляют API для фильтрации и компоновки компонентов.

Но и это не решает всех проблем. Для одних случаев обработки будет оптимальнее использовать компонент в виде сочетания позиции и направления (операции по передвижению затрагивают обе переменных), для других их стоит разнести по разным. Оптимумы будут разными для разных игр и компонентов, более того, могут быть разными в разных режимах игры.
И в любом случае это довольно сильно зависит от вашей конкретной логики, а соответственно от вашего понимания, что и как организовать.

ECS имеет и другие преимущества.

Самое важное - представление данных. Все ваши данные гарантированно собраны в единообразные брусочки-массивы, равномерные и одинаковые. Вы можете легко пройтись по ним, скопировать их простым копированием байт, легко сериализовать и десериализовать, отправить по сети, сохранить любой кадр состояния, организовать запись матча и перемотку.
Можно сказать, что это приятный и очень полезный побочный эффект.
Нужно отметить, что аналог “сериализованного контейнера состояния” можно организовать и при помощи классического ООП, просто повозиться придется больше.
Сохранить просто кусок памяти нельзя, ссылки придется перевести в другой формат при сериализации и восстановить при десериализации, могут быть ограничения на структуры, бинарное представление может быть сложным и т.д. Некоторые объекты могут потребовать отдельной сериализации.

И если вы не подумали про это на старте, реализация может оказаться весьма сложной, в то время как в ECS она есть из коробки.

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

Почему вам может не подойти ECS?

Так почему ECS вам может не подойти?
Все просто - она вероятно не решит ВАШИХ проблем с производительностью, но создаст большие сложности с обслуживанием кода.

Производительность неигровой логики.

ECS эффективна только с тем, с чем она работает, то есть с сущностями игровой логики. При этом сущности игровой логики на самом деле самая сложная, но не самая большая часть игры.
В контексте игры структура логического объекта обычно весит десятки байт, в то время как текстура может быть от сотен килобайт до мегабайтов, а каждый меш включает тысячи точек.
Пример с текстурами и мешами лукавый - они загружаются в видеопамять и крутятся там, оптимальные и эффективные стараниями инженеров из NVidia. Но на каждый супер эффективный рендер меша требуется большой объем вычислений - подготовка материалов, расчет параметров сцены, сортировка (для прозрачных) и группировка (для батчинга) объектов, переключение контекстов и т.д.

И это только рендер. Большая часть разнородных операций, ради которых мы, собственно, используем движки, работает на процессоре - композинг и проигрывание музыки и звуков, симуляции систем частиц и физики, разнородные логические компоненты, загрузка ресурсов, обслуживание дерева объектов, отрисовка интерфейсов. Сделать оптимальный рендер из миллиона треугольников не проблема, проблема сделать с ним игру.

Большая часть этих компонентов написана неплохо, вероятно хорошими разработчиками, и оптимизирована в ключевых местах. Разработчики аудио библиотек наверняка знают про эффективное использование массивов и кеш процессора. Но вопрос в количестве. Музыка будет все равно занимать больше килобайт и тратить больше тактов, чем типичная игровая логика движения монстров. Массив с параметрами символов шрифтов будет содержать больше элементов, чем средний массив с игровыми объектами.

Вам следует начать оптимизацию вашей игры с контроля этих величин, и никакой ECS вам не поможет.
Если вы инди - вам нужно следить за этим и попытаться разобраться, как это работает. Если вы в команде - вам следует объяснить это художникам, потому что никакая оптимизация объекта до 18 байт не поможет, если художник повесит на объект 15 систем частиц и сделает волшебный рейтрейсинг для вашей мобильной игры.
Unity не идеален, но весьма хорош, и некоторые его недостатки - продолжения его преимуществ.
На нем очень легко сделать тормозящий объект без единой строчки кода!

Наиболее эффективной стратегией будет тестировать, искать и лечить эти проблемы.

Есть и другой путь - вы можете переписать любой компонент, систему или функцию в кеш френдли манере, специализировать их для вашей игры. Таким образом вы можете оптимизировать и улучшить любой аспект игры.
Только придется переписать кучу готового кода. Нужно ли говорить, что пока это не тормозит, делать этого не нужно?

Производительность игровой логики

Отдельный вопрос, что многим играм в принципе не нужен ECS и в игровой логике. Посмотрите на популярные игры:
Шутеры? - Симуляция сотни игроков и тысяч пуль для ECS это даже не разминка, с этим легко справится обычный код, вы можете даже не получить преимущества.

РПГ? - Сотни монстров это скорее вопрос рендера, чем вопрос игровой логики монстра.

Стратегии? - даже тут это не всегда оправданно.
И мы даже не говорим про пиксельные инди рогалики и 3-в-ряд.
Отдельные части таких игр можно оптимизировать отдельно и при обычном ООП подходе, но большая часть в общем не требует оптимизации. Гораздо эффективнее организовать этот код в хорошо декомпозированные объекты и системы, с нормальными связями, понятными методами и хорошей иерархией.
Для сетевых игр будут полезны оптимизации структур и бинарная сериализация, но оптимизация заключается в том, какие данные передавать, а не в скорости сериализации. Если не передавать ненужные данные - они оптимизированы идеально.

Производительность альтернатив

Важно отметить, что обычное ООП тоже не прямо уж медленное. Компилятор умеет оптимизировать.

Случайные объекты в памяти располагаются не случайно - аллокаторы располагают объекты одинакового размера в одних блоках памяти (так проще потом их удалять и заменять, избегая фрагментации) таким образом объекты одного типа естественным образом попадают в одно место памяти.

Если мы создаем множество объектов за раз - они с большой вероятностью будут созданы в памяти по порядку. Таким образом создав массив с объектами мы получаем и локализованный массив со ссылками и с большой вероятностью локализованные объекты в этом массиве.

Часть объектов вообще не создаются в куче, потому что компилятор понимает что они короткоживущие. В то же время, ECS по сути сама занимается управлением памятью, и не факт, что во всех сценариях она делает это эффективнее.

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

Резюмируя: в вашей игре, вероятно, тормозит не игровая логика, соответственно и оптимизировать нужно не ее.

Сложность

Многие пропагандируют ECS как решение по умолчанию, потому что “просто используйте ее и получите хорошую производительность за бесплатно”.
Проблема в том, что эта серебряная пуля не бесплатная - ECS требует организовывать логику в весьма специфичной и организационно сложной форме, которая затрудняет чтение и написание кода.
С виду все кажется просто - 2 слоя, можно достучаться до любого компонента, что тут сложного?
Зато теперь у вас в проекте есть архитектура!
Но архитектура проекта это не только слои и паттерны, это ваши сущности и игровая логика. Ваши юниты, пули, эффекты и методы взаимодействия между ними - вот настоящая архитектура вашей игры.
Создание эффективных, удобных и понятных сущностей критически важно для вашей игры и ECS скорее мешает этому.

Во-первых, прежде удобные сущности, например, пулька, теперь разъединены на несколько компонентов и несколько систем. Помним про производительность? Позиция пульки и ее атрибуты должны храниться в разных компонентах, иначе вы потеряете в скорости. Порождение, передвижение, попадание - это теперь не разные функции в одном методе близко друг к другу, но 3 системы, где код организован не для вас, а для оптимальности прохождения массива.

Эта архитектура навязывает вам чрезмерную декомпозицию и когнитивная сложность понимания такого кода очень высока.

Чтобы понять полное поведение сущности, нужно найти все ее компоненты, все системы, обновляющие ее, и сопоставить их в голове в порядке выполнения (который настраивается порядком систем). При плохой декомпозиции может быть довольно сложно понять, где заканчивается юнит и начинается пулька.
А чтобы модифицировать ее, нужно не только понять, как оно работает, но и придумать необходимые модификации и, возможно, создать для них целую систему или новый компонент.

Во-вторых - у вас нет базовых примитивов. Хотите делегат? Их нет, используйте компонент. Полиморфизм? Нет, это enum и несколько систем. Фильтр по циклу? Вам нужен компонент + система. Нужно дерево? Деревьев нет, используйте компоненты со ссылками на сущности. Ах да, и быстро это будет работать только если вы организуете дерево правильно, произвольные связи поломают вам локальность кэша.
У вас нет половины современных инструментов программирования, только императивное программирование и базовые структуры.
Либо, вы можете использовать все, что захотите - тогда у вас будет мутант, который не имеет преимуществ ECS.


В-третьих - у вас нет привычных инструментов разработки. Для классического программирования есть нотации, области видимости, переходы по ссылкам, поиск классов, наследников и интерфейсов.
В ООП у вас есть приватные переменные и интерфейсы для сокрытия деталей, понятные паттерны организации кода, иерархическая организация. Конечно, классический код тоже можно сделать запутанным и с плохой декомпозицией, но для ECS его очень легко сделать таким.
Конечно, вы можете придумать какие то нотации, различные правила оформления объектов. Но вероятность ошибок, сложность понимания и расширения такого кода очень велики и быстро растут с ростом проекта.

Когда все же использовать ECS

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

Вы делаете Factorio.

Некоторые специфичные игры, действительно, могут требовать максимальной производительности игровой логики и взаимодействия большого количества игровых объектов. В блоге авторов Factorio можно почитать, какие хаки они применяли (они не используют ECS, просто своя реализация на C++), чтобы обрабатывать все эти тысячи брусочков меди на конвейерах в реальном времени и по сети. Нужно ли говорить, что это специфические и сложные игры? Возможно, вам не нужно представлять десятки тысяч слитков?

Вы делаете сложную сетевую игру.

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

Вы учитесь.

Думаю, тут понятно, попробуйте сами, не обязательно верить мне на слово. Только пожалуйста, на чем нибудь маленьком и дешевом.

Вы знаете, что делаете.

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

Вы используете ECS только для части проекта.

Важно понимать, что вся игра не обязательно должна быть написана в этом стиле. Возможно, оправданным будет как раз обратный подход - вы точно выделяете то, к чему есть эти супер требования. Стейт игры, синхронизируемая по сети модель сессии боя. Остальной код игры, интерфейс, мета часть, могут быть написаны в обычном ООП стиле, с упором на удобство и понятность кода, либо удобство и понятность расширения, редактирования дизайнерами и прочими требованиями.

Альтернативы

Кроме того, нужно помнить очень важную часть - вы можете оптимизировать отдельно любой специализированный аспект игры без ECS.
Практически все ее преимущества могут быть использованы отдельно от нее.

Хотите генерировать 3д воксельный ландшафт и генерировать к нему 3д меши? Возможно, вам не нужен ECS, а нужен специализированный компонент. Он может работать на нативных массивах и бинарных преобразованиях и использовать Jobs для параллельных вычислений.

Хотите супер быструю систему событий? Возможно, только ее и нужно написать специализированно.

Хотите миллион пулек? Вы можете сделать систему пулек очень быстрой, используя массив атрибутов, массив позиций и массив направлений, и сгруппировав передвижения в один метод. Останется только одна проблема - отрисовать их, и для этого не нужен ECS, а нужен DrawInstance.

Хотите высокой производительности для поиска пути? Вам нужно выполнение задач в других потоках.
Кстати, DOTS не требует ECS, его инструменты типа jobs можно использовать отдельно.

Вместо заключения

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

Юнити - отличный инструмент. У него есть много недостатков, но большая часть - это продолжение его достоинств:

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

  • Он мощен и позволяет собирать уровни, сущности и любые игровые запчасти почти без кода, используя стандартные компоненты и комбинируя их со скриптами программистов. Но это же позволяет сделать тормозящую игру без единой строчки кода! Художники и гейм дизайнеры могут создавать тормоза прямо в редакторе, и мы должны дать им такую возможность, потому что это упрощает разработку и действительно раскрывает их потенциал.

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

Не стремитесь за хайпом, постарайтесь понять, что нужно вам.

Комментарии (41)


  1. netricks
    30.09.2024 05:48

    Очень верно сказано про возможность оптимизации отдельных систем в без какого либо ECS.

    Вообще, сама идея ecs нарушает заповедь о преждевременной оптимизации.

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

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


    1. Lekret
      30.09.2024 05:48

      Я так понимаю речь про "ROC theorem: Readable, Optimised and Correct code."? Если да, то это какой-то левый сайт, где нет никакой математики и доказательств, поэтому хз кто осмелился назвать это теоремой. Максимум субъективное рассуждение. Другой "теоремы" найти увы не смог.

      Заповедь о преждевременной оптимизации это не заповедь и не фундаментальный закон, а совет, который много кем критиковался и который нельзя применять или использовать как оправдение в большом количестве кейсов.
      Да, не нужно оптимизировать прототипный или временный код, но нужно разделять оптимизацию и непессимизацию ((с) Кейси Муратори). Если библейские заповеди говорят мне написать какую-то медленную шляпу, когда я могу с теме же усилями написать что-то быстрее и не потерять в читаемости, либо даже потерять, но получить большой буст к перфе, то мой долг как программиста не следовать заповедям и догмам, а сделать так, чтобы у юзера был нормальный фпс. Мы пишем игры в конце концов.

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


  1. SH42913
    30.09.2024 05:48
    +8

    является специализированной высокопроизводительной архитектурой

    Да нет же, не является ECS "специализированной высокопроизводительной архитектурой". "Специализированным высокопроизводительным" может быть фреймворк, например, таким можно считать DOTS от Unity, который крутится вокруг ECS, но сам подход нельзя называть так, ибо разделение данных и логики само по себе НИКАК не говорит про производительность. И в этом моя главная претензия ко всем нелюбителям(и половине любителей) ECS и именно это сподвигло меня написать статью, на которую вы ссылаетесь в самом начале. ECS это в первую очередь удобство разработки благодаря отделению данных от логики, производительность вторична и оооочень сильно зависит от фреймворка. В то же время какой-нибудь DOD(Data-Oriented-Design, реализация ECS вполне может соответствовать DOD) уже - да, разговаривает про производительность.

    Статью пока дальше не читал(судя по оглавлению там только тейк про производительность другими средствами), но обязательно прочитаю как появится свободное время, так что ждите других комментов :)


    1. SadOcean Автор
      30.09.2024 05:48

      Это отчасти правда, но я не согласен с этим потому, что изначально эта архитектура была ассоциирована с решением проблем производительности.

      Cама концепция систем сосредоточена на пакетной обработке компонентов, иначе их бы можно было бы назвать командами и применять к отдельной паре компонентов. Есть известная цитата про нарезку хлеба (с которой я не согласен).
      Вторичная система индексации, когда у вас есть Entity и это просто индекс, так же направлена на это, иначе можно было бы просто использовать ссылку на объект entity.
      Все это вместе дает свою философию доступа к объектам, которая отличается от просто доступа по ссылке.

      Если бы речь шла только про декомпозицию и отделение данных от логики, вы бы могли (и можете) организовать это на обычных компонентах unity + системах, в этом смысле она и так entity-component.

      Но в целом, значительная часть моей критики и направлена на то, что ультрадекомпозиция и отделение данных от логики по умолчанию - это плохое решение, которое в долгосрочной перспективе сделает проект крайне сложным для обслуживания.
      Так что да, Я критикую и эту часть тоже)


      1. SH42913
        30.09.2024 05:48
        +1

        изначально эта архитектура была ассоциирована с решением проблем производительности

        Ассоциировать её с решением проблем производительности начали только после того как Unity занялись популяризацией DOTS(скорее всего подсмотрев у фростбайта). До этого все просто пользовались гибкостью.
        Я работал в одной конторе, где свой самописный ECS под Unity появился задолго до Entitas(то есть вообще до ECS-хайпа) и там это было обусловленно именно гибкостью разработки и оно было реально гибко. Собсно я с тех пор и полюбил ECS всем сердцем. Жаль проект не взлетел из-за в том числе проблем с производительностью фреймворка, ибо он был полностью на рефлексии и достаточно кривоват(но крут в плане возможностей).

        Все это вместе дает свою философию доступа к объектам, которая отличается от просто доступа по ссылке.

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

        Если бы речь шла только про декомпозицию и отделение данных от логики, вы бы могли (и можете) организовать это на обычных компонентах unity + системах, в этом смысле она и так entity-component.

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

        ультрадекомпозиция

        Ультрадекомпозиция это лишь один из взглядов на ECS, совсем не обязательно делить всё до атомарного уровня(а скорее даже не стоит)

        отделение данных от логики по умолчанию - это плохое решение, которое в долгосрочной перспективе сделает проект крайне сложным для обслуживания

        На моём опыте на дальней перспективе как раз классическая ООП иерархия или КОП доставляют больше жжения в жопе, чем ECS, в плане сложности добавления новых фич


        1. SadOcean Автор
          30.09.2024 05:48

          В моем опыте скорее аккуратный КОП является близким к идеалу.
          Но есть несколько важных нюансов, в частности размер команды, количество в ней ГД и то, как принято работать с игрой с точки зрения ГД - есть ли полноценный левел дизайн и работают ли технические художники и неспециалисты с уровнями и префабами. Либо они допущены только до конфигураций.
          В первом случае сосредоточиться на правильной декомпозиции и удобстве для последних на порядок важнее любой архитектуры и стандартные компоненты, ну ОК.

          Глубокие иерархии (наследования) однозначно зло. А вот иерархии объектов очень нужны (в смысле когда один класс использует напрямую другие - отряд - юнит - оружие - пуля).

          В этом контексте обычные способы декомпозиции предлагаемые ECS (типично иметь компонент-позицию, здоровье и компоненты-флаги, к примеру) превращают проект в сложную комбинацию эффектов, понять которые крайне сложно.

          Из описаний выше с Match-3 примером как раз потребовалось осознать, что ECS подход сам по себе не поможет. А помогла логическая декомпозиция того, из чего состоит реальная фишка на состояние, эффект и взаимодействие. Условно на триггер и эффект, которые можно сочетать независимо.
          Без этого код рос условиями каждый-к-каждому в том числе в ECS рос бесконтрольно и рожал много ошибок. Классический ООП тоже тут в общем то так же споткнулся бы.

          Сейчас Я бы лучше использовал что-то типа анемичного объектного подхода, не пытаясь тащить инфраструктуру какого то ECS подхода.

          Для проблемы веревки и проблемы декомпозиции вообще есть на самом деле простое решение - правило владения.
          Решать должен тот, кто знает про все свои детали.
          Соответственно в примере с веревкой ни юнит не содержит веревку, ни веревка - юнит, соответственно они не должны решать, как им взаимодействовать.
          Кто тогда должен решать - другой вопрос, на который этот принцип ответа не дает)


  1. Leopotam
    30.09.2024 05:48
    +10

    Херня написана - хорошую работу по промыванию мозгов проделали маркетологи юнитехов, которые рассказали, что ECS это про скорость и приравняли его к DOD (https://en.wikipedia.org/wiki/Data-oriented_design). Нет, ECS - это не про скорость, про скорость - это DOD. ECS не накладывает никаких требований и ограничений на то, как будут реализованы кишки, удобно ли для кеша разложены данные и будет ли оно максимально быстро обрабатываться - следование DOD является особенностью конкретной реализации. ECS - это архитектура, причем скорее архитектурный паттерн, не имеющей фиксированной реализации. Если бы оно было про скорость, то Entitas (https://github.com/sschmid/Entitas) никогда бы не появился на свет и не был бы до сих пор самым быстро растущим по популярности ECS-фреймворком (сейчас уже по 1к звезд в год) с 2015 года, ведь он весь на классах, через интерфейсы, на реактивных системах и местами постоянно аллоцирует память, про производительность вообще не стоит заводить разговор - это самая медленная реализация из всех существующих на сегодня.
    Смысл ECS не в скорости, а в гибкости и простоте рефакторинга. Если системы распилены на независимые части (features, "фичи"), то их можно наращивать и выпиливать почти безболезненно, пользуясь понятием "composition over inheritance". Берут его именно по этой причине, когда ТЗ или не готово, или меняется каждую неделю - с ECS рефакторинг проходит менее болезненно, чем в красиво выстроенной абстракции ООП-типов.
    Да, в ECS неприятно делать обработку любых иерархических данных типа графов, работы с UI и т.п, но не нужно все тащить в ECS, оно прекрасно живет рядом с ООП-сервисами, реализующими проблемные места.
    Непонятно, почему после миллиона статей про ECS продолжают приравнивать ECS к DOD или, что еще хуже, к UECS/DOTS, хотя это всего лишь частный случай реализации.


    1. SadOcean Автор
      30.09.2024 05:48

      Вы не правы в том, что производительность не ассоциирована с ECS.
      Практически все ее упоминания или реализации на этом сосредоточены и так было с момента ее создания.
      Cама концепция систем сосредоточена на пакетной обработке компонентов, иначе их бы можно было бы назвать командами и применять к отдельной паре компонентов.
      Есть известная цитата про нарезку хлеба (с которой я не согласен).
      Вторичная система индексации, когда у вас есть Entity и это просто индекс, так же направлена на это, иначе можно было бы просто использовать ссылку на объект entity.

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

      Остальные факторы могут быть достигнуты без ECS:

      • Composition over inheritance является хорошей практикой и в современном ООП сообществе, глубокие иерархии очень хрупкие.
        По сути обычная среда Unity - это composition over inheritance, это, можно сказать, entity-component архитектура.

      • Для достижения гибкости не нужна полная ECS, достаточно просто компонентов и Data-Driven дизайна, когда игровые сущности можно собирать из кусочков ориентируясь на данные.
        Именно поэтому все современные движки это так или иначе дерево узлов и компоненты на них.

      • Анемичный дизайн, когда логика и данные развязаны, тоже много где характерен и для него не требуется волшебства.

      • Хранимое состояние - опять же, для этого нужны какие то общие методы сериализации и десериализации объектов, а не вся архитектура.

      Не в последнюю очередь моя статья про то, что разделение логики и данных кажется хорошей идеей, но возможно это только кажется.
      ООП буквально придуман для того, чтобы собрать это вместе по хорошей причине - локальность данных и кода, защита целостного состояния объекта. Безусловно это может мешать, создавать хорошие абстракции непросто, но вы можете регулировать уровень этой локальности. Без этой абстракции создавать хорошие абстракции совершенно нереально.
      Я бы не сказал, что сложные задачи, где нужны гибкие отношения многих ко многим сильно упрощаются в ecs


      1. kipar
        30.09.2024 05:48
        +4

        ООП буквально придуман для того, чтобы собрать это вместе по хорошей причине - локальность данных и кода, защита целостного состояния объекта. Безусловно это может мешать, создавать хорошие абстракции непросто, но вы можете регулировать уровень этой локальности. Без этой абстракции создавать хорошие абстракции совершенно нереально.
        Я бы не сказал, что сложные задачи, где нужны гибкие отношения многих ко многим сильно упрощаются в ecs
        Я много лет писал игры (как хобби) на ООП. И основная проблема всегда была в том что в какой-то момент добавлять фичи становится сложнее чем всё переписать. И компоненты юнити тоже не слишком помогают - каждый компонент со временем превращается во все более и более сложную штуку и любое серьезное изменение затронет почти все из них. Допускаю что да, я не умею в правильную архитектуру. Но как ни продумывай архитектуру заранее, все равно в какой-то момент она перестает соответствовать реальности игры и ее надо переделывать, а заниматься этим либо лень либо некогда.
        А вот когда я попробовал ецс возвращаться к ооп нет никакого желания - больше никакого продумывания архитектур, просто декомпозируй компоненты и пиши фичи отдельными системами. Даже если нужны серьезные изменения, они будут локализованы в нескольких системах и что самое приятное - не надо задумываться к какому месту в иерархии правильно отнести этот код, вопросов типа "должен ли юнит знать о том что качается на веревке или это веревка должна знать о юните", просто добавляешь еще компонент и еще одну систему.


        1. SH42913
          30.09.2024 05:48
          +1

          не надо задумываться к какому месту в иерархии правильно отнести этот код, вопросов типа "должен ли юнит знать о том что качается на веревке или это веревка должна знать о юните", просто добавляешь еще компонент и еще одну систему.

          О, это прям классика, да


        1. SadOcean Автор
          30.09.2024 05:48

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


          1. kipar
            30.09.2024 05:48
            +2

            так это отдельный компонент - "качается на веревке". Да, можно его навесить на пулю или даже на постройку и они тоже будут качаться.
            Для меня это скорее плюс, т.к. заранее не предугадать в какие дебри сочетания механик заведет фантазия геймдизайнера и ецс как будто специально на это рассчитан.
            Да, есть и минус - нет типизации, т.е. можно случайно навесить компонент туда где его быть не должно. У меня был например такой баг - в платформере перестала работать статическая геометрия. Оказалось что я для отладки навесил на платформы компонент "можно подобрать" и забыл убрать. Игрок касался платформы, "подбирал" ее и падал сквозь нее. Но такие баги на мой взгляд - небольшая плата за удобство рефакторинга и расширения.


            1. SadOcean Автор
              30.09.2024 05:48

              Поинт всей статьи в том, что про то, как объект качается на веревке, придется задумываться в любом случае.
              Сказать, что ECS решит это за вас, безо всяких орг практик, дисциплины и организации - портал в маленький ад.
              Только ад не такой, как переусложненное ООП, а другой, когда ты не можешь найти концов и краев и все пули застревают в веревках, потому что при пролете на самом деле качаются на них.


              1. Lekret
                30.09.2024 05:48
                +3

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

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

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


      1. Leopotam
        30.09.2024 05:48
        +2

        Вы не правы в том, что производительность не ассоциирована с ECS

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

        Практически все ее упоминания или реализации на этом сосредоточены и так было с момента ее создания.

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

        Следующая известная веха - это GDC 2015, когда авторы entitas начали активно продвигать этот паттерн и конкретно свою реализацию в рамках юнити. Ни слова про производительность не было ни тогда, ни на следующем GDC2016, где опять же было выступление про удобство декомпозиции, добавления и удаления игромеханик без ломания остального кода.

        Юнитехи со своим "performance by default" заявились на ECS-сцене только в 2017 году и то не смогли ничего работающего показать раньше середины 2018, причем этим пользоваться было нельзя, а производительность была ужасна.

        Безусловно важным фактором является и разделение логики и данных - и, на самом деле, я критикую это не в последнюю очередь как способ создать реально сложные и плохо обслуживаемые решения

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

        ООП буквально придуман для того, чтобы собрать это вместе по хорошей причине - локальность данных и кода

        Внезапно, от локальности и когерентности данных в памяти/кеше и производительности по этой причине перешли к локальности данных по отношению к логике. В чем тогда был смысл рассказывать про производительность? Экземпляры ООП классов в общем случае всегда размазаны по памяти случайным образом и никогда не смогут догнать на тех же линейных переборах DOD-подход, когда данные лежат плотно и подряд (и нет, это опять сравнение не ECS с ООП, а DOD с ООП).

        Я бы не сказал, что сложные задачи, где нужны гибкие отношения многих ко многим сильно упрощаются в ecs

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


        1. SadOcean Автор
          30.09.2024 05:48

          Хм, а можно вопрос, вы разрабатываете проекты один или в команде?

          Моя проблема с ECS в том, что нельзя изменить все в произвольном направлении из-за когнитивной сложности - буквально крайне тяжело понять, как работает условная абстракция, если ее писал не ты.
          То есть такая проблема везде есть, конечно же, просто в ецс с произвольным разделением чтобы понять поведение, условно, юнита, нужно в голове поднять целый граф объекта - все компоненты позиции, здоровья, статусы и флаги.
          И несколько систем, обязательно в нужном порядке, потому что порядок, очевидно, критичен.
          В итоге любой процесс (скажем, получение урона, вместе со всеми деталями - просчетом доп типов урона, смертью персонажа, анимацией попадания и прочего) размазано по десятку систем.
          По факту компоненты флажки используются как переменные связывания вместо функций по порядку или событий.
          В итоге на ровном месте ты получаешь аналог event-hell.

          У меня есть стойкое ощущение, что люди живут с этим event-hell и такие "I'm fine", просто потому, что не знают, что есть методологии, как уменьшить это.
          Но это ведь сложно - правильные архитектуры нужно делать.

          В итоге на самом деле решения на ECS имеют архитектуру - это просто нотации и договоренности в команде, может документация, что за чем следует и какие окончания у компонентов, заведующих событиями. И так как инструментальных решений организовать это нет или мало, на самом деле это увеличивает требования к архитектуре.
          Если же ты вываливаешься за пределы упорядоченных систем, например если нужны сложные связи или дерево - это как писать на сырых ссылках в C++, тебе нужно проверять каждый объект на наличие компонент и возможность обработки.


          1. Leopotam
            30.09.2024 05:48
            +3

            Про идеи организации можно почитать вот тут https://habr.com/ru/articles/742376/
            Какие-то административные договоренности по взаимодействию нужны, да. Еще не стоит мельчить с компонентами, иначе это на самом деле может превратиться в неуправляемое месиво, когда никто не будет знать как и что должно работать. Функционал стоит делить на "фичи", когда есть блок компонентов, управляемых блоком систем, а снаружи к ним идет обращение через создание сущностей-событий или навешивание маркеров-событий, а дальше они сами работают. Когда логика разбивается на подобные логические блоки - управлять ей становится проще. Это все решается административными мерами - какими-то правилами, которыми должны руководствоваться все со-командники, т.к строгих правил не существует из-за гибкости самого ECS-подхода (одни правила подходят на одном проекте, другие на другом).


          1. SH42913
            30.09.2024 05:48

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

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

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


            1. SadOcean Автор
              30.09.2024 05:48

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

              Но одна из важных мыслей моей статьи - ECS не решает эту проблему автоматически.
              Так же и не приносит особых преимуществ на ранних стадиях - просто не используй ООП и пиши как попало, получишь ту же "все ко всем" декомпозицию


    1. SadOcean Автор
      30.09.2024 05:48

      И да, Entitas очень сосредоточен на производительности - он использует массивы структур, сложную систему запросов для выбора систем обработки компонентов и кодогенерацию.
      Мой основной опыт это Entitas и несколько самописных решений, Я не использовал Unity-ECS в коммерческих проектах.


      1. Leopotam
        30.09.2024 05:48
        +2

        Абсолютно нет, Entitas построен исключительно на классах, про структуры автор начал заговаривать в рамках разработки нового фреймворка с атомарными компонентами, где все разбивалось вплоть до отдельных значений, но это было в 2020 году и все благополучно умерло.

        Я свои самописные поделия начал делать как раз в конце 2017 как альтернативу платному кодогену Entitas, который проблемно ставился на macos. В начале 2018 была написана реализация на классах, которая обогнала или показала такую же производительность как Entitias, но без кодогена - задача была выполнена и появился LeoECS Classic, про Entitas больше не вспоминал. В 2019 эта реализация переехала на структуры и скорость работы еще возросла - Entitas все это время был в коме и не развивался.


        1. SadOcean Автор
          30.09.2024 05:48

          Хм, ваша правда, Я спутал entitas и Leo ECS


          1. Leopotam
            30.09.2024 05:48
            +2

            В LeoECS нет кодогенерации, ответственно заявляю как автор.


            1. SadOcean Автор
              30.09.2024 05:48

              ОМГ, а что я тогда использовал?
              Структуры и селекторы для классов были, но есть ощущение, что кодогенерация тоже была для гетеров.


              1. Leopotam
                30.09.2024 05:48

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


  1. SH42913
    30.09.2024 05:48
    +3

    Нушож, время обеда, можно и прочитать...

    Сейчас будет куча цитат и моих контр-аргументов.
    TLDR: автор путает DOD и ECS, смешивает ECS как архитектурный паттерн и реализацию ECS фреймворков, ну и вцелом не совсем понимает что представляет собой ECS архитектура.

    Одной из самых важных проблем, которые ECS призван решить, является производительность

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

    производительность доступа к памяти

    Этим занимается DOD, а не ECS

    Теперь за выделением памяти следит ECS, она располагает все компоненты в чанки - массивы с заранее выделенной памятью и одним типов компонентов.

    ECS про это ничего не говорит и не занимается этим, но вот ECS-фреймворк уже может устроить такую оптимизацию. Например, в тупую можно реализовать ECS так, чтобы у тебя сущность была классом, внутри которого лежат её компоненты(то есть без глобальной локальности памяти) в виде словаря type-object. И я даже встречал такие фреймворки...

    И если вы не подумали про это на старте, реализация может оказаться весьма сложной, в то время как в ECS она есть из коробки.

    Нету в ECS этого из коробки. В паре ECS фреймворков это есть, но лишь в паре.

    но создаст большие сложности с обслуживанием кода

    Может создать, а может и не создать. Как впрочем и любой другой паттерн, если его использовать неправильно.

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

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

    Продолжение будет в комментарях к этому комменту, чтобы не засирать нижний уровень комментов...


    1. SH42913
      30.09.2024 05:48
      +3

      Шутеры? - Симуляция сотни игроков и тысяч пуль для ECS это даже не разминка, с этим легко справится обычный код, вы можете даже не получить преимущества.

      А если завтра прилетит геймдиз, который захочет чтобы "камера убивала"? В ООП иерархии это может оказаться неподдерживаемым и придётся много костылить, а в ECS накинул компонент, добавил нужную систему(если вообще нужно) и всё готово.

      И мы даже не говорим про пиксельные инди рогалики и 3-в-ряд.

      Знаю разработчиков, которые очень довольны тем, что выбрали ECS для разработки 3-в-ряд как раз по причине гибкости :)

      Важно отметить, что обычное ООП тоже не прямо уж медленное.

      Так никто этого и не утверждает вроде бы. Я даже больше скажу: невозможно сделать игру на ECS совсем без использования ООП(ну ладно-ладно, можно, но это такой гемор, особенно на Unity), поэтому нормальной практикой считается комбинирования ECS с ООП там, где оно нужно.

      В то же время, ECS по сути сама занимается управлением памятью, и не факт, что во всех сценариях она делает это эффективнее.

      Не занимается ECS памятью. В C#-ECS-фреймворках это по прежнему делает компилятор за редким исключением, где нужен был полный контроль над памятью(как раз для сериализации, например).

      Резюмируя: в вашей игре, вероятно, тормозит не игровая логика, соответственно и оптимизировать нужно не ее.

      С этим утверждением я солидарен


    1. SH42913
      30.09.2024 05:48
      +1

      Многие пропагандируют ECS как решение по умолчанию, потому что “просто используйте ее и получите хорошую производительность за бесплатно”.

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

      Порождение, передвижение, попадание - это теперь не разные функции в одном методе близко друг к другу, но 3 системы, где код организован не для вас, а для оптимальности прохождения массива.

      Это не для оптимальности нужно, а для разделения ответственности. Одна система занимается тем, что спаунит объекты. Другая система занимается тем, что двигает объекты. А третья тем, что обрабатывает попадания(наносить урон будет скорее всего четвертая система). Классическая S из SOLID.

      Эта архитектура навязывает вам чрезмерную декомпозицию и когнитивная сложность понимания такого кода очень высока.

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

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

      Потому что в ECS нужно думать не объектами, а поведениями и тогда всё станет проще

      Во-вторых - у вас нет базовых примитивов. Хотите делегат? Их нет, используйте компонент. Полиморфизм? Нет, это enum и несколько систем. Фильтр по циклу? Вам нужен компонент + система. Нужно дерево? Деревьев нет, используйте компоненты со ссылками на сущности.
      [...]
      У вас нет половины современных инструментов программирования, только императивное программирование и базовые структуры. Либо, вы можете использовать все, что захотите - тогда у вас будет мутант, который не имеет преимуществ ECS.

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

      В ООП у вас есть приватные переменные и интерфейсы для сокрытия деталей

      Тебе не нужно скрывать детали, когда у тебя каждая логика и так обособлена в своей системе

      понятные паттерны организации кода

      Ну так ECS сам по себе и есть паттерн организации кода :D
      Я понимаю что хотел сказать автор, поэтому у меня есть контр-аргумент - для ECS уже тоже придуманы "понятные паттерны организации кода", как например в этой статье.

      Конечно, классический код тоже можно сделать запутанным и с плохой декомпозицией, но для ECS его очень легко сделать таким

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

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

      Частично согласен, об этом сказал выше. С другой стороны весов больших проектов у нас жёсткая ригидность ООП иерархии, когда вносить изменения в неё становится очень больно(или практически невозможно), в то время как в ECS ты просто добавляешь новый компонент и новую систему(можно к ней Unit-тест написать сразу) зачастую не трогая старый код.


    1. SH42913
      30.09.2024 05:48
      +2

      Но это всего лишь инструмент, необходимо использовать его правильно.

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

      могут требовать максимальной производительности игровой логики и взаимодействия большого количества игровых объектов

      Для этого в контексте Unity есть Jobs + Burst, ECS тут совсем не нужен.

      Жгите.

      Жгу :)

      Важно понимать, что вся игра не обязательно должна быть написана в этом стиле. Возможно, оправданным будет как раз обратный подход - вы точно выделяете то, к чему есть эти супер требования. Стейт игры, синхронизируемая по сети модель сессии боя. Остальной код игры, интерфейс, мета часть, могут быть написаны в обычном ООП стиле, с упором на удобство и понятность кода, либо удобство и понятность расширения, редактирования дизайнерами и прочими требованиями.

      Я бы как раз порекомендовал наоборот: вся игровая логика на ECS с вкраплениями ООП в нужных местах(деревья, сеть, сохранения и тп).

      Практически все ее преимущества могут быть использованы отдельно от нее.

      По отдельности преимуществ ECS можно достичь, писать код для идеальной тестируемости/модульности, заморачиваться про SRP, но зачем лезть из кожи с ООП если можно просто разделить данные и логику?

      Кстати, DOTS не требует ECS, его инструменты типа jobs можно использовать отдельно.

      Так и есть, поэтому и не стоит смешивая конкретную реализацию Entities из пакета DOTS с ECS архитектурой как таковой.

      Я кончил


      1. SadOcean Автор
        30.09.2024 05:48

        Мне кажется, что "если просто разделить данные и логику" - вот это тоже не правда.
        Это подается как простое решение, фигячь как хочешь, всегда понятно, что и где добавить.
        Проблема в том, что это не решает архитектурных проблем в организационной части.
        Архитектура это не только что у меня есть данные и код, архитектура конкретной игры - это конкретно ваши сущности, ваши пули, юниты и что там еще есть.
        Если это организовано само по себе разделение на логику и данные никак не влияет на то, что пуля должна наносить ущерб.
        Мы использовали ECS как раз для match 3 и когда речь зашла про возможное взаимодействие каждого с каждым ECS был организован так же сложно. Тут ООП подход ломается влегкую, Я согласен, но решение находится на организационной стороне, нужно придумать хороший список абстракций, возможно разделить процесс на триггер и эффект и тому подобное. Решение которое не зависит от деталей организации.
        И когда ты к нему приходишь, все равно придётся переписывать.

        Вместе с тем статьи по ECS описывают все так, как будто проблемы не существует, надо лишь избавиться от ООП и все станет легко и приятно.
        Вместе с тем, данные с процессами вместе соединяют не просто так.
        Есть несколько представлений, со стороны которых можно работать - со стороны состояния и со стороны процесса.
        И штуки как корутины, асинки, колбеки и прочие реактивные библиотеки придуманы для того, чтобы держать близко контекст процесса - действие за действием, которые можно обозреть.
        ECS представляет все как один шаг тысячи ног одновременно, что может привести к аду, когда ты пытаешься понять поведение сущности.
        В теоретическом абстрактном мире системы могут быть написаны независимо, чтобы можно было понять что происходит только по компоненту и системам, использующим его.
        На практике чтобы понять поведение условно юнита, нужно развернуть в голове целый граф из многих компонентов и систем в нужном порядке, понять важные и не важные части и только потом получить данные, чтобы внести изменения.
        ООП не идеален, но буквально заточен на эту изоляцию. ECS концептуально ее отвергает.


    1. SadOcean Автор
      30.09.2024 05:48

      Возможно вы правы и во многих статьях упор действительно делается прежде всего на композицию и решение архитектурных проблем.
      Тем не менее преимущества производительности подчеркивались тоже с ранних пор и многочисленные реализации, включая Unity ECS или ecs на rust напирают на это.
      Я встречал это многократно и про это же говорит типичная реализация, в которой entity - это идентификатор, а не объект, а системы предназначены для обработки данных батчами.
      По сути без этого ECS можно сделать на обычной юнити, просто используя GO и компоненты с публичными полями.
      Эти реализации тащат определенные правила - обычно для систем сущности выбираются неким специальным механизмом фильтров, вызывающим систему только на определенном наборе компонент / единичном компоненте.
      А работа с ссылками на сущности в обязательном порядке обрастает экстра кодом проверки наличия компонента на этой сущности.
      И то и другое вместе с овердекомпозицией серьезно запутывает код.

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


  1. yatanai
    30.09.2024 05:48

    Решились написать как-то простую демку, дабы проверить свои идеи о "тысячах юнитов на экране". После профилирования оказалось что UI занимает времени столько же, сколько рисование 50К юнитов и сервер для обработки их передвижения. Было грустно(


  1. nenuacho
    30.09.2024 05:48
    +4

    Так почему ECS вам может не подойти? Все просто - она вероятно не решит ВАШИХ проблем с производительностью, но создаст большие сложности с обслуживанием кода.

    Это причина по которой может не подойти ООП. ЕЦС как раз в первую очередь решает проблему обслуживания кода, пилить и поддерживать проект на нём гораздо проще, даже прототип


    1. SadOcean Автор
      30.09.2024 05:48

      Мой гет в том, что пилить и поддерживать на нем гораздо проще только прототип.
      А дальше это становится кучей кода с компонентным аналогом event-hell, который очень тяжело понимать и обслуживать, потому что инструментов организации, помимо нейминга, практически нет.


      1. SH42913
        30.09.2024 05:48
        +2

        Способы организации есть, вот в этих двух статьях расписаны :)
        https://habr.com/ru/articles/665276/
        https://habr.com/ru/articles/742376/


        1. SadOcean Автор
          30.09.2024 05:48

          Собственно и недостатки там те же перечислены, как и методы их решения, другой лишь фокус)


  1. DCFApixels
    30.09.2024 05:48
    +2

    Я докопаюсь только до этой строчки, так как все остальное тут уже вроде как разложили по кирпичику.
    > Эта архитектура навязывает вам чрезмерную декомпозицию и когнитивная сложность понимания такого кода очень высока.
    Тут во-первых буквально просто заменить слово декомпозиция на абстракция и стрелочка поворачивается. Во-вторых а в классическом ооп нет проблем с декомпозицией? Ну и в третьих, при использовании ецс во многих случаях декомпозицию можно проводить по мере разработки, например, создать жирный объект который включает в себя все данные и жирные системы которые обрабатывают эти компоненты, и далее по мере необходимости разделять на отдельные части.

    P.S. и если ECS это только про перформанс, то почему многие разрабы создают фреймворки для lua или js? Там кеш френдли в принципе не возможно.


    1. SadOcean Автор
      30.09.2024 05:48

      Для тру ООП как его многие представляют чрезмерная архитектурная космонавтика еще более характерна.
      И плохая декомпозиция.
      Но ярых фанатов в геймдеве я видел мало.
      Но ООП призывает начать с этого и признает проблему плохой декомпозиции. Собственно и ECS и многие паттерны для этого и нужны - потому что рефакторить больно.

      Но ECS конечно же призывает к ультрадекомпозиции. Посмотрите на представленные статьи - они буквально говорят о том, что надеваем прыгательный компонент и у нас все прыгает.
      Совершенно без контекста того, как самые разные объекты могут взаимодействовать с прыганием тоже треш получится


  1. DarkHaunt
    30.09.2024 05:48
    +2

    Открывая статью расчитвывал на более обширный список pros / cons, чем ходьба вокруг производительности.

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

    Но строить статью на аргументе "оно не настолько быстро, как вам кажется" сомнительно, как по мне.


    1. SadOcean Автор
      30.09.2024 05:48

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

      Я с этим не согласен, но создать целую статью лишь с болью про организацию, особенно без хороших альтернатив (а сложные ООП проекты не будут так же содержать серебрянных пуль) я не готов.

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


  1. noldowalker
    30.09.2024 05:48

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