Я говорю з невеликого досвіду, переходячи від жорсткої конструкції OO до дизайну Entity-Component-System (ECS).
В той час я був таким же, як ти , у мене була купа різних видів речей, які мали подібні властивості, і я створив різні предмети і намагався використати спадщину для її вирішення. Дуже розумна людина сказала мені, що цього не роблять, а замість цього використовуйте Entity-Component-System.
Зараз ECS - це велика концепція, і це важко отримати правильно. Там багато роботи вкладається, правильно будуючи сутності, компоненти та системи. Перш ніж ми зможемо це зробити, нам потрібно визначити умови.
- Сутність : це річ , гравець, тварина, NPC, що завгодно . Це річ, яка потребує приєднаних до неї компонентів.
- Компонент : це атрибут або властивість , наприклад, "Ім'я" або "Вік" або "Батьки" у вашому випадку.
- Система : це логіка компонента чи поведінки . Зазвичай ви будуєте одну систему на компонент, але це не завжди можливо. Крім того, інколи системи потребують впливу на інші системи.
Отже, ось де я б пішов з цим:
Перш за все, створіть ID
для своїх персонажів. An int
, Guid
що завгодно. Це "Сутність".
По-друге, почніть думати про різні способи поведінки, які у вас відбуваються. Такі речі, як "Сімейне дерево" - це поведінка. Замість того, щоб моделювати це як атрибути сутності, побудуйте систему, яка містить всю цю інформацію . Потім система може вирішити, що з цим робити.
Так само ми хочемо побудувати систему для "Чи персонаж живий чи мертвий?" Це одна з найважливіших систем у вашому дизайні, оскільки вона впливає на всі інші. Деякі системи можуть видалити "мертві" символи (наприклад, система "спрайт"), інші системи можуть внутрішньо впорядкувати речі, щоб краще підтримувати новий статус.
Ви створите, наприклад, систему "Спрайт" або "Малюнок" або "Візуалізація". Ця система несе відповідальність за визначення того, з яким спрайтом потрібно відображати символ, і як його відображати. Потім, коли персонаж помирає, видаліть їх.
Крім того, система «AI», яка може підказати персонажу, що робити, куди йти і т. Д. Це повинно взаємодіяти з багатьма іншими системами та приймати рішення на їх основі. Знову, мертві персонажі, ймовірно, можуть бути видалені з цієї системи, оскільки вони вже нічого не роблять.
Ваша система "Ім'я" та "Сімейне дерево", ймовірно, повинні зберігати характер (живий чи мертвий) у пам'яті. Ця система повинна нагадувати цю інформацію, незалежно від стану персонажа. (Джим все ще залишається Джимом, навіть після того, як ми його похоронимо.)
Це також дає перевагу зміни, коли система реагує ефективніше: у системи є власний таймер. Деякі системи потребують швидкого запуску, деякі - ні. Тут ми починаємо вникати в те, що робить гру ефективно ефективної. Нам не потрібно перерахувати погоду кожні мілісекунди, ми, мабуть, можемо робити це кожні 5 чи більше.
Це також дає вам більше творчих важелів: ви можете побудувати систему "Pathfinder", яка може обробляти обчислення шляху від A-B до, і може оновлювати за необхідності, дозволяючи системі Movement сказати "куди мені потрібно йти далі? " Зараз ми можемо повністю розділити ці проблеми і міркувати про них ефективніше. Руху не потрібно знаходити шлях, він просто повинен вас дістати.
Ви хочете викрити деякі частини системи зовні. У вашій Pathfinder
системі вам, мабуть, захочеться Vector2 NextPosition(int entity)
. Таким чином, ви можете зберігати ці елементи в жорстко контрольованих масивах або списках. Ви можете використовувати більш дрібні struct
типи, що допоможе вам зберегти компоненти в менших, суміжних блоках пам'яті, що може зробити оновлення системи набагато швидшим. (Особливо якщо зовнішні впливи на систему мінімальні, тепер їй потрібно дбати лише про її внутрішній стан, наприклад Name
.)
Але я не можу наголосити на цьому достатньо, тепер Entity
це просто просто ID
, включаючи плитки, об'єкти тощо. Якщо суб'єкт не належить до системи, система не відстежує його. Це означає, що ми можемо створювати наші «Дерево» об’єкти, зберігати їх у системах Sprite
і Movement
(дерева не рухатимуться, але вони мають компонент «Позиція») і не захищати їх від інших систем. Нам більше не потрібен спеціальний список для дерев, оскільки візуалізація дерева не відрізняється від символів, окрім паперових робіт. (Яка Sprite
система може керувати, або Paperdoll
система може керувати.) Тепер наша NextPosition
може бути трохи переписана:, Vector2? NextPosition(int entity)
і вона може повернути null
позицію для суб'єктів, які її не цікавлять. Ми також застосовуємо це до нашого NameSystem.GetName(int entity)
, воно повертається null
для дерев та скель.
Я закінчу це на завершення, але ідея тут полягає в тому, щоб дати вам деяку інформацію про ECS, і як ви дійсно можете використовувати це, щоб ви мали кращий дизайн вашої гри. Ви можете збільшити продуктивність, роз’єднати непоєднувані елементи та зберегти речі більш організовано. (Це також добре поєднується з функціональними мовами / налаштуваннями, такими як F # і LINQ. Я настійно рекомендую перевірити F #, якщо ви цього ще не зробили, він дуже добре поєднується з C #, коли ви використовуєте їх спільно.)