Spiiin's blog

ECS - типы и проблемы

Where is my components

Одна из идей ecs — держать компоненты рядом в памяти и обходить в порядке хранения в памяти. По способу организации хранения ECS FAQ выделяет такие группы:

  • Реактивные ECS

Entitas - компоненты валяются где-то на хипе, entity хранит указатели на компоненты. Добавление и удаление компонентов с помощью событий уведомляет системы, чтобы они обновили свои списки. О хранении рядом речи не идёт. На самом деле, по типу хранения тут скорее “Entity based”. Идея с тем, чтобы уведомлять системы при изменении списка компонента на сущности реализована и в других типах ECS

  • Bitset based

EntityX - компоненты хранятcя в отдельных пулах, entity хранит битовую маску, присутствует ли компонент в конкретной entity. Быстро, но очень расточительно в плане количества потребляемой памяти.

  • Sparse set

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

Managing Decoupling Part 4 — The ID Lookup Table - простое объяснение.
ECS back and forth Part 2 - Where are my entities? - объяснение использования в EnTT.
Groups - более детальное объяснения того, как ускорить доступ к паре компонентов, которыми владеет одна система. Совсем кратко — если система владеет компонентами A и B, то она может хранить их так, чтобы при обращении за сущностями с обоими компонентами компоненты этих сущностей всегда лежали в начале массивов.
Part 2, insights - Sparse sets and grouping functionalities - еще раз подробно о способе сортировки групп.

  • Archetypes (aka “Dense ECS” or “Table based ECS”)
    flecs - компоненты хранятся в архетипах. Архетип — сочетание уникального множества компонентов. Для каждого архетипа существует отдельный массив. Компонент хранит список архетипов, которые его включают. Сущность хранит указатель на массив архетипов, и индекс внутри этого массива (по которому получает сразу все свои компоненты).

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

Building an ECS #1: Where are my Entities and Components и
Building an ECS #2: Archetypes and Vectorization - идея расскладыванием на архетипы

decs - библиотека ecs в daScript на архетипах
decs_boost - набор макросов, с помощью которых обращение с архетипами синтаксически похоже на обычный императивный код

Идеальным сочетанием для скорости выглядит использование архетипов в тех системах, где 1) не происходит постоянного добавления/удаления компонентов и 2) компонент используются только небольшим количеством систем (в примеры приводят низкоуровневые физические и графические подсистемы) + использование sparse sets там где существует большое количество типов компонент, которые постоянно добавляются/удаляются и систем, использующих разные сочетания компонентов (игровая логика).

Проблемы с ECS

На чистом ECS сложно выразить некоторые распространённые в геймдеве задачи:

  • Иерархии
  • Конечные автоматы
  • Декларативный GUI
  • Порядок работы систем (и повторный запуск)
  • Расширение/наследование компонентов
  • Контракты компонентов (взаимоисключающие сочетания, неудаляемые и т.п.)
  • Отображение объекта из нескольких сущностей как целого

И более редкие

  • Общее использование компонента сущностями
  • Несколько компонентов одного типа на сущности

ECS: From Tool to Paradigm - список проблем
ECS Questions + More ECS Questions - еще пара разборов ситуаций в игровом коде и способов их решения — порядок вызова систем в иерархических структурах, применительно к подсистеме анимации.

Kruger Engine Entity Model

Game Engine Entity/Object Models — большой обзор ECS подхода от Bobby Anguelov, с предложением гибридного подхода.
Kruger Engine Entity Model — презентация про демо-движок с подходом из видео-доклада.
Esoterica - более позднее название демо-движка

Системы делятся на глобальные и локальные.

  • Глобальные

Чтобы передавать информацию между сущностями, и обновлять состояние мира

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

Разрешено добавление нескольких компонентов одного типа на сущность, чтобы упростить построение иерархий (меш + сабмеши).
Разрешено явное задание пространственной иерархии между сущностями (с помощью Spatial Component, который имеет список дочерних Spatial Component и хранит пространственные матрицы)