Склад важких OOP проти чистих компонентних систем? [зачинено]


13

Зізнаюся, я зробив гріх надмірним зловживанням і навіть зловживанням спадщиною. Перший (текстовий) ігровий проект, який я зробив, коли я проходив курс OOP, йшов аж до «Замкнутих дверей» та «Відчинених дверей» від «Двері» та «Кімнати з одними дверима», «Кімнати з двома дверима» та так з "Кімнати".

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

Я думав, що все ще роблю не так, і, переглянувши його в Інтернеті, я виявив, що я не єдиний із цією проблемою. Так я закінчив відкрити системи Entity після ретельного дослідження (читайте: googlefu)

Коли я почав читати це, я зміг зрозуміти, наскільки чітко він міг вирішити проблеми, що виникають у мене з традиційною ієрархією ООП з компонентами. Однак вони були в перших читаннях. Коли я натрапив на більш… “радикальні” підходи до ES, як, наприклад, на T-машині .

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

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

Наступний приклад показує, що я маю на увазі (це натхнене прикладом, який я знайшов в «Ігровій архітектурі», глава 14).

Я мав би маленьке дерево для транспортних засобів. Кореневий клас транспортного засобу мав би компонент відтворення, компонент зіткнення, компонент позиції тощо.

Тоді танк, підклас транспортного засобу успадкував би ці компоненти від нього і отримав би його власний "гарматний" компонент.

Те саме стосується персонажів. У персонажа є його власні компоненти, тоді Клас гравця успадкував би його та отримав контролер Input, а інші класи ворога успадкують від класу Character та отримають контролер AI.

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

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

Ваша думка?

PS: Я використовую Java, тому використання декількох успадкувань для реалізації цього замість звичайних компонентів неможливо.

PPS: Інтеркомпонентні комунікації здійснюватимуться шляхом з'єднання залежних компонентів один з одним. Це призведе до з’єднання, але я думаю, що це нормально.


2
Це все мені здається прекрасним. Чи є конкретний приклад у вашій ієрархії чи системі сутності, який "пахне" для вас? Зрештою, справа в тому, щоб зробити гру більше, ніж деяким почуттям чистоти.
drhayes

Ні, але, як я вже сказав, я майже не маю досвіду, і я далеко не найкращий суддя для цього.
Midori Ryuu

3
Я проголосував за закриття цього питання, оскільки воно суб'єктивне та відкрите для широкого обговорення. Я вдячний, що його просили з щирої позиції - але вибір того, як діяти тут, є суто питанням особистої думки. Єдиним реальним недоліком фіксації ваших компонентів у підкласі є те, що розташування компонентів не змінюється під час виконання - але це, безумовно, повинно бути очевидним для вас, і це вибір, який ви робите.
Килотан

4
Я також проголосував за закриття. Це добре і добре написане питання, але не підходить для GDSE. Ви можете спробувати відновити це на GameDev.net.
Шон Міддлічч

Як написано, запитання "Ваші думки?", Воно справді відкрите і невідчутне. Тим НЕ менше, це відповідає , якщо ви відповісти на нього , як якщо б питання було "Чи є якісь - зяючі пастки , які я міг би потрапити в не помітивши? (Принаймні, якщо ви думаєте, що насправді є якась емпірична різниця між різними архітектурами.)
Джон Калсбек,

Відповіді:


8

Розглянемо цей приклад:

Ти робиш РТС. Згідно з повною логікою, ви вирішили скласти базовий клас GameObject, а потім два підкласи Buildingта Unit. Звичайно, це працює чудово, і ви закінчите щось таке:

  • GameObject
    • ModelComponent
    • CollisionComponent
  • Building
    • ProductionComponent
  • Unit
    • AimingAIComponent
    • WeaponComponent
    • ParticleEmitterComponent
    • AnimationComponent

Тепер кожен підклас, який Unitви складаєте, вже має всі необхідні компоненти. І в якості бонусу, у вас є одне місце , щоб помістити все , що виснажливий код настройки , що newS вгору WeaponComponent, з'єднує його до AimingAIComponentі ParticleEmitterComponent-wonderful!

І звичайно, оскільки він все ще розбиває логіку на компоненти, коли ви врешті вирішите, що хочете додати Towerклас, який є будівлею, але має зброю, ви можете ним керувати. Ви можете додати а AimingAIComponent, а WeaponComponentта а ParticleEmitterComponentдо свого підкласу Building. Звичайно, тоді вам доведеться пройти і викопати код ініціалізації з Unitкласу і помістити його кудись ще, але це не велика втрата.

А тепер, залежно від того, скільки передбачуваності ви мали, ви можете, а може, і не закінчитись з тонкими помилками. Виявилося, що в грі може бути якийсь інший код, який виглядав приблизно так:

if (myGameObject instanceof Unit)
    doSomething(((Unit)myGameObject).getWeaponComponent());

Це мовчки не працює для вашої Towerбудівлі, навіть якщо ви дуже хотіли, щоб вона працювала на що-небудь зі зброєю. Тепер це має виглядати приблизно так:

WeaponComponent weapon = myGameObject.getComponent(WeaponComponent.class);
if (weapon)
    doSomething(weapon);

Значно краще! Після зміни цього вам трапляється, що будь-який із описаних вами методів доступу може опинитися в цій ситуації. Отже, найбезпечніше - це зняти їх і отримати доступ до кожного компонента за допомогою цього одного getComponentметоду, який може працювати з будь-яким підкласом. (Крім того, ви можете додати кожен тип get*Component()до надкласу GameObjectта замінити їх у підкласах, щоб повернути компонент. Хоча я припускаю перший).

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

Якщо ви дотримуєтесь цього до крайності, ви завжди закінчуєте робити підкласи повністю інертними пакетами компонентів, і тоді навіть підкласи не існують. Ви можете отримати майже всю користь від наявності ієрархії класів за допомогою ієрархії методів, яка створює належні компоненти. Замість того, щоб мати Unitклас, у вас є addUnitComponentsметод, який робить і, AnimationComponentа також дзвінки addWeaponComponents, який робить an AimingAIComponent, a WeaponComponentі a ParticleEmitterComponent. У результаті ви маєте всі переваги системи сутності, все повторне використання коду ієрархії, і жодна спокуса перевірити instanceofчи передати його типу вашого класу.


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

Нічого не читаючи твій пост, подарував мені кошмар! Звичайно, я маю на увазі, що по-доброму, бачачи, як ти відкрив мені очі, до якого кошмару я можу закінчитися! Мені б хотілося більше відповісти на таку детальну відповідь, але додати насправді нема чого!
Midori Ryuu

На більш простий спосіб. Ви закінчуєте використання спадщини як фабричного шаблону бідної людини.
Zardoz89

2

Композиція над успадкуванням - це термінологія OOP (принаймні так, як я її навчився / навчив). Для вибору між складом та спадщиною ви говорите вголос "Автомобіль - це тип колеса" (спадкування) та "Автомобіль має колеса" (композиція). Це повинно звучати правильніше, ніж інше (композиція в цьому випадку), і якщо ви не можете вирішити, за замовчуванням композиція, поки ви не вирішите інше.


1

Що стосується інтеграції систем на основі компонентів до існуючих ігор на основі ігрових об'єктів, є стаття про програму ковбоїв http://cowboyprogramming.com/2007/01/05/evolve-your-heirachy/, яка може дати вам деякі вказівки на спосіб переходу може бути здійснений.

Що стосується проблем, вашій моделі все одно доведеться поводитися з гравцем інакше, ніж у іншого ворога. Ви не зможете переключити статус (тобто гравець, який перебуває під контролем AI), коли гравець відключається або в особливих випадках, не обробляючи його інакше, ніж інші персонажі.

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

Змішання успадкування ігрових об'єктів з підходом на основі компонентів приносить вам деякі переваги компонентного підходу з недоліками обох підходів.

Це може працювати для вас. Але я б не змішував обидва поза перехідного періоду від однієї архітектури до іншої.

PS написано по телефону, тому мені важко підключитися до зовнішніх ресурсів.


Дякую за вашу відповідь, навіть якщо ви були по телефону! Я розумію, що ти маєш на увазі. Речі, що базуються на компонентах руху, тим більше гнучкості я повинен змінювати. Це причина, чому я збираюся йти вперед і спробувати композиційний підхід з мінімальним спадком. (Навіть більше того, що я запропонував.)
Мідорі Рюу

@MidoriRyuu уникає успадкування в об'єктах сутності. Вам пощастило використовувати мову з вбудованою рефлексією (і самоаналіз). Ви можете використовувати файли (txt або XML) для ініціалізації об'єктів з потрібними параметрами. Це не надто багато роботи, і це дозволить покращити точну настройку гри без перекомпіляції. Але зберігайте клас Entity, принаймні, для міжкомпонентної комунікації та інших корисних завдань. PS все ще по телефону :(
Койот
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.