Система компонентної сутності - оновлення та замовлення на дзвінки


10

Для того, щоб компоненти могли мати змогу оновлювати кожен кадр (а також залишати цю функціональність поза компонентами, які не потребують), у мене з’явилася ідея зробити компонент UpdateComponent. Інші компоненти на зразок MovableComponent(який утримує швидкість) успадкували б від IUpdatableабстрактного класу. Це змушує MovableComponentреалізувати Update(gametime dt)метод та інший, RegisterWithUpdater()що дає UpdateComponentвказівник на MovableComponent. Багато компонентів могли це зробити, а потім UpdateComponentмогли закликати всі свої Update(gametime dt)методи, не піклуючись про те, хто чи що вони є.

Мої запитання:

  1. Чи здається це схожим на все, що є нормальним чи хтось використовує? Я нічого не можу знайти з цього приводу.
  2. Як я можу підтримувати порядок таких компонентів, як фізика, а потім змінювати положення? Це навіть потрібно?
  3. Які ще способи гарантувати, що компоненти, які слід обробити кожен кадр, насправді обробляються?

EDIT
Я думаю, що я буду вирішувати, як надати менеджеру суб’єктів список типів, які можна оновити. Тоді ВСІ компоненти цього типу можуть оновлюватись, а не керувати нею для кожної сутності (що так само є індексами в моїй системі).

Все-таки. Мої питання залишаються актуальними для мене. Я не знаю, чи це розумно / нормально, чи що, як правило, роблять інші.

Крім того, люди в Безсонні є приголомшливими! / РЕД

Скинутий код для попереднього прикладу:

class IUpdatable
{
public:
    virtual void Update(float dt) = 0;
protected:
    virtual void RegisterAsUpdatable() = 0;
};

class Component
{
    ...
};

class MovableComponent: public Component, public IUpdatable
{
public:
    ...
    virtual void Update(float dt);
private:
    ...
    virtual void RegisterWithUpdater();
};

class UpdateComponent: public Component
{
public:
    ...
    void UpdateAll();
    void RegisterUpdatable(Component* component);
    void RemoveUpdatable(Component* component);
private:
    ...
    std::set<Component*> updatables_;
};

Чи є у вас приклад для компонента, який не можна оновити? Це здається досить марним. Поставте функцію оновлення як віртуальну функцію в компонент. Потім просто оновіть всі компоненти в суті, не потрібно "UpdateComponent"
Maik Semder,

Деякі компоненти більше схожі на власників даних. Як і позиціяComponent. Багато об'єктів можуть мати позицію, але якщо вони є нерухомими, що може бути більшості, всі ці додаткові віртуальні дзвінки можуть накопичуватися. Або скажіть HealthComponent. Для цього не потрібно нічого робити кожен кадр, просто модифікуйте, коли це потрібно. Можливо, накладні витрати не такі вже й погані, але мій UpdateComponent був спробою не мати Update () у ВСЕМ компоненті.
ptpaterson

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

@Maik Моя думка НЕ ​​була тим, що це компоненти, які ніколи / ніколи не зміняться. Я згоден, це нерозумно. Їм просто не потрібно оновлювати кожен кадр, скоріше повідомляйте про те, щоб змінити інформацію, коли це необхідно. У випадку здоров’я з часом з'явиться компонент бонусу за здоров'я, який має оновити. Я не думаю, що є підстави поєднувати це двоє.
ptpaterson

Хочу також зазначити, що код, який я опублікував, містить лише те, що потрібно для пояснення концепції UpdateComponent. Він виключає всі інші форми зв’язку між компонентами.
ptpaterson

Відповіді:


16

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

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

Що ви повинні робити, це зберігання пулів для кожного типу та повторення кожного типу самостійно для оновлення.

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

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

Як я можу підтримувати порядок таких компонентів, як фізика, а потім змінювати положення? Це навіть потрібно?

Код у таких мовах, як C ++, має природний спосіб замовити виконання: введіть висловлювання в цьому порядку.

for (PhysicsComponent *c : physics_components)
    c->update(dt);
for (PositionComponent *c : position_components)
    c->update(dt);

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

physics_step();
for (PositionComponent *c : position_components)
    c->update(dt);
// Updates the position data from the physics data.

Дякую. Я розпочав весь цей проект з орієнтованими на дані (не такими, як керовані даними) на увазі, запобігаючи пропускам кешу і подібного. Але як тільки я почав кодувати, я вирішив просто зробити щось, що працювало в точці com. Виявляється, це ускладнило мені справи. І якщо серйозно, ця стаття про безсоння була чудовою!
ptpaterson

@Joe +1 за дуже гарну відповідь. Хоча я хотів би знати, що таке ікаче та дкаче. Спасибі
Рей Дей

Кеш інструкцій та кеш даних. Будь-який вступний текст з архітектури комп'ютера повинен охоплювати їх.

@ptpaterson: Зауважте, що в презентації Insomniac є невелика помилка - клас компонентів повинен містити свій реєстровий індекс, або вам потрібно окреме відображення індексів пулу до індексів реєстру.

3

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


Я думаю, що я наткнувся на цю статтю кілька тижнів тому. Мені вдалося кодувати декілька дуже простих версій складової системи сутності, кожна з яких дуже різна, оскільки я намагаюся робити різні речі. Намагаюся зібрати всі статті та приклади у щось, чого я хочу і вмію. Дякую!
ptpaterson

0

Мені подобаються такі підходи:

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

Метод IUpdatable ідеї та оновлення (gametime dt) здається трохи надто обмежуючим та вводить додаткові залежності. Це може бути добре, якщо ви не використовуєте підсистем підходу, але якщо ви їх використовуєте, то IUpdatable - це надмірний рівень ієрархії. Зрештою, MovingSystem повинна знати, що вона повинна оновлювати компоненти розташування та / або швидкості безпосередньо для всіх об'єктів, у яких є ці компоненти, тому немає необхідності в деякому проміжному компоненті IUpdatable.

Але ви можете використовувати компонент "Оновлений" як механізм для пропуску оновлення деяких об'єктів, незважаючи на те, що вони мають компоненти розташування та / або швидкість. Компонент, що оновляється, може містити прапор bool, на який можна встановити, falseі який би сигналізував кожній підсистемі, що усвідомлює оновлення, про те, що даний конкретний об'єкт наразі не повинен оновлюватися (хоча в такому контексті Freezable, здається, є більш підходящою назвою компонента).

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