Передача специфичных для платформы данных в независимый от платформы дизайн? - PullRequest
2 голосов
/ 25 января 2010

У меня есть дизайн игрового движка, написанный на C ++, где независимый от платформы игровой объект содержится в специфичном для платформы объекте Application.

Проблема, которую я пытаюсь решить, - это случай, когда мне нужно передать специфичные для ОС данные из приложения в игру. В этом случае мне нужно передать основной HWND из Windows для DirectX или контекст OpenGL для других платформ в средство визуализации, которое я использую. К сожалению, я плохо контролирую рендерер, который может ожидать специфичные для платформы данные.

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

У меня также была идея иметь своего рода «Property Manager», где я мог бы передавать данные через строки, но мне эта идея не очень нравится.

Есть идеи?

Ответы [ 4 ]

3 голосов
/ 25 января 2010

Помните, что вам нужно знать целевую платформу только во время компиляции. С помощью этой информации вы можете «вставлять и выключать» компоненты для правильной платформы.

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

Ваши классы 'Engine' должны беспокоиться о платформе.

Классы Game должны взаимодействовать с объектами Engine только через публичные функции, которые не являются специфичными для платформы; Вы можете иметь несколько версий объектов Engine для каждой платформы и выбирать, какую из них использовать во время компиляции.

Например, у вас может быть класс двигателя 'Texture', который представляет текстуру в игре. Если вы поддерживаете OS X и Windows, у вас может быть «Texture.h», который включает «Windows / Texture.h» или «OSX / Texture.h» в зависимости от платформы, на которой вы компилируете. Оба заголовка будут определять класс Texture с одинаковым интерфейсом (т. Е. Они будут иметь одинаковые публичные функции с одинаковыми аргументами), но их реализация будет зависеть от платформы.

Для уточнения, Игра должна указать Приложению инициализировать Рендерер; должна быть строгая грань между игровой логикой и деталями реализации. Рендерер - это деталь реализации, а не часть игровой логики. Классы игры не должны знать ничего о системе и только об игровом мире.

1 голос
/ 25 января 2010

Как насчет класса SystemContext, который передается? У вас есть Win32Context, LinuxContext и т. Д. Таким образом, OGRE обрабатывает его (RenderContext в этом случае).

Класс рендерера принимает указатель SystemContext.

Внутренне, DirectXRenderer (потомок Renderer) динамически (однократно) указывает на Win32Context и выбирает из него все зависящие от платформы данные.

1 голос
/ 25 января 2010

См. Шаблон шаблона (используйте абстрактный базовый класс с чисто виртуальными функциями, которые можно настроить в производном классе).

http://en.wikipedia.org/wiki/Template_pattern

Если вы предпочитаете более управляемый (и менее объектно-ориентированный) способ, часть Game должна вызвать настраиваемую функцию обратного вызова в части Application для выполнения конфигураций, специфичных для платформы.

например:.

// in Application:
static void SetWindowHandle(GameEngine const& p_game_engine, void* p_callback_data)
{
  p_game_engine.DoSomethingWithHandle(static_cast<ApplicationManager*>(p_callback_data)->GetHWND());
}

void Initialize() {
  this->m_game_engine.Initialize(this, &Application::SetWindowHandle);
}

// ...
// in Game Engine:
// ...

typedef void (*TSetWindowsHandleCallback)(GameEngine const*, void*);

void* m_application_data;
TSetWindowsHandleCallback m_windows_handle_callback;

void Initialize(void *p_application_data, TSetWindowsHandleCallback p_callback)
{
  this->m_application_data = p_application_data;
  this->m_windows_handle_callback = p_callback;
}

void SetWindowsHandle()
{
  this->m_windows_handle_callback(*this, m_application_data);
}
0 голосов
/ 25 января 2010

Мне нравится делать базовый класс общим для всех реализаций с общими членами данных. Затем у меня есть нативный класс с информацией о платформе, включенной в сам базовый класс. Это требовало определенной структуры каталогов. Например, у вас есть:

code
  renderer
    context.h
  platforms
    win32
      renderer
        context_native.h
    osx
      renderer
        context_native.h

code/renderer/context.h
class RenderContextBase { /* shared members */ };
#include "renderer/context_native.h"

code/platform/win32/renderer/context_native.h
class RenderContext : public RenderContextBase { /*win32 specific */ };

code/platform/osx/renderer/context_native.h
class RenderContext : public RenderContextBase { /*osx specific */ };

Используя ваш компилятор «Дополнительные каталоги включения», вы просто добавляете правильный каталог в зависимости от платформы. Например, в win32 вы добавляете «code / platform / win32» в качестве дополнительного каталога. При включении renderer / renderer_native.h он не будет найден в расположении «по умолчанию» и попытается использовать дополнительный каталог.

В любом месте кода RenderContext является нативной реализацией. Вам не нужно иметь указатель на базовый класс и новый собственный класс, поскольку у вас действительно есть 1 реализация. Это позволяет избежать использования базовых виртуальных функций, если у вас действительно есть одна реализация данной платформы.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...