Як фізичні чи графічні компоненти зазвичай побудовані в системі, орієнтованій на компоненти?


19

Останні 48 годин я провів за читанням на системах компонентних об'єктів, і відчуваю, що готовий почати її впроваджувати. У мене створені базові класи Object and Component, але тепер, коли мені потрібно почати створювати фактичні компоненти, я трохи заплутався. Коли я думаю про них з точки зору HealthComponent або чогось, що в основному було б просто властивістю, це має ідеальний сенс. Коли це щось більш загальне, як компонент фізики / графіки, я трохи заплутався.

Мій клас «Об’єкт» виглядає таким чином досі (Якщо ви помітили якісь зміни, які я повинен зробити, будь ласка, повідомте мене, все ще нове в цьому) ...

typedef unsigned int ID;

class GameObject
{
public:

    GameObject(ID id, Ogre::String name = "");
    ~GameObject();

    ID &getID();
    Ogre::String &getName();

    virtual void update() = 0;

    // Component Functions
    void addComponent(Component *component);
    void removeComponent(Ogre::String familyName);

    template<typename T>
    T* getComponent(Ogre::String familyName)
    {
        return dynamic_cast<T*>(m_components[familyName]);
    }

protected:

    // Properties
    ID m_ID;
    Ogre::String m_Name;
    float m_flVelocity;
    Ogre::Vector3 m_vecPosition;

    // Components
    std::map<std::string,Component*> m_components;
    std::map<std::string,Component*>::iterator m_componentItr;
};

Тепер проблема, з якою я стикаюся, полягає в тому, що б загальна сукупність містила такі компоненти, як фізика / графіка? Для Ogre (мого механізму візуалізації) видимі об'єкти будуть складатися з декількох Ogre :: SceneNode (можливо, декількох), щоб приєднати його до сцени, Ogre :: Entity (можливо, декількох), щоб показати видимі сітки тощо. Було б найкраще просто додати кілька об’єктів GraphicComponent до Об'єкта і дозволити кожному GraphicComponent обробляти один SceneNode / Entity, або ідея мати один з кожного компонента?

Щодо фізики я ще більше плутаю. Я думаю, можливо, створити RigidBody та відстежувати масовість / медіа / тощо. мав би сенс. Але у мене виникають проблеми з думкою про те, як насправді вкласти конкретні компоненти в компонент.

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

Відповіді:


32

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

Це має додаткову перевагу в тому, щоб утримувати системи візуалізації та фізики нероз'єднаними від компонентної системи.

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

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

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

Інші коментарі

Це не пов'язане з вашими прямими питаннями, але це те, що я помітив, переглядаючи ваш код:

Чому ви змішуєте Ogre :: String та std :: string у вашому ігровому об’єкті, який нібито не графічний об’єкт і, отже, не повинен мати залежності від Ogre? Я б запропонував видалити всі прямі посилання на Ogre, за винятком самого компонента візуалізації, де ви повинні зробити свою трансформацію.

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

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

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


Ця відповідь була чудовою. Я все ще не з'ясував способу пробілу в коментарях (клавіша Enter просто подає), але я відповім на ці відповіді. Ваш опис системи Об'єкт / Компонент мав багато сенсу. Отже, щоб уточнити, що стосується PhysicsComponent, то в конструкторі компонента я міг би просто зателефонувати до функції CreateRigidBody () PhysicsManager, а потім зберегти посилання на це в компоненті?
Ейдан Найт

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

1
+1 Ось перша розумна відповідь, що стосується моделі на основі компонентів, яку я прочитав тут, приємний контраст проти надмірного узагальнення
Maik Semder

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

2
+1 за "найважливіше робити речі, а не мучитися над ідеальним дизайном"
Trasplazio Garzuglio

5

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

Ви показуєте архітектуру компонентів із "загальними" компонентами. У вас є основна логіка приєднання та від'єднання «загальних» компонентів. Архітектуру із загальними компонентами важче досягти, тому що ти не знаєш, який це компонент. Перевага полягає в тому, що такий підхід є розширюваним.

Дійсна архітектура компонентів досягається з визначеними компонентами. Під визначеним я маю на увазі інтерфейс визначений, а не реалізація. Наприклад, GameObject може мати IPhysicInstance, Transformation, IMesh, IAnimationController. Це має перевагу в тому, що ви знаєте інтерфейс, і GameObject знає, як поводитися з кожним компонентом.

Відповідаючи на термін надзагально узагальнюючий

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

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

Основною особливістю, яка відрізняє архітектуру компонентів, є перетворення відношення is-a в has-a. Наприклад, архітектура об'єкта is-a:

є-архітектура

Замість об’єктної архітектури has-a (компоненти):

має-архітектуру

Існує також архітектура, орієнтована на властивості:

Наприклад, звичайна архітектура об'єкта така:

  • Об'єкт1

    • Позиція = (0, 0, 0)
    • Орієнтація = (0, 43, 0)
  • Об'єкт2

    • Позиція = (10, 0, 0)
    • Орієнтація = (0, 0, 30)

Архітектура, орієнтована на властивості:

  • Позиція

    • Об'єкт1 = (0, 0, 0)
    • Об'єкт2 = (10, 0, 0)
  • Орієнтація

    • Об'єкт1 = (0, 43, 0)
    • Об'єкт2 = (0, 0, 30)

Краще архітектура об'єктної моделі? З точки зору продуктивності, воно краще орієнтоване на властивості, оскільки воно сприятливіше для кешу.

Компонент посилається на відношення is-a замість has-a. Компонент може бути матрицею перетворення, кватерніоном і т. Д. Але відношення до ігрового об’єкта є відношенням -a, ігровий об’єкт не має трансформації, оскільки він успадковується. Гра-об’єкт має (відношення has-a) перетворення. Трансформація є компонентом.

Гра-об’єкт схожа на хаб. якщо ви хочете компонент, який завжди є, то, можливо, компонент (як матриця) повинен бути всередині об'єкта, а не вказівником.

Не існує ідеального ігрового об’єкта для всіх випадків, це залежить від двигуна, бібліотек, якими користується двигун. Можливо, я хочу, щоб камера не мала сітки. Проблема з архітектурою is полягає в тому, що якщо в ігровому об’єкті є сітка, то камера, яка успадковує від ігрового об'єкта, повинна мати сітку. У компонентних мережах камера має трансформацію, контролер руху та все, що вам потрібно.

Для отримання додаткової інформації, розділ "14.2. Архітектура моделі об'єктного режиму виконання" з книги "Архітектура ігрового двигуна" "Джейсон Григорій" стосується саме цієї теми.

Діаграми UML витягуються звідти


Я не погоджуюсь з останнім абзацом, є тенденція до надмірного узагальнення. Компонентна модель не означає, що кожна функціональність повинна бути компонентом, все ж слід враховувати взаємозв'язки функціональних можливостей і даних. AnimationController без Mesh марний, тому покладіть його там, де йому належить, у сітку. Гра-об’єкт без перетворення не є Game-Object, вона за визначенням належить до 3D-світу, тому введіть її як звичайний член у Game-Object. Нормальні правила функціонування капсуляції та даних все ще діють.
Майк Семдер

@Maik Semder: Я згоден з вами в загальному узагальненні, але думаю, що термін компонент не зрозумілий. Я відредагував свою відповідь. Прочитайте його та дайте мені свої враження.
мамбоко

@momboco добре, ви більш менш повторювали ті ж самі пункти;) Transform і AnimationController все ще описуються як компоненти, що, на мій погляд, є надмірним використанням компонентної моделі. Ключовим моментом є визначення того, що слід включити в компонент, а що ні:
Майк Семдер,

Якщо щось потрібно, щоб змусити Game-Object (GO) працювати в першу чергу, інакше кажучи, якщо це не необов’язково, але обов’язково, то це не компонент. Компоненти повинні бути необов'язковими. Трансформація є хорошим прикладом, вона взагалі не є опціональною для GO, GO без Transform не має сенсу. З іншого боку, не кожен КЗ потребує фізики, тому це може бути складовою.
Майк Семдер

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