Однак чи розумно також створювати додатки, використовуючи загальну для ігрових двигунів архітектуру Component-Entity-System?
Мені абсолютно. Я працюю у візуальному FX та вивчав широке розмаїття систем у цій галузі, їх архітектури (включаючи CAD / CAM), голодних SDK та будь-яких паперів, які б давали мені відчуття плюсів і мінусів, здавалося б, нескінченних архітектурних рішень, які можна зробити, навіть навіть найтонші не завжди роблять тонкий вплив.
VFX досить схожий на ігри тим, що існує одна центральна концепція "сцени", із вікнами перегляду, які відображають відображені результати. Існує тенденція до того, що в анімаційних контекстах постійно обертається навколо цієї сцени, що постійно обертається навколо цієї сцени, де може статися фізика, нерестові випромінювачі частинок, анімовані та надані сітки, анімація руху тощо, і в кінцевому підсумку їх рендерувати все для користувача в кінці.
Ще одна подібна концепція принаймні дуже складних ігрових двигунів полягала в необхідності "дизайнерського" аспекту, де дизайнери могли гнучко розробляти сцени, включаючи можливість робити легке програмування самостійно (сценарії та вузли).
З роками я виявив, що ECS найкраще підходить. Звичайно, це ніколи повністю не відривається від суб'єктивності, але я б сказав, що це, напевно, мало найменші проблеми. Це вирішило набагато більші проблеми, з якими ми завжди стикалися, а лише повернув нам кілька нових другорядних.
Традиційний ООП
Більш традиційні підходи до ООП можуть бути дуже сильними, якщо ви чітко розумієте вимоги до дизайну наперед, але не вимоги щодо імплементації. Будь то через більш плоский підхід до інтерфейсу чи більш вкладений ієрархічний підхід ABC, він, як правило, цементує дизайн і ускладнює його зміни, роблячи впровадження легшим та безпечнішим. Завжди є потреба в нестабільності в будь-якому продукті, що минає одну версію, тому підходи OOP мають тенденцію до перекосу стабільності (труднощі змін та відсутність причин для зміни) до рівня дизайну та нестабільності (простота змін та причини змін) до рівня реалізації.
Однак, незважаючи на вимоги, що розвиваються для користувачів, як дизайн, так і реалізація, можливо, потрібно буде часто змінювати. Ви можете знайти щось дивне, як сильна потреба в користувальницькій аналогічній істоті, яка повинна бути одночасно і рослиною, і твариною, що повністю приводить до недійсності всієї побудованої вами концептуальної моделі. Звичайні об'єктно-орієнтовані підходи не захищають вас тут, а іноді можуть зробити такі непередбачувані зміни, що порушують концепцію, ще важче. Якщо задіяні дуже критичні для продуктивності сфери, причини дизайну змінюються ще більше.
Поєднання декількох зернистих інтерфейсів для формування відповідного інтерфейсу об'єкта може багато допомогти в стабілізації клієнтського коду, але це не допомагає стабілізувати підтипи, які іноді можуть зменшити кількість клієнтських залежностей. Ви можете мати один інтерфейс, який використовується лише частиною вашої системи, наприклад, але з тисячею різних підтипів, що реалізують цей інтерфейс. У такому випадку підтримання складних підтипів (складних, оскільки у них так багато розрізнених обов'язків щодо інтерфейсу) може стати кошмаром, а не кодом, що використовує їх через інтерфейс. OOP прагне передати складність на об'єктний рівень, тоді як ECS передає його на рівень клієнта («системи»), і це може бути ідеально, коли систем дуже мало, але ціла купа відповідних «об’єктів» («сутностей»).
Клас також володіє своїми даними приватно, і таким чином може підтримувати інваріанти самостійно. Тим не менш, існують "грубі" інваріанти, яких насправді все ще важко підтримувати, коли об'єкти взаємодіють один з одним. Щоб складна система в цілому перебувала у дійсному стані, часто потрібно враховувати складний графік об'єктів, навіть якщо їх окремі інваріанти належним чином підтримуються. Традиційні підходи в стилі ООП можуть допомогти у підтримці зернистих інваріантів, але насправді може ускладнити підтримку широких, грубих інваріантів, якщо об’єкти зосереджуються на маленьких гранях системи.
Ось де подібні підходи або варіанти створення лего-блокових ECS можуть бути настільки корисними. Крім того, що системи мають більш грубу конструкцію, ніж звичайний об'єкт, стає легше підтримувати такі види грубих інваріантів під виглядом пташиного польоту. Багато взаємодій підліткових об'єктів перетворюються на одну велику систему, що фокусується на одній широкій задачі, а не маленькі об'єкти, що зосереджуються на маленьких завданнях з графіком залежності, який охоплює кілометр паперу.
І все-таки мені довелося шукати поза своїм полем, в ігровій індустрії, щоб дізнатися про ECS, хоча я завжди був одним із орієнтованих на дані мислення. Крім того, як не дивно, я майже самостійно пробрався до ECS, просто переглянувши і намагаючись придумати кращі проекти. Я не встиг продемонструвати все це і пропустив дуже важливу деталь, а саме формалізацію частини "системи" та розбиття компонентів аж до необроблених даних.
Я спробую проаналізувати, як я закінчився влаштуванням на ECS, і як це врешті-решт вирішило всі проблеми з попередніми ітераціями дизайну. Я думаю, що це допоможе точно виділити, чому відповідь тут може бути дуже сильним «так», що ECS потенційно застосовується далеко за межами ігрової індустрії.
1980-х рр. Архітектура грубої сили
Перша архітектура, над якою я працював у галузі VFX, мала давню спадщину, яка вже минула десятиліття з моменту приєднання до компанії. Це було грубою силою C, що кодує всю дорогу (не нахил на C, як я люблю C, але спосіб, яким він тут користувався, був справді сирим). Мініатюрний і надпростий фрагмент нагадував такі залежності:
І це надзвичайно спрощена схема однієї крихітної частини системи. Кожен із цих клієнтів на діаграмі ("Візуалізація", "Фізика", "Рух") отримає якийсь "загальний" об'єкт, за допомогою якого вони перевірять поле типу, наприклад:
void transform(struct Object* obj, const float mat[16])
{
switch (obj->type)
{
case camera:
// cast to camera and do something with camera fields
break;
case light:
// cast to light and do something with light fields
break;
...
}
}
Звичайно зі значно меншим і складнішим кодом, ніж цей. Часто з цих випадків комутатора викликаються додаткові функції, які б рекурсивно здійснювали перемикання знову і знову і знову. Ця схема і код може майже виглядати ECS-лайт, але не було ніякого сильного сутність-компонент відмінності ( « є цей об'єкт камера?», А не «не ця об'єкт забезпечить рух?"), І ніякої формалізації "системи" ( просто купа вкладених функцій, які збираються всюди і змішують обов'язки). У цьому випадку майже все було складним, будь-яка функція була потенційною для катастрофи, яка чекає відбутися.
У нашій процедурі тестування тут часто доводилося перевіряти речі, такі як сітки, окремо від інших типів елементів, навіть якщо однакова річ відбувається з обома, оскільки характер кодування тут (часто супроводжується великою кількістю копій та вставлення) часто робиться дуже ймовірно, що в іншому випадку точно така ж логіка може перейти від одного типу елемента до іншого. Спроба розширити систему для обробки нових типів елементів була досить безнадійною, навіть якщо була сильно виражена потреба в кінцевому користуванні, оскільки це було надто важко, коли ми так сильно намагалися просто обробити існуючі типи елементів.
Деякі плюси:
- Ага ... я не вимагає жодного інженерного досвіду, гадаю? Ця система не вимагає ніяких знань навіть про такі основні поняття, як поліморфізм, це абсолютно груба сила, тому я думаю, що навіть початківець може зрозуміти частину коду, навіть якщо професіонал при налагодженні ледве підтримує його.
Деякі мінуси:
- Кошмар технічного обслуговування. Наша маркетингова команда насправді відчула необхідність похвалитися тим, що ми виправили понад 2000 унікальних помилок за один 3-річний цикл. Мені щось бентежить, що ми мали в першу чергу стільки помилок, і цей процес, ймовірно, все ще фіксував близько 10% від загальної кількості помилок, які постійно зростали.
- Про найбільш негнучкі рішення.
1990-ті роки COM Архітектура
Більшість галузей VFX використовує цей стиль архітектури з того, що я зібрав, читаючи документи про свої дизайнерські рішення та дивлячись на набори для розробки програмного забезпечення.
Це може бути не COM на рівні ABI (деякі з цих архітектур могли мати лише плагіни, написані за допомогою одного і того ж компілятора), але поділяє багато подібних характеристик із запитами інтерфейсу, зробленими на об'єктах, щоб побачити, які інтерфейси підтримують їх компоненти.
При такому підході transform
вищевказана аналогічна функція нагадувала цю форму:
void transform(Object obj, const Matrix& mat)
{
// Wrapper that performs an interface query to see if the
// object implements the IMotion interface.
MotionRef motion(obj);
// If the object supported the IMotion interface:
if (motion.valid())
{
// Transform the item through the IMotion interface.
motion->transform(mat);
...
}
}
Це такий підхід, на якому погодилася нова команда цієї старої бази коду, щоб врешті рефакторировать до. І це було кардинальним покращенням порівняно з оригіналом з точки зору гнучкості та ремонтопридатності, але все ж були проблеми, які я висвітлюю в наступному розділі.
Деякі плюси:
- Набагато більш гнучким / розтяжним / ремонтопридатним, ніж попереднє жорстоке рішення.
- Сприяє сильній відповідності багатьом принципам SOLID, роблячи кожен інтерфейс абсолютно абстрактним (без стану, без реалізації, лише чисті інтерфейси).
Деякі мінуси:
- Багато котлована. Наші компоненти повинні були бути опубліковані через реєстр, щоб створювати об'єкти, інтерфейси, які вони підтримували, вимагали як успадковувати ("реалізовувати" в Java) інтерфейс, так і надавати якийсь код, щоб вказати, які інтерфейси були доступні в запиті.
- Популяризована дубльована логіка всюди в результаті чистого інтерфейсу. Наприклад, всі компоненти, що реалізуються
IMotion
, завжди мали б такий самий стан і точну реалізацію для всіх функцій. Щоб пом'якшити це, ми почали б централізувати базові класи та допоміжні функції по всій системі для речей, які, як правило, будуть реалізовані однаково для того ж інтерфейсу, і, можливо, з багатократним успадкуванням відбувається за капотом, але це було досить безладно під капотом, навіть незважаючи на те, що клієнтський код мав це легко.
- Неефективність: сеанси vtune часто демонстрували основну
QueryInterface
функцію, майже завжди показуючи середню та верхню точку доступу, а іноді навіть точку №1. Щоб пом'якшити це, ми зробимо такі речі, як рендеринг частин кешу бази даних списку об'єктів, які, як відомо, підтримуютьсяIRenderable
, але це значно збільшило складність та витрати на обслуговування. Крім того, це було важче виміряти, але ми помітили певні уповільнення порівняно з кодуванням у стилі С, яке ми робили раніше, коли кожен інтерфейс вимагав динамічної відправки. Такі речі, як неправильні прогнози та бар'єри для оптимізації, важко виміряти за межами невеликого аспекту коду, але користувачі, як правило, помічають чуйність інтерфейсу користувача та подібні речі, що погіршуються, порівнюючи попередні та новіші версії програмного забезпечення " Сторона для областей, де алгоритмічна складність не змінилася, лише константи.
- Було ще важко міркувати про правильність на більш широкому системному рівні. Незважаючи на те, що це було значно простіше, ніж попередній підхід, все-таки важко було зрозуміти складну взаємодію між об'єктами в цій системі, особливо з деякими оптимізаціями, які стали для цього необхідними.
- У нас виникли проблеми з правильним інтерфейсом. Навіть незважаючи на те, що в системі, яка використовує інтерфейс, може бути лише одне широке місце, вимоги до кінця користувача змінюватимуться у порівнянні з версіями, і ми в кінцевому підсумку повинні робити каскадні зміни для всіх класів, які реалізують інтерфейс для розміщення нової функції, доданої до інтерфейс, наприклад, якщо б не був якийсь абстрактний базовий клас, який уже централізував логіку під кришкою (деякі з них проявлятимуться посеред цих каскадних змін у надії не повторювати це знову і знову).
Прагматична відповідь: Склад
Одне з речей, які ми раніше помічали (або принаймні я був), що викликало проблеми, - це те, що він IMotion
може бути реалізований 100 різними класами, але з точно такою ж реалізацією та станом. Крім того, його використовували б лише декілька систем, таких як візуалізація, рух із ключовими кадрами та фізика.
Таким чином, у такому випадку ми можемо мати відношення 3 до 1 між системами, що використовують інтерфейс до інтерфейсу, і відношення 100 до 1 між підтипами, що реалізують інтерфейс до інтерфейсу.
Складність та технічне обслуговування тоді були б різко перетворені на реалізацію та обслуговування 100 підтипів, а не 3 клієнтських систем, від яких залежить IMotion
. Це перенесло всі наші труднощі з технічного обслуговування на обслуговування цих 100 підтипів, а не на 3 місця за допомогою інтерфейсу. Оновлення 3-х місць у коді з кількома або відсутніми "непрямими еферентними муфтами" (як у залежностях до нього, але опосередковано через інтерфейс, а не пряма залежність), нічого великого: оновлення 100 підтипових місць із завантаженням "непрямих еферентних з'єднань" , досить велика справа *.
* Я усвідомлюю, що в цьому сенсі з точки зору впровадження поняття "еферентні муфти" дивно і неправильно, я просто не знайшов кращого способу описати складність технічного обслуговування, пов'язану з інтерфейсом та відповідними реалізаціями ста підтипів. повинні змінитися.
Тому мені довелося наполягати, але я запропонував спробувати стати трохи більш прагматичним і розслабити всю ідею "чистого інтерфейсу". Мені не було сенсу робити щось на кшталт IMotion
абсолютно абстрактного і безгромадянського, якщо ми не побачили користь для цього, маючи велику різноманітність реалізацій. У нашому випадку, IMotion
щоб мати багату різноманітність реалізацій, насправді перетворився б на цілий кошмар технічного обслуговування, оскільки ми не хотіли різноманітності. Натомість ми ітератували до спроби зробити єдину реалізацію руху, що дійсно добре проти змін клієнтських вимог, і часто працювали навколо чистої ідеї інтерфейсу, багато намагаючись змусити кожного виконавця IMotion
використовувати ту саму реалізацію та стан, пов’язаний, щоб ми не робили ' t дублювати цілі.
Таким чином, інтерфейси стали більше схожими на широкі, Behaviors
пов'язані з сутністю. IMotion
просто перетворився б на Motion
"компонент" (я змінив спосіб, який ми визначили "компонент" від COM, на той, де ближче до звичайного визначення, фрагмента, що становить "повну" сутність).
Замість цього:
class IMotion
{
public:
virtual ~IMotion() {}
virtual void transform(const Matrix& mat) = 0;
...
};
Ми перетворили це на щось подібне:
class Motion
{
public:
void transform(const Matrix& mat)
{
...
}
...
private:
Matrix transformation;
...
};
Це кричуще порушення принципу інверсії залежності для того, щоб почати відходити від абстрактного назад до конкретного, але для мене такий рівень абстракції корисний лише тоді, коли ми можемо передбачити справжню потребу в якомусь майбутньому, поза розумним сумнівом і не здійснення смішних сценаріїв "що робити", повністю відірваних від досвіду користувача (що, мабуть, вимагатиме зміни дизайну), для такої гнучкості.
Тому ми почали розвиватися до цього дизайну. QueryInterface
став більше схожий QueryBehavior
. Крім того, тут стало здаватися безглуздим використання спадщини. Ми замість цього використовували композицію. Об'єкти перетворилися на сукупність компонентів, доступність яких можна було запитати та вводити під час виконання.
Деякі плюси:
- Було набагато простіше підтримувати в нашому випадку, ніж попередня система із стильним COM-стилем. Непередбачені сюрпризи, такі як зміна вимог або скарги на робочий процес, можуть бути легше прийняті за допомогою однієї дуже центральної та очевидної
Motion
реалізації, наприклад, і не розповсюджуються по ста підтипів.
- Дав абсолютно новий рівень гнучкості такого типу, який ми насправді потребували. У нашій попередній системі, оскільки моделі успадкування статичні відносини, ми могли лише ефективно визначати нові сутності під час компіляції в C ++. Ми не могли зробити це з мови сценаріїв, наприклад, із підходом до композиції ми могли об'єднати нові об'єкти під час виконання, просто приєднавши до них компоненти та додавши їх до списку. "Сутність" перетворилася на порожнє полотно, на якому ми могли просто зібрати колаж всього, що нам було потрібно на льоту, при цьому відповідні системи автоматично розпізнавали та обробляли ці сутності.
Деякі мінуси:
- У нас все ще було важко перебувати у відділі ефективності та ремонту в критичних для продуктивності сферах. Кожна система все-таки бажала б кешувати компоненти сутностей, які забезпечували цю поведінку, щоб не повторювати їх повторно та перевіряти, що є в наявності. Кожна система, що вимагає продуктивності, зробила б це дещо так по-різному, і схильна до іншого набору помилок, коли не вдалося оновити цей кешований список і, можливо, структуру даних (якщо якась форма пошуку була задіяна, наприклад, збиття фруктів або ретрансляція) на деяких неясна подія зміни сцени, наприклад
- Ще було щось незграбне і складне, на що я не міг покласти пальця, пов'язаний з усіма цими дрібнистими маленькими поведінковими, простими предметами. Ми все ще породжували багато подій, щоб розібратися з взаємодіями між цими "поведінковими" об'єктами, які іноді були необхідними, і в результаті вийшов дуже децентралізований код. Кожен маленький предмет легко перевірити на правильність і, взятий окремо, часто був абсолютно правильним. І все-таки відчувалося, що ми намагаємося підтримувати масивну екосистему, що складається з маленьких сіл, і намагаємось розмірковувати про те, що всі вони роблять окремо і складають, щоб скласти загалом. Кодова база стилю 80-х років відчувала себе одним епічним, перенаселеним мегаполісом, який, безумовно, був кошмаром технічного обслуговування,
- Втрата гнучкості через відсутність абстракції, але в тій області, де ми ніколи насправді не стикалися з справжньою потребою в цьому, тому навряд чи практичний підхід (хоча, безумовно, принаймні теоретичний).
- Збереження сумісності з ABI завжди було важким, і це ускладнювало необхідність стабільних даних, а не просто стабільного інтерфейсу, пов'язаного з "поведінкою". Однак ми можемо легко додати нові форми поведінки та просто знехтувати наявні, якщо потрібна зміна стану, і це, мабуть, простіше, ніж робити зворотні підказки під інтерфейсами на рівні підтипу, щоб вирішити проблеми з версією.
Одне з явищ, що відбулося, було те, що, оскільки ми втратили абстракцію на ці поведінкові компоненти, у нас їх було більше. Наприклад, замість абстрактного IRenderable
компонента ми би прикріпили об'єкт з бетоном Mesh
або PointSprites
складовою. Система візуалізації знала б, як рендерінг Mesh
та PointSprites
компоненти, і знайшла б об'єкти, які надають такі компоненти, і намалювати їх. В інший час у нас були різні перекази, подібні до того, SceneLabel
що ми виявили, що нам потрібно заднім числом, і тому ми прив’язуємо їх SceneLabel
у цих випадках до відповідних організацій (можливо, крім а Mesh
). Потім реалізація системи візуалізації буде оновлена, щоб знати, як рендерувати об'єкти, які їх надали, і це було досить легко змінити.
У цьому випадку сутність, що складається з компонентів, також може бути використана як компонент для іншого об'єкта. Ми б створили речі таким чином, підключивши блоки лего.
ECS: Системи та компоненти сировини
Ця остання система була настільки, наскільки я її створив самостійно, і ми все ще бастаризували її з COM. Таке відчуття, ніби воно хоче стати системою компонентів сутності, але я на той час не був знайомий. Я оглядав приклади в стилі COM, які наситили моє поле, коли я мав би дивитися на ігрові двигуни AAA для архітектурного натхнення. Я нарешті почав це робити.
Що мені бракувало, було кілька ключових ідей:
- Формалізація "систем" для обробки "компонентів".
- "Компоненти" - це вихідні дані, а не поведінкові об'єкти, складені разом у більший об'єкт.
- Суб'єкти як не що інше, як суворий ідентифікатор, пов'язаний із колекцією компонентів.
Нарешті я покинув цю компанію і почав працювати над системою ECS (як і раніше працюю над нею, поки витрачаю мої заощадження), і на сьогоднішній день це була найпростіша система управління.
Що я помітив при підході до ECS, це те, що він вирішив проблеми, з якими я все ще боровся. Найголовніше для мене, здавалося, що ми керуємо "містами" здорового розміру замість маленьких селищ зі складними взаємодіями. Це було не так складно, як монолітний "мегалополіс", занадто великий у своєму населенні, щоб ефективно керувати, але не був таким хаотичним, як світ, наповнений крихітними маленькими селами, взаємодіючи між собою, де просто думали про торгові шляхи в між ними утворився кошмарний графік. ECS перегнала всю складність на об'ємні "системи", як система рендеринга, "місто" здорового розміру, але не "перенаселений мегаполіс".
Компоненти, що стають необробленими даними, спочатку відчували мене справді дивними , оскільки це порушує навіть основний принцип приховування інформації OOP. Це було свого роду викликом однієї з найбільших цінностей, яку я шанував щодо OOP, - її здатністю підтримувати інваріанти, що вимагало інкапсуляції та приховування інформації. Але це стало непокоїти, оскільки швидко стало очевидним, що відбувається лише з десятком або настільки широкими системами, що трансформують ці дані замість такої логіки, що розповсюджується на сотні до тисяч підтипів, реалізуючи комбінацію інтерфейсів. Я схильний вважати це як і раніше в стилі OOP, за винятком того, що системи забезпечують функціональність та реалізацію доступу до даних, компоненти надають дані, а суб'єкти надають компоненти.
Стало ще простіше , протиінтуїтивно зрозуміти, про побічні ефекти, викликані системою, коли було лише кілька громіздких систем, що перетворювали дані в широкі пропуски. Система стала набагато "більш плоскою", стеки моїх викликів стали дрібнішими, ніж будь-коли раніше, для кожного потоку. Я міг би подумати про систему на тому наглядовому рівні і не стикатися з дивними сюрпризами.
Так само це спростило навіть критичні показники щодо ефективності щодо усунення цих запитів. Оскільки ідея "Система" стала дуже формалізованою, система могла підписатись на компоненти, які її цікавили, і просто надіслати кешований список сутностей, які відповідають цим критеріям. Кожен з них не повинен був керувати цією оптимізацією кешування, він став централізованим на одне місце.
Деякі плюси:
- Здається, якраз вирішує майже кожну велику архітектурну проблему, з якою я стикався у своїй кар’єрі, не відчуваючи, що потрапив у куточок дизайну, коли не стикався з непередбаченими потребами.
Деякі мінуси:
- Мені все ще важко іноді обернути голову навколо цього, і це не найзріліша чи добре усталена парадигма навіть в ігровій індустрії, де люди сперечаються, що саме це означає і як робити справи. Це точно не те, що я міг би зробити з колишньою командою, з якою я працював, яка складалася з членів, глибоко підключених до стилю COM-стилю або мислення на стилі 1980-х років оригінальної кодової бази. Де я плутаюсь іноді - це як моделювати зв'язки в стилі графіки між компонентами, але я завжди знаходив рішення, яке не виявилося жахливим пізніше, де я можу просто зробити компонент залежним від іншого ("цей рух компонент залежить від цього іншого як батьківський, і система буде використовувати мемоалізацію, щоб уникнути повторного виконання тих же рекурсивних розрахунків руху ", наприклад)
- ABI все ще важкий, але поки що я б навіть ризикнув сказати, що це простіше, ніж чистий інтерфейсний підхід. Це зміна мислення: стабільність даних стає єдиним фокусом для ABI, а не стабільністю інтерфейсу, і дещо легше досягти стабільності даних, ніж стабільності інтерфейсу (наприклад, немає спокус змінити функцію лише тому, що їй потрібен новий параметр. Такі речі трапляються всередині грубої реалізації системи, яка не порушує ABI).
Однак чи розумно також створювати додатки, використовуючи загальну для ігрових двигунів архітектуру Component-Entity-System?
Отже, я б сказав абсолютно "так", оскільки мій особистий приклад VFX є сильним кандидатом. Але це все ще досить схоже на потреби в іграх.
Я не ставлю це на практиці у більш віддалених районах, повністю відірваних від проблем ігрових двигунів (VFX досить схожий), але мені здається, що набагато більше областей є хорошими кандидатами на підхід до ECS. Можливо, навіть система GUI була б придатною для однієї, але я все ще використовую там більш підхід OOP (але без глибокого успадкування на відміну від Qt, наприклад).
Це широкодосліджена територія, але мені здається підходящою, коли ваші сутності можуть складатися з багатої комбінації "рис" (і саме того, яке поєднання рис вони надають, коли вони можуть бути змінені), і де у вас є кілька узагальнених системи, що обробляють сутності, які мають необхідні риси.
Це стає дуже практичною альтернативою в тих випадках, коли будь-який сценарій, коли ви можете спокусити використовувати щось на зразок багаторазового успадкування або емуляції концепції (міксин, наприклад), лише для створення сотень і більше комбо в глибокій ієрархії спадкування або сотні комбо класів у плоскій ієрархії, що реалізує певну комбінацію інтерфейсів, але там, де ваші системи малочисельні (десятки, наприклад).
У цих випадках складність бази коду починає відчувати себе більш пропорційною кількості систем замість кількості комбінацій типів, оскільки кожен тип тепер є лише об'єктом, що складається з компонентів, які є не що інше, як необроблені дані. Системи GUI, природно, підходять до таких специфікацій, де вони можуть мати сотні можливих типів віджетів, поєднаних з іншими базовими типами або інтерфейсами, але лише декілька систем для їх обробки (система компонування, система візуалізації тощо). Якщо система GUI використовувала ECS, можливо, було б набагато простіше міркувати про правильність системи, коли вся функціональність забезпечується декількома цими системами замість сотень різних типів об'єктів із успадкованими інтерфейсами чи базовими класами. Якщо система GUI використовувала ECS, віджети не мали б функціоналу, а лише дані. Лише жменька систем, які обробляють об'єкти віджетів, мали б функціонал. Як обробляються важкі події для віджета, - це не за межами мене, але, виходячи з мого обмеженого досвіду поки що, я не знайшов випадок, коли такий тип логіки не міг би бути переданий централізовано до даної системи таким чином, щоб задній погляд, дав набагато більш елегантне рішення, якого я коли-небудь очікував.
Мені б хотілося, щоб він працював у багатьох сферах, оскільки це був рятівник у моєму. Звичайно, це неправильно, якщо ваш дизайн не розбивається таким чином, від сукупностей, що агрегують компоненти, до грубої системи, що обробляє ці компоненти, але якщо вони, природно, відповідають такій моделі, це найдивовижніша річ, з якою я стикався ще .