Как физические или графические компоненты обычно встроены в компонентно-ориентированную систему?

19

Последние 48 часов я потратил на чтение компонентов систем компонентов и чувствую, что готов достаточно приступить к его реализации. Я создал базовые классы Object и Component, но теперь, когда мне нужно начать создавать настоящие компоненты, я немного запутался. Когда я думаю о них с точки зрения HealthComponent или чего-то, что в сущности будет просто собственностью, это имеет смысл. Когда это нечто более общее в качестве компонента физики / графики, я немного запутался.

Мой класс Object до сих пор выглядит следующим образом (Если вы заметили какие-либо изменения, которые я должен сделать, пожалуйста, дайте мне знать, все еще новичок в этом) ...

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 и отслеживание массы / interia / и т.д. будет иметь смысл. Но мне трудно думать о том, как на самом деле поместить специфику в компонент.

Как только я сделаю пару этих «Обязательных» компонентов, я думаю, это будет иметь больше смысла. На данный момент, хотя я все еще немного озадачен.

Эйдан Найт
источник

Ответы:

32

Прежде всего, когда вы создаете системы, основанные на компонентах, вам не нужно подходить к превращению всего в компонент. На самом деле, вы вообще не должны - это ошибка неофита. По моему опыту, лучший способ связать воедино системы рендеринга и физики в компонентной архитектуре - сделать эти компоненты чуть более, чем просто контейнеры для реального объекта рендеринга или физики.

Это имеет дополнительное преимущество, заключающееся в том, что системы рендеринга и физики отделены от системы компонентов.

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

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

Это относительно просто - по какой-то причине это, кажется, сбивает с толку людей, но часто потому, что они слишком усердно об этом думают. Там не так много магии. А поскольку очень важно добиться успеха, а не мучиться из-за идеального дизайна, вам следует строго рассмотреть подход, при котором, по крайней мере, некоторые компоненты имеют определенные интерфейсы и являются прямыми членами игрового объекта, как предположил Момбоко. Это столь же правильный подход, и его легче уловить.

Другие комментарии

Это не относится к вашим прямым вопросам, но вот что я заметил, глядя на ваш код:

Почему вы смешиваете Ogre :: String и std :: string в игровом объекте, который якобы не является графическим объектом и поэтому не должен зависеть от Ogre? Я бы предложил удалить все прямые ссылки на Ogre, кроме самого компонента рендеринга, где вы должны выполнить свое преобразование.

update () - это чисто виртуальная среда, которая подразумевает, что нужно создать подкласс GameObject, что на самом деле прямо противоположно направлению, в котором вы хотите идти с компонентной архитектурой. Суть использования компонентов заключается в предпочтении агрегации функциональности, а не наследования.

Вы могли бы извлечь выгоду из некоторого использования const(в частности, когда вы возвращаетесь по ссылке). Также я не уверен, что вы действительно хотите, чтобы скорость была скаляром (или если она и позиция должны присутствовать в игровом объекте в виде чрезмерно общей методологии компонента, к которой вы, похоже, стремитесь).

Ваш подход к использованию большой карты компонентов и update()вызова в игровом объекте является довольно неоптимальным (и распространенной ошибкой для тех, кто впервые строит подобные системы). Это приводит к очень плохой когерентности кэша во время обновления и не позволяет вам использовать преимущества параллелизма и тенденции к процессу SIMD-стиля для больших пакетов данных или поведения одновременно. Часто лучше использовать дизайн, в котором игровой объект не обновляет свои компоненты, а подсистема, отвечающая за сам компонент, обновляет их все сразу. Это может стоить прочитать.


источник
Этот ответ был превосходным. Я до сих пор не нашел способ расставить абзацы в комментариях (просто введите клавишу ввода), но я рассмотрю эти ответы. Ваше описание системы объектов / компонентов имело большой смысл. Так что просто для пояснения, в случае PhysicsComponent, в конструкторе компонента, я мог бы просто вызвать функцию CreateRigidBody () моего PhysicsManager, а затем сохранить ссылку на это в компоненте?
Эйдан Найт
Что касается части "Другие комментарии", спасибо за это. Я смешал строки, потому что я обычно предпочитаю использовать все функции Ogre в качестве основы для моего проекта, который использует Ogre, но я начал переключать его в системе Object / Component, так как в ней отсутствовали map / pair / etc. Хотя вы правы, и я преобразовал их всех в члены std, чтобы отделить его от Ogre.
Эйдан Найт
1
+1 Это первый здравомыслящий ответ, касающийся модели на основе компонентов, которую я читал здесь за
короткое время. Отличный
5
Это обеспечивает лучшую согласованность (как данных, так и в кэше инструкций) и предоставляет вам возможность почти тривиальной выгрузки обновлений в отдельные потоки или рабочие процессы (например, SPU). В некоторых случаях компоненты даже не нуждаются в обновлении, что означает, что вы можете избежать накладных расходов при вызове метода no-op для update (). Также помогает создавать ориентированные на данные системы - это все о массовой обработке, которая использует преимущества современных тенденций в архитектуре процессора.
2
+1 за «самое важное - добиться цели, а не мучиться из-за идеального дизайна»
Trasplazio Garzuglio
5

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

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

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

Отвечая на термин сверхобобщающий

Я думаю, что понятия не ясны. Когда я говорю «компонент», это не значит, что все компоненты игрового объекта должны наследоваться от интерфейса компонента или чего-то подобного. Однако это подход к универсальным компонентам, я думаю, что это концепция, которую вы называете компонентом.

Компонентная архитектура является еще одной из архитектур, существующих для объектной модели времени выполнения. Это не единственный и не лучший вариант для всех случаев, но опыт показал, что он имеет много преимуществ, таких как низкая связь.

Главной особенностью, которая отличает архитектуру компонентов, является преобразование отношения is-a в has-a. Например, объектная архитектура is-a:

это архитектура

Вместо объектной архитектуры есть -a (компоненты):

имеет архитектуру

Существует также имущественно-ориентированная архитектура:

Например, нормальная объектная архитектура выглядит так:

  • Object1

    • Позиция = (0, 0, 0)
    • Ориентация = (0, 43, 0)
  • Object2

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

Имущественно-ориентированная архитектура:

  • Позиция

    • Object1 = (0, 0, 0)
    • Object2 = (10, 0, 0)
  • ориентация

    • Object1 = (0, 43, 0)
    • Object2 = (0, 0, 30)

Лучше объектная модель архитектуры? С точки зрения производительности, он лучше ориентирован на свойства, потому что он более кеш-ориентирован.

Компонент ссылается на отношение is-a вместо has-a. Компонентом может быть матрица преобразования, кватернион и т. Д. Но отношение с игровым объектом является отношением a-a, у игрового объекта нет преобразования, поскольку оно наследуется. Игровой объект имеет (отношение имеет-а) преобразование. Преобразование тогда является компонентом.

Игровой объект похож на хаб. если вам нужен компонент, который всегда есть, то, возможно, компонент (например, матрица) должен быть внутри объекта, а не указатель.

Существует не идеальный игровой объект для всех случаев, это зависит от движка, библиотек, которые использует движок. Может быть, я хочу камеру, которая не имеет сетки. Проблема с архитектурой заключается в том, что если у игрового объекта есть сетка, то у камеры, которая наследуется от игрового объекта, должна быть сетка. Благодаря компонентам у камеры есть трансформация, контроллер движения и все, что вам нужно.

Для получения дополнительной информации глава 14.2. Архитектура объектной модели времени выполнения из книги «Архитектура игрового движка» Джейсона Грегори посвящена именно этому вопросу.

UML-диаграммы извлекаются оттуда

momboco
источник
Я не согласен с последним абзацем, есть тенденция к чрезмерному обобщению. Компонентная модель не означает, что каждая функциональность должна быть компонентом, тем не менее, необходимо учитывать взаимосвязи функциональности и данных. AnimationController без меша бесполезен, поэтому поместите его в меш. Игровой объект без трансформации не является игровым объектом, он по определению относится к трехмерному миру, поэтому поместите его как обычный элемент в игровой объект. Нормальные правила функциональности инкапсуляции и данных все еще применяются.
Майк Земдер
@Maik Semder: Я согласен с вами в обобщении общих терминов, но я думаю, что термин «компонент» неясен. Я отредактировал свой ответ. Прочитайте это и дайте мне свои впечатления.
Момбоко
@momboco хорошо, вы более менее повторяли одни и те же точки;) Transform и AnimationController по-прежнему описываются как компоненты, что, на мой взгляд, является чрезмерным использованием модели компонентов. Ключ должен определить , что должно быть введено в качестве компонента , а что нет:
Maik Semder
Если что-то необходимо для того, чтобы заставить Game-Object (GO) работать в первую очередь, иными словами, если это не обязательно, а обязательно, то это не компонент. Компоненты должны быть необязательными. Преобразование является хорошим примером, оно вообще не является обязательным для GO, GO без Transform не имеет смысла. С другой стороны, не каждому GO нужна физика, так что это может быть компонентом.
Майк Земдер
Если между двумя функциональными возможностями существует тесная связь, как между AnimationController (AC) и Mesh, то ни в коем случае не нарушайте эту связь, нажимая AC в компоненте, в отличие от Mesh - идеальный компонент, но AC принадлежит в сетку.
Майк Земдер