Як оновити стани та анімації сутностей у грі на основі компонентів?


10

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

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

На даний момент я маю на увазі, що компоненти містять дані та компоненти оновлення систем.

Отже, якщо у мене є проста двовимірна гра з деякими сутностями (наприклад, гравцем, ворогом1, ворогом2), які містять компоненти "Трансформація", "Рух", "Стан", "Анімація" та "Візуалізація", я думаю, що я повинен мати:

  • Система Movement, яка переміщує всі компоненти Movement і оновлює компоненти стану
  • І RenderSystem, що оновлює компоненти Анімації (компонент анімації повинен мати одну анімацію (тобто набір кадрів / текстур) для кожного стану, і оновлення означає означає вибір анімації, що відповідає поточному стану (наприклад, стрибки, переміщення_ліво тощо), і оновлення індексу кадру). Потім RenderSystem оновлює компоненти Render текстурою, що відповідає поточному кадру анімації кожної сутності, і відображає все на екрані.

Я бачив такі реалізації, як Artemis Framework, але не знаю, як вирішити цю ситуацію:

Скажімо, у моїй грі є такі сутності. Кожна сутність має набір станів та одну анімацію для кожного стану:

  • гравець: "простоювати", "рухатися_право", "стрибати"
  • neprijatelj1: "переміщення_уп", "переміщення_наниз"
  • neprija2: "переміщення_ліво", "переміщення_право"

Які найбільш прийняті підходи для оновлення поточного стану кожної організації? Єдине, про що я можу подумати - це наявність окремих систем для кожної групи сутностей та окремих державних та анімаційних компонентів, тому я мав би PlayerState, PlayerAnimation, Enemy1State, Enemy1Animation ... PlayerMovementSystem, PlayerRenderingSystem ... але я думаю, це погано рішення та порушує мету створення системи на основі компонентів.

Як бачите, я тут зовсім загубився, тому дуже вдячний за будь-яку допомогу.

EDIT: Я думаю, що рішення зробити цю роботу такою, якою я маю намір, це:

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

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


Я припускаю, що ви бачили це: зміни стану в об'єктах чи компонентах ? Чи ваше питання принципово відрізняється від цього?
MichaelHouse

@ Byte56 Так, я читав це кілька годин тому. Рішення, яке ви запропонували там, схоже на ідею, яку я тут викрив. Але моя проблема виникає тоді, коли StateComponent і AnimationComponent не однакові для всіх суб'єктів, що є в системі. Чи повинен я розділити цю систему на більш дрібні системи, які обробляють групи сутностей, які мають однакові стани та анімації? (див. останню частину мого оригінального допису для кращого роз'яснення)
miviclin

1
Ви робите statecomponentі animationcomponentзагальнодоступні, щоб використовуватись для всіх організацій. Дані, які вони містять, будуть модифікатором для зміни речей, наприклад, які анімації відтворюються або які стани доступні.
MichaelHouse

Якщо ви говорите про залежність, ви маєте на увазі залежність даних або залежність порядку виконання? Крім того, у вашому проектному рішенні MovementSystem тепер має реалізувати всі різні способи, з якими щось може рухатися? Схоже, це порушує ідею складової системи ...
ADB

@ADB Я кажу про залежність даних. Для оновлення анімації (наприклад, перехід від анімації move_right до анімації move_left) мені потрібно знати поточний стан сутності, і я не бачу, як зробити ці 2 компоненти більш загальними.
miviclin

Відповіді:


5

IMHO Movementкомпонент повинен утримувати поточний стан ( Movement.state), а Animationкомпонент повинен спостерігати за змінами Movement.stateта оновлювати свою поточну анімацію ( Animation.animation) відповідно, використовуючи простий пошук ідентифікатора стану до анімації (як запропоновано в кінці ОП). Очевидно, це від цього Animationбуде залежати Movement.

Альтернативною структурою було б мати загальний Stateкомпонент, який Animationспостерігає та Movementмодифікує, що в основному є модель-перегляд-контролер (стан-анімація-рух у цьому випадку).

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

Удачі.


Тож анімація спостерігає за державою, а держава спостерігає за Рухом ... Залежності все ще є, але я можу спробувати. Чи буде останньою альтернативою щось подібне: Рух сповіщає про зміни суб'єкта господарювання, і суб'єкт господарювання розсилає подію до держави, і тоді той самий процес буде повторений для Держави та Анімації? Як цей підхід може вплинути на ефективність роботи?
miviclin

Перший випадок: контролювавMovement би (не спостерігав). Останній випадок: так, це зробить так, і всі інші компоненти, які слухають такий тип події, отримають його. Продуктивність, звичайно, гірша, ніж чисті методи викликає, але не набагато. Ви можете об'єднати об'єкти події, наприклад. До речі, вам не потрібно використовувати сутність як "вузол події", ви також можете використовувати виділену "шину подій", не виходячи з цього класу сутності повністю. StateMovemententity.dispatchEvent(...);
Суворий

2

Щодо вашої проблеми, якщо ДЕРЖАВИ використовуються лише в анімаціях, то вам навіть не потрібно виставляти це іншим компонентам. Якщо він має більше одного використання, то його потрібно викрити.

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

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

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

Щодо проблеми, яку ви ставите перед різними станами / анімацією для різних ігрових об'єктів, ось що я б робив. По-перше, я б не надто захоплювався на цьому етапі впровадження: реалізуйте лише те, що вам потрібно зараз, щоб він працював, а потім додайте дзвіночки та свистки, коли вони вам потрібні.

Отже, я почав би з компонента "State": PlayerStateComponent, Enemy1State, Enemy2State. Державна складова піклується про зміну штату у відповідний час. State - це щось дуже багато у всіх ваших об'єктів, тому він може перебувати в GameObject.

Тоді з'явився б анімаційний компонент. Це мав би словник анімації, накреслений державою. У update () змініть анімацію, якщо стан зміниться.

Є чудова стаття про побудову рамки, яку я не можу знайти. У ньому сказано, що коли у вас немає досвіду роботи в домені, вам слід вибрати одну проблему і зробити найпростішу реалізацію, яка вирішує поточну проблему . Тоді ви додаєте ще одну проблему / case-use і розширюєте рамки по мірі того, як ви рухаєтесь далі, так що вона органічно зростає. Мені дуже подобається такий підхід, особливо коли ти працюєш з новою концепцією, як ти робиш.

Пропонована мною реалізація є досить наївною, але ось кілька можливих удосконалень, оскільки ви додаєте більше корисних випадків:

  • замініть змінну GameObject словником. Кожен компонент використовує словник для зберігання значень. (переконайтеся, що правильно зіткнутися зіткненням ...)
  • замініть словник простих значень замість посилань: class FloatVariable () {public value [...]}
  • Замість декількох компонентів стану створіть загальний StateComponent, в якому ви можете будувати машини змінних станів. Потрібно мати загальний набір умов, за допомогою яких стан може змінюватися: натискання клавіш, введення миші, змінні змін (ви можете прив'язати це до FloatVariable вище).

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

0

На додаток до відповіді ADB, ви можете використовувати http://en.wikipedia.org/wiki/Dependency_injection , що допомагає, коли вам потрібно побудувати багато компонентів, передаючи їх як посилання на їх конструктори. Очевидно, вони все одно залежатимуть один від одного (якщо це потрібно у вашій кодовій базі), але ви можете поставити всю цю залежність в одне місце, де встановлюються залежності, а решта вашого коду не потребує знати про залежність.

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

Для простих систем, від яких ви можете піти, не використовуючи DI чи чистий код, ваші класи RenderingSystem звучать так, що вам потрібно статично називати їх або хоча б мати їх у кожному компоненті, що в значній мірі робить їх залежними один від одного і важко змінити. Якщо ви зацікавлені в більш чистому підході, перевірте посилання DI wiki посилання вище та прочитайте про «Чистий код»: http://clean-code-developer.com/


У мене вже є одна система, де компоненти досить залежать один від одного. Я дуже активно використовував там ін'єкцію залежності, і хоча я віддаю перевагу над глибокими ієрархіями, я намагаюся створити нову, щоб уникнути з’єднання компонентів, якщо це можливо. Я б нічого не називав статично. У мене був би ComponentManager, до якого має доступ кожна система (кожна система повинна мати посилання на нього), і RendererSystem отримає всі компоненти анімації від менеджера компонентів і надасть поточний стан кожної анімації.
miviclin
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.