Зарегистрировать компоненты игровых объектов в игровых подсистемах? (Компонентный дизайн игровых объектов)
Я создаю систему объектных игровых объектов. Несколько советов:
GameObject
это просто списокComponents
,- Есть
GameSubsystems
, Например, рендеринг, физика и т. Д. КаждыйGameSubsystem
содержит указатели на некоторые изComponents
,GameSubsystem
это очень мощная и гибкая абстракция: она представляет любой фрагмент (или аспект) игрового мира.
Существует необходимость в механизме регистрации Components
в GameSubsystems
(когда GameObject
создан и составлен). Есть 4 подхода:
- 1: Схема цепочки ответственности. каждый
Component
предлагается каждомуGameSubsystem
,GameSubsystem
принимает решение, котороеComponents
зарегистрироваться (и как их организовать). Например, GameSubsystemRender может зарегистрировать Renderable Components.
профи. Components
ничего не знаю о том, как они используются. Низкая связь. А. Мы можем добавить новый GameSubsystem
, Например, давайте добавим GameSubsystemTitles, который регистрирует все ComponentTitle и гарантирует, что каждый заголовок уникален и предоставляет интерфейс для запроса объектов по заголовку. Конечно, ComponentTitle не должен быть переписан или унаследован в этом случае. B. Мы можем реорганизовать существующие GameSubsystems
, Например, GameSubsystemAudio, GameSubsystemRender, GameSubsystemParticleEmmiter могут быть объединены в GameSubsystemSpatial (чтобы разместить все аудио, emmiter, render Components
в той же иерархии и использовать родительские относительные преобразования).
против. Индивидуальная проверка. Очень неэффективно.
против. Subsystems
знать о Components
,
- 2: каждый
Subsystem
ищетComponents
конкретных типов.
профи. Лучшая производительность, чем в Approach 1
,
против. Subsystems
до сих пор знаю о Components
,
- 3:
Component
регистрируется вGameSubsystem(s)
, Во время компиляции мы знаем, что существует GameSubsystemRenderer, поэтому давайте ComponentImageRender будет вызывать что-то вроде GameSubsystemRenderer::register(ComponentRenderBase*).
Наблюдатель шаблон.Component
подписывается на событие "обновление" (отправленоGameSubsystem(s)
).
профи. Спектакль. Нет ненужных проверок, как в Approach 1
а также Approach 2
,
против. Components
плохо связаны с GameSubsystems
,
- 4: образец посредника.
GameState
(который содержитGameSubsystems
) может реализовать registerComponent(Component*).
профи. Components
а также GameSubystems
ничего не знаю друг о друге.
против. В C++ это выглядело бы как уродливый и медленный переключатель типа.
Вопросы: Какой подход лучше и чаще всего используется в компонентном проектировании? О чем говорит практика? Любые предложения по (управляемой данными) реализации Approach 4
?
Спасибо.
2 ответа
Голосуйте за третий подход.
В настоящее время я работаю над компонентной системой игровых объектов, и я ясно вижу некоторые дополнительные преимущества этого подхода:
Компонент - все более самодостаточная субстанция, поскольку он зависит только от набора доступных подсистем (я предполагаю, что этот набор зафиксирован в вашем проекте).
Управляемый данными дизайн более применим. В идеале, таким образом, вы можете спроектировать систему, в которой компоненты полностью определены в терминах данных, но не в C++.
РЕДАКТИРОВАТЬ: Одна функция, о которой я думал, работая над CBGOS. Иногда удобно иметь возможность проектировать и конструировать пассивные компоненты без подсистем. Когда вы думаете об этом, четвертый подход - единственный путь.
Мой подход состоял в том, чтобы реализовать шаблон прокси в каждой подсистеме. Поскольку каждая подсистема заинтересована только в подмножестве всех компонентов, которые может содержать каждая сущность, прокси хранит указатели только на те компоненты, о которых заботится система, например, система движения заботится только о положении и скорости, поэтому ей нужен прокси, который хранит два указателя на эти компоненты. Если у объекта отсутствует один или несколько из них, подсистема будет игнорировать его. Если присутствуют оба компонента, то прокси-узел создается и добавляется во внутреннюю коллекцию. Также прокси-серверу полезно хранить значение уникального идентификатора для сущности, так что прокси-серверы могут быть добавлены / удалены в постоянное время из каждой подсистемы, если это будет необходимо.
Таким образом, если объект должен быть удален из механизма, одно сообщение, содержащее идентификатор объекта, может быть отправлено каждой подсистеме. Затем прокси-сервер может быть удален из каждой коллекции подсистем независимо.