🎮 Розробка ігор · Дані-орієнтований дизайн
📅 Березень 2026⏱ ≈ 10 хв читання🟡 Середній

Entity Component System

Традиційні ігрові архітектури використовують ієрархії успадкування, які погано масштабуються й неефективно працюють із кешем процесора. ECS перевертає підхід: замість об'єктів, що мають поведінку, ми маємо таблиці даних (компоненти), які перетворюють функції (системи). Результат може бути в 10–100 разів швидшим для великих світів.

1. Проблема ООП

Класичні ієрархії ігрових об'єктів виглядають так: Entity → Actor → Pawn → Character → FPSCharacter. Проблеми за масштабування:

Підхід ООП

  • GameObject з усіма можливими полями
  • Віртуальні методи update() для кожного типу
  • Успадкування для «видів» об'єктів
  • Виділення пам'яті для кожного об'єкта з переходами за вказівниками
  • Можливість = підклас або прапорець

Підхід ECS

  • Сутність = просто ідентифікатор (ціле число)
  • Компоненти = чисті структури даних
  • Системи = функції над наборами компонентів
  • Щільне масивне сховище для кожного типу компонента
  • Можливість = додати/видалити компонент

2. Основні поняття

Мінімальний ECS на JavaScript const world = new World(); const eid = createEntity(world); addComponent(world, Position, eid); Position.x[eid] = 0; // масив структур або структура масивів Position.y[eid] = 0; // Система function moveSystem(world, dt) { const query = defineQuery([Position, Velocity]); for (const eid of query(world)) { Position.x[eid] += Velocity.x[eid] * dt; Position.y[eid] += Velocity.y[eid] * dt; } }

3. Сховище архетипів

Домінівна стратегія зберігання — архетипи: групувати сутності, що мають точно однаковий набір компонентів, у суміжні масиви. Архетип визначається своїм набором компонентів (наприклад, {Position, Velocity, Health} → архетип A).

Ідентифікатори сутностей
сутність 1
сутність 4
сутність 7
Position[]
{1.0, 2.0, 0.0}
{3.5, 0.0, 1.2}
{-1.0, 4.0, 0.0}
Velocity[]
{0.1, 0.0, 0.0}
{0.0, 0.5, 0.0}
{-0.2, 0.1, 0.0}
Health[]
100
80
55

Усі компоненти сутностей у архетипі зберігаються у щільних масивах. Ітерація системи = послідовний доступ до пам'яті → чудова продуктивність кешу. Додавання компонента до сутності переміщує її до іншого архетипу.

Альтернатива — розріджені множини (sparse sets): для кожного типу компонента підтримується відображення розріджений→щільний. Краще для дуже великих просторів ідентифікаторів сутностей або частих змін архетипів. Flecs використовує архетипи; bitECS використовує розріджені множини; обидва можуть бути дружніми до кешу.

4. Системи та запити

Система оголошує запит: обов'язкові, необов'язкові та виключені типи компонентів. Рушій ECS повертає відповідні сутності. Системи можуть бути:

Приклад запиту Flecs на C++ world.system<Position, const Velocity>("Move") .each([](Position& p, const Velocity& v) { p.x += v.x * DT; p.y += v.y * DT; }); // Запит із виключенням: world.query_builder<Position>() .without<Frozen>() .build();

5. Ефективність кешу

Сучасні процесори у 10–100 разів швидші за затримку доступу до RAM. Рядок кешу — 64 байти. Випадкові переходи за вказівниками вбивають продуктивність; послідовний доступ до масиву максимізує повторне використання рядка кешу.

Архетиповий ECS природно дає SoA на кожен компонент у кожному архетипі. Проста фізична система, що ітерує 100 000 сутностей, може виконатися за ~1 мс (дружньо до кешу) проти ~10 мс із ООП-об'єктами з переходами за вказівниками.

Реальний бенчмарк: фізика Unity ECS DOTS зі 100 000 динамічних твердих тіл працює на 60 fps; еквівалентна фізика на основі GameObject ледве справляється понад ~5000 об'єктів. Різниця майже повністю в ефективності кешу + SIMD-автовекторизації щільних масивів float.

6. Зв'язки та теги

Сучасні фреймворки ECS виходять за межі плоских компонентів:

7. Реалізації

Коли використовувати ECS: ECS блищить для великої кількості подібних об'єктів (кулі, частинки, юніти, ШІ-агенти). Для невеликих проєктів із <1000 різнорідних об'єктів класичний ООП або патерни «компонент-на-об'єкті» простіші. ECS має вищу початкову вартість складності.