Я на правильному шляху з цією компонентною архітектурою?


9

Нещодавно я вирішив оновити свою ігрову архітектуру, щоб позбутися глибокої ієрархії класів і замінити їх на налаштовані компоненти. Перша ієрархія, яку я замінюю, - це ієрархія предметів, і я хотів би дізнатися поради, чи я на вірному шляху.

Раніше у мене була ієрархія, яка мала щось подібне:

Item -> Equipment -> Weapon
                  -> Armor
                  -> Accessory
     -> SyntehsisItem
     -> BattleUseItem -> HealingItem
                      -> ThrowingItem -> ThrowsAsAttackItem

Потрібно говорити, що він починав ставати безладним, і це було непросте рішення для предметів, які потребували декількох типів (тобто деяке обладнання використовується для синтезу виробів, деяке обладнання кидає і т.д.)

Потім я спробував перефактурувати та розмістити функціональність у базовий клас елементів. Але тоді я зазначив, що в Елементі є багато невикористаних / зайвих даних. Зараз я намагаюся зробити такий компонент, як архітектура, принаймні для моїх предметів, перш ніж намагатися це зробити для інших моїх ігрових класів.

Ось що я зараз думаю про налаштування компонента:

У мене є базовий клас елемента, який має слоти для різних компонентів (наприклад, слот для компонентів обладнання, слот для загоєння компонентів тощо), а також карту для довільних компонентів).

class Item
{
    //Basic item properties (name, ID, etc.) excluded
    EquipmentComponent* equipmentComponent;
    HealingComponent* healingComponent;
    SynthesisComponent* synthesisComponent;
    ThrowComponent* throwComponent;
    boost::unordered_map<std::string, std::pair<bool, ItemComponent*> > AdditionalComponents;
} 

Усі компоненти елемента успадковуватимуться від базового класу ItemComponent, і кожен тип компонентів відповідає за те, щоб повідомити двигуну, як реалізувати цю функціональність. тобто HealingComponent розповідає бойовій механіці, як споживати предмет як цілющий предмет, тоді як ThrowComponent розповідає бойовому двигуну, як ставитися до предмета як до предмета, що кидається.

Карта використовується для зберігання довільних компонентів, які не є основними елементами елемента. Я поєдную його з bool, щоб вказати, чи повинен контейнер елементів керувати ItemComponent або чи ним керує зовнішнє джерело.

Моя ідея тут полягала в тому, що я визначаю основні компоненти, які використовуються моїм ігровим двигуном на передній панелі, і мій заводський елемент присвоює компоненти, якими є предмет, інакше вони є недійсними. Карта міститиме довільні компоненти, які, як правило, додаються / споживаються сценаріями файлів.

Моє запитання, чи це хороший дизайн? Якщо ні, то як це можна покращити? Я розглядав можливість групування всіх компонентів на карті, але використання рядкової індексації видається непотрібним для основних елементів елемента

Відповіді:


8

Це здається дуже розумним першим кроком.

Ви вибираєте комбінацію загальності (карта "додаткових компонентів") та ефективності пошуку (жорстко закодовані члени), що може бути трохи попередньою оптимізацією - ваша думка щодо загальної неефективності на основі рядків пошук добре продуманий, але ви можете полегшити це, вибравши індексувати компоненти чимось швидшим хешуванням. Одним із підходів може бути надання кожному типу компонентів унікального ідентифікатора типу (по суті, ви реалізуєте легкий користувальницький RTTI ) та індексу на основі цього.

Незважаючи на це, я б застеріг вас відкрити відкритий API для об'єкта Item, який дозволяє вам запитувати будь-який компонент - твердо кодований та додатковий - рівномірно. Це полегшило б змінити базове представлення або баланс компонентів з твердим кодом / не жорсткого коду, не потребуючи перефактурування всіх клієнтів компонентів.

Ви також можете розглянути можливість надання "фіктивних" неоперативних версій кожного з жорстко кодованих компонентів та забезпечити їх призначення завжди - ви можете використовувати еталонні члени замість покажчиків, і вам ніколи не потрібно буде перевіряти наказник NULL перед тим, як взаємодіяти з одним із важко кодованих класів компонентів. Ви все одно будете нести витрати на динамічну розсилку на взаємодію з членами цього компонента, але це відбудеться навіть з членами вказівника. Це більше питання чистоти коду, оскільки вплив на продуктивність буде незначним, швидше за все.

Я не думаю, що це гарна ідея мати два різних види життєвого діапазону (іншими словами, я не думаю, що ти маєш додаткову карту компонентів). Це ускладнює систему і означає, що знищення та вивільнення ресурсів не будуть дуже детермінованими. API для ваших компонентів буде набагато зрозумілішим, якби ви вибрали одну стратегію управління життям або іншу - або суб'єкт господарювання керує терміном експлуатації компонента, або підсистема, яка реалізує компоненти, робить це (я вважаю за краще останній, тому що він краще поєднується з зовнішнім компонентом) підхід, про який я розповім далі).

Важливим недоліком, який я бачу у вашому підході, є те, що ви з’єднуєте всі компоненти разом в об'єкті "сутність", що насправді не завжди є найкращим дизайном. З моєї відповіді на інше запитання на основі компонентів:

Ваш підхід до використання великої карти компонентів та виклику update () в ігровому об’єкті є досить неоптимальним (і загальним підводним шаром для тих, хто перший будує подібні системи). Це забезпечує дуже погану когерентність кешу під час оновлення і не дозволяє скористатися паралельністю і тенденцією до процесу великих розмірів даних або поведінки в стилі SIMD. Часто краще використовувати дизайн, коли ігровий об’єкт не оновлює свої компоненти, а навпаки, підсистема, відповідальна за компонент, оновлює їх усі відразу.

По суті, ви використовуєте той же підхід, зберігаючи компоненти в об'єкті елемента (що, знову ж таки, цілком прийнятний перший крок). Ви можете виявити, що основна частина доступу до компонентів, про які ви стурбовані продуктивністю, полягає лише в тому, щоб оновити їх, і якщо ви вирішите скористатися більш підхідним підходом до організації компонентів, де компоненти зберігаються в кеш-когерентності Ефективна (для їхнього домену) структура даних підсистемою, яка найбільше розуміє їхні потреби, ви можете досягти набагато кращої, більш паралелізованої роботи оновлення.

Але я вказую на це лише як на щось, що слід розглядати як майбутній напрямок - ви, звичайно, не хочете перебирати це за надмірний розвиток; Ви можете зробити поступовий перехід шляхом постійного рефакторингу, або Ви можете виявити, що Ваша поточна реалізація ідеально відповідає Вашим потребам, і не потрібно її повторювати.


1
+1 за пропозицію позбутися предмета. Хоча це буде більше роботи вперед, це в кінцевому підсумку створить кращу систему компонентів.
Джеймс

У мене було ще кілька запитань, не впевнених, чи варто запускати новий topiuc, тому спершу спробую тут: Для мого класу елементів немає методів, які я буду викликати кадром evert (або навіть закрити). Щодо моїх графічних підсистем, я прийму ваші поради та триматиму всі оновлені об’єкти в системі. Інше питання, яке у мене було, як я обробляю перевірку компонентів? Як, наприклад, я хочу перевірити, чи можу я використовувати елемент як X, тому, природно, я би перевірив, чи є у елемента компонент, необхідний для виконання X. Це правильний спосіб зробити це? Ще раз дякую за відповідь
користувач127817
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.