"Ігровий об'єкт" - і на основі компонентів дизайн


25

Я працював над деякими хобі-проектами останні 3-4 роки. Просто прості 2d та 3d ігри. Але останнім часом я розпочав більший проект. Так, останні кілька місяців я намагався створити клас ігрових об'єктів, який може бути базою всіх моїх ігрових об'єктів. Тож після довгого тестування і тестування я звернувся до Google, який швидко вказав мені на деякі файли GDC та PowerPoints. А зараз я намагаюся зрозуміти ігрові об’єкти на основі компонентів.

Я розумію, що двигун створює ігровий об’єкт, а потім приєднує різні компоненти, які обробляють такі речі, як здоров'я, фізика, мережа та все, що ви з цим робите. Але я не розумію, як компонент X знає, чи Y змінив стан об'єкта. Як, наприклад, як PhysicsComponent знає, чи гравець живий, адже здоров'я контролює HealthComponent ..? І як HealthComponent відтворює "анімацію-помер гравця"?

У мене було враження, що це було щось подібне (In HealthComponent):

if(Health < 0) {
   AnimationComponent.PlayAnimation("played-died-animation")
}

Але знову ж таки, як HealthComponent знає, що до ігрового об’єкта, до якого він приєднаний, додається AnimationComponent? Єдине рішення, яке я бачу тут, - це

  1. Проведіть перевірку, чи додано AnimationComponent чи ні (або всередині коду компонента, або з боку двигуна)

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

  3. Напишіть, як, HealthWithAnimationComponent, HealthNoAnimationComponent та soo on, що, схоже, знову відповідає всій ідеї дизайну компонентів.


1
Любіть питання. Я повинен був запитати те саме місяці тому, але ніколи не обійшов його. Додатковою проблемою, з якою я стикався, є те, коли в ігровому об’єкті є кілька екземплярів одного компонента (наприклад, кілька анімацій). Було б чудово, якби відповіді могли торкнутися цього. Я в кінцевому підсумку використовував повідомлення для сповіщень із змінними, що ділилися між усіма компонентами ігрового об’єкта (тому їм не потрібно надсилати повідомлення, щоб отримати значення для змінної).
ADB

1
Залежно від типу гри, ви, мабуть, не матимете ігрових об’єктів, які мають оздоровчий компонент і не містять анімаційних компонентів. І всі ці ігрооб'єкти, мабуть, представляють щось на зразок Unit. Таким чином, ви можете викинути компонент охорони здоров'я та створити UnitComponent, який би мав стан здоров'я, і ​​знав про всі компоненти, якими повинен бути пристрій. Ця деталізація компонентів насправді нічого не допомагає - більш реально мати один компонент у домені (візуалізація, аудіо, фізика, гра-логіка).
Кікаймару

Відповіді:


11

У всіх ваших прикладах є жахлива проблема. Компонент охорони здоров'я повинен знати про кожен тип компонента, який може потребувати реакції на суб'єкта, який помирає. Тому жоден із ваших сценаріїв не підходить. У вашої організації є складова здоров'я. Він має анімаційний компонент. Ні про інше, ні про інше. Вони спілкуються через систему обміну повідомленнями.

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

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


Гаразд, це має сенс. Але хто оголошує "держави" на кшталт "мертвих", або "портал - зламаний" і т. Д. Компонент або двигун? Тому що додавання стану "мертвого" до речі, до якої ніколи не буде прикріплений медичний компонент, здається, мені щось марно. Я думаю, що я просто занурюсь і почну перевіряти якийсь код і побачити, що працює.
хайєр

Майкл та Патрік Хьюз мають правильну відповідь вище. Компоненти - це просто дані; тож насправді компонент здоров'я не виявляє, коли суб'єкт помер і надсилає повідомлення, це певна логіка, що відповідає певній грі. Як абстрактно залежати від вас. Фактичний стан загибелі ніколи не потрібно зберігати ніде. Об'єкт мертвий, якщо його здоров'я <0, і компонент здоров’я може інкапсулювати цей біт логіки перевірки даних, не порушуючи "немає поведінки!" обмеження, якщо ви розглядаєте лише речі, які змінюють стан компонента як поведінку.
Блекі

Просто цікаво, як би ви попрацювали з MovementComponent? Коли він виявляє вхід, йому потрібно збільшити швидкість в компоненті PositionComponent. Як виглядатиме повідомлення?
Поради48

8

Таким чином , що Артеміс вирішує цю проблему, щоб не робити обробки в компонентах. Компоненти містять лише необхідні їм дані. Системи читають кілька типів компонентів і виконують будь-яку необхідну обробку.

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


Це в кінцевому підсумку є хорошим способом вирішення проблеми: Компоненти представляють властивості, в той час як Системи поєднують різні властивості та використовують їх для роботи. Це величезний відхід від традиційного мислення про ООП і завдає шкоди головам деяких людей =)
Патрік Хьюз,

Гаразд, зараз я справді загублений .. "На противагу цьому, в ES, якщо у вас на полі бою 100 одиниць, кожне представлене об'єктом, то у вас є нульові копії кожного методу, які можна викликати в одиниці - тому що Суб'єкти не містять методів, а також Компоненти не містять методів. Натомість у вас є зовнішня система для кожного аспекту, і ця зовнішня система містить усі методи, на які можна звертатися в будь-якому об'єкті, що має Компонент, який позначає його як сумісний з цим система ". Ну де зберігаються дані в GunComponent? Як раунди тощо. Якщо всі об'єкти поділяють один і той же компонент.
hayer

1
Наскільки я розумію, всі об'єкти не мають однакового компонента, до кожної сутності можуть бути приєднані N екземплярів компонентів. Потім система запитує гру для списку всіх сутностей, які мають інстанції компонентів, про які вони дбають про приєднані до них, а потім виконує будь-яку обробку на них
Джейк Вудс

Це просто переміщує проблему. Як система знає, які компоненти використовувати? Системі також можуть знадобитися інші системи (наприклад, система StateMachine може зажадати анімації). Однак це все-таки вирішує проблему, яким володіє ВООЗ. Насправді, більш простою реалізацією було б мати словник в ігровому об’єкті, і кожна система створює там свої змінні.
ADB

Це дійсно пересуває проблему, але не до місця, яке є більш вигідним. Системи мають відповідні компоненти з жорстким провідником. Системи можуть спілкуватися один з одним за допомогою компонентів (StateMachine може встановити значення компонента, яке читає Анімація, щоб знати, що робити (або це може запустити подію). Підхід до словника звучить як шаблон властивостей, який також може працювати). Компоненти полягають у тому, що пов’язані властивості згруповані разом, і їх можна статично перевірити. Немає химерних помилок, оскільки ви додали "Dammage" в одному місці, але намагалися відновити його, використовуючи "Damage" в іншому.
Майкл

6

У вашому коді ви можете знайти способи (я використовував їх, можливо, існують інші способи) дізнатися, чи змінився об'єкт стан:

  1. Відправити повідомлення.
  2. Читати безпосередньо дані з компонента.

1) Проведіть перевірку, чи додано AnimationComponent чи ні (або всередині коду компонента, або з боку двигуна)

Для цього я використав: 1. HasComponent функцію GameObject, або 2. коли ви приєднуєте компонент, ви можете перевірити залежності в якійсь конструюючої функції, або 3. Якщо я точно знаю, що в цьому об'єкті є цей компонент, я просто його використовую.

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

У деяких статтях я читав, що в системі Ideal компоненти не залежать один від одного, але в реальному житті це не так.

3) Напишіть, як, HealthWithAnimationComponent, HealthNoAnimationComponent, і так далі, що, схоже, знову відповідає всій ідеї дизайну компонентів.

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

Але коли я прочитав статтю про CBES, це мене вразило, тому я дуже радий зараз, коли використовую CBES та відкриваю нові можливості цього.


1
Ну, google.no/… @ slide 16
hayer

@bobenko, будь ласка, дайте посилання на статтю про CBES. Мені теж дуже цікаво в цьому;)
Edward83

1
І lambdor.net/?p=171 @ знизу, це своєрідне резюме мого питання Як можна визначити різні функціональні можливості щодо відносно складних, неелементарних компонентів? Які найелементарніші компоненти? Яким чином елементарні компоненти відрізняються від чистих функцій? Як існуючі компоненти можуть автоматично спілкуватися з новими повідомленнями з нових компонентів? Який сенс ігнорувати повідомлення, про який компонент не знає? Що сталося з моделлю введення-процес-вихід?
хайєр

1
ось хороший відповідь на ПБС stackoverflow.com/a/3495647/903195 більшість статей я досліджував це від цієї відповіді. Я почав і надихнувся на cowboyprogramming.com/2007/01/05/evolve-your-heirachy тоді в Gems 5 (наскільки я пам’ятаю) була хороша стаття з прикладами.
Євген

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

3

Це як Майкл, Патрік Хьюз і Блекі. Рішення уникнути простого переміщення проблеми навколо - це відмовитись від ідеології, яка спричинює проблему.

Його менше OOD і більше схоже на функціональне програмування. Коли я почав експериментувати з компонентним дизайном, я помітив цю проблему в дорозі. Я ще більше погуглився, і знайшов "Функціональне реактивне програмування" як рішення.

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

Однак певним чином ви все-таки можете стверджувати, що проблема знову змінилася. Бо що робити, якщо це не прямо, в якому порядку потрібно запускати ваші Системи? Що робити, якщо існують циклічні взаємозв'язки і це лише питання часу, перш ніж ви будете дивитися на безлад, якщо інакше і перемикаєте заяви? Це неявна форма обміну повідомленнями, ні? На перший погляд, я думаю, що це невеликий ризик. Зазвичай речі обробляються по порядку. Щось на кшталт: Введення гравця -> Позиції особи -> Виявлення зіткнень -> Логіка гри -> Візуалізація -> Почніть спочатку. У такому випадку ви маєте одну систему для кожної, надайте кожній Системі метод update (), а потім запускайте їх послідовно у вашому гейлері.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.