Suslik
ну вот, как я и говорил - букв много, толку ноль
ты вот лучше скажи сколько систем на реальном проекте а не демке
Suslik
> DOD формулируется не господом богом, а людьми, и нет никакой гарантии, что их формулировка является безупречной.
DOD зависит от архитектуры железа
/A\
> DOD зависит от архитектуры железа
выравнивание по размеру кэшлинии для каждой железке,да
innuendo
> выравнивание по размеру кэшлинии для каждой железке,да
То что сейчас архитектура всех популярных процессоров более менее одинаково это плюс.
Суть то в том, что при DOD производительность цпу приближается к максимальной.
Вот если бы был умный префетч, умный бранч предикшн, кэш оптимизированный под рандом аксесс, то можно было и не запорачиваться со всякими DOD и ECS, а использовать всем понятное ООП.
А как в анриле с DOD ?
/A\
> а использовать всем понятное ООП.
да ты шо - если не брать PowerPC в старых конзолях то всё чикипуки
> А как в анриле с DOD ?
я после последней правки её не вышел из запоя
Suslik
> Существуют игровые сущности, которые не являются обособленными объектами.
> Например, дождь, или отладочный рендер, который рендерит всё, что только можно.
> Или, например, логгер. Приянто ли такие сущности называть системами
Не осилил пока все сообщения в теме, но от себя хочу сказать, что например в моем доморощенном ECS отладочный рендер, да и простой рендер довольно удачно легли в отельную систему. К тому же очень удобно, надо отключить отладочный рендер - отключил систему и все.
А вот логгер просто сделал как синглтон для удобства использования везде где хочу.
Suslik
> короче, TL;DR, они как раз рассказывают про то, как ввели паттерн singleton
> component, который им показался полезным для инпута (потому что им тоже
> показалось странным для всех объектов хранить копию инпута или хранить один
> объект с компонентом ипута) и ВНЕЗАПНО у них 40% всех компонентов переехало в
> этот паттерн
Я эту презу не смотрел, но мне как-то сразу показалось логичным ввести сингл-компоненты. Я даже заточил свою ECS для оптимального и удобного использования таких. Ладно инпут, а тот же игрок (точнее компонент-маркер, который позволяет из множества персонажей определить где персонаж игрока) или камера которые предполагаются в единственном экземпляре. Да и много еще чего может быть такого. Тут главное не ограничивать себя.
Dimm Smile
> > Существуют игровые сущности, которые не являются обособленными объектами.
> > Например, дождь, или отладочный рендер, который рендерит всё, что только
> > можно.
отладочный рендер как отдельный компонент? спасибо не надо
innuendo
> отладочный рендер как отдельный компонент? спасибо не надо
Как отдельная система, которая работает с набором других компонентов. Конечно я не говорю, что это идеальное решение. У меня очень простой 2д рендер, топорный я бы сказал. Но мне этого хватает, тем более он же 2д, там можно не упарываться по мегаоптимизациям
вспомнил еще видео
Dimm Smile
> Как отдельная система, которая работает с набором других компонентов.
для простых примеров пойдёт
Подглядел в entt хорошую идею хранить разреженный массив индексов постранично, а не одним большим куском. Заимплементил себе :)
Теперь не страшно иметь редкие компоненты с большим entity_id - их индексный массив будет занимать минимум памяти, независимо от величины entity_id.
Query производительность чуть ухудшилась из-за дополнительной индирекции, но в целом время работы систем почти не изменилось. А в синтетических тестах на 10 млн сущностей наоборот всё ускорилось, точно не понял почему, мб L2/L3 кеш стал эффективнее использоваться.
Ещё интересный момент в entt - они не создают длинный variadic template с типами всех компонентов, а создают контейнеры под компоненты в рантайме. С одной стороны очень удобно, но с другой есть фатальный недостаток (с) - нельзя проитерироваться по списку всех типов компонентов, потому что сделано это на шаблонной конвертации типа в type_id (uint32), которая работает только в одну сторону. Есть метод visit(func), который вернёт этот type_id типа для каждого компонента, по которому можно максимум строковое имя вытащить, но сам тип уже не получить. Это же жутко неудобно.
То есть чтобы запилить что-то подобное (отображение инфы по каждому компоненту для конкретной сущности):
Нужно будет городить огород с интерфейсами или visiter'ами для каждого типа компонента и прочие бесполезные обёртки.
В то время как у себя я просто итерируюсь по всем типам std::tuple и бесплатно получаю каждый контейнер компонентов с их исходными типами, и могу безо всяких лишних обёрток заимплементить редактор/browser сущностей с отображением всех компонентов.
Alexander K
> мб L2/L3 кеш стал эффективнее использоваться.
опять писькомизации
Обнаружил (или скорее осознал) для себя интересный паттерн использования ECS, который разумеется кому-то может показаться очевидным.
При итерации по сущностям часто бывает что для выполнения какой-либо логики мы ждём/проверяем несколько условий. Если условия для некоторой сущности выполняются - выполняем логику. Пример с перезарядкой оружия (псевдокод):
for (auto& entity : registry.view<const ActionComponent, EquipmentComponent>) { auto& [action_c, equip_c] = entity.components; // проверяем, не выполняли ли мы действие Reload и не завершилось ли оно if ( action_c.action_type == ActionType::Reload && action_c.is_finished( )) { // собственно перезаряжаем текущее оружие equip_c.reload_current_weapon( ); } }
Проблема здесь может быть и очевидна, но в больших системах по мере разработки она часто ускользает от внимания. Мы перебираем набор компонентов каждый кадр и проверяем какие-то флаги чтобы изредка выполнить некоторую логику. Компонентов несколько, так что перебор здесь дополнительно дорожает.
По другому это можно сделать следующим образом: какая-нибудь ActionSystem и так знает, когда персонаж начинает перезаряжать оружие (выполнять action_type == Reload) - пусть она в этот момент создаёт например ReloadEventComponent. И далее логику связанную с перезарядкой мы будем выполнять уже только с этим компонентом:
for (auto& entity : registry.view<ReloadEventComponent, EquipmentComponent>) { // инстансов ReloadEventComponent в среднем много меньше чем ActionComponent // так что перебирать будем меньше auto& [reload_c, equip_c] = entity.components; // всё также проверяем некоторое условие if ( reload_c.is_finished( )) { equip_c.reload_current_weapon( ); } }
Или вовсе ReloadFinishedEventComponent:
for (auto& entity : registry.view<ReloadFinishedEventComponent, EquipmentComponent>) { auto& [reload_c, equip_c] = entity.components; // безусловно выполняем логику equip_c.reload_current_weapon( ); }
В итоге, например в ситуации когда ни один персонаж не перезаряжается, никакой код связанный с перезарядкой не будет выполняться. Profit. Кроме того, в коде становится меньше бранчевания логики, что делает его в среднем проще/понятнее/надёжднее.
Плата за такой подход - это траты на создание и удаление Event-компонентов, ну и увеличение количества типов компонентов. Поэтому очевидно стоит взвешивать насколько часто или редко выполняется та или иная логика, и выбирать:
- проверять каждый кадр
- проверять в рамках редкого, но чуть растянутого по времени события (ReloadEventComponent)
- проверять в рамках одноразового aka one-shot события (ReloadFinishedEventComponent)
Понятно, что если мы уже и так итерируемся по нужному набору компонентов и читаем их, то добавление одного условия может ни на что не повлиять. Но иногда бывает так, что после какого-нибудь рефакторинга замечаешь, что система только и делает, что проверяет несколько разных редких условий, которые можно раскидать на независимые события.
Дополнение:
- Вместо использования отдельного специального механизма событий внутри ECS, использование самих компонентов и сущностей для этого давно зарекомендовало себя отличной идеей.
- Описанное явно соответствует точно такой же ситуации и в не-ECS коде - бранчевание логики vs создание отдельных объектов с безусловной логикой (грубо). Только в ECS подходе ситуация более явная, и решение тоже более явное, отлично ложащееся на сам ECS подход.
Alexander K
> ActionComponent
Так сколько у тебя экш компонентов?