在独立于平台的设计中传递特定于平台的数据?

时间:2010-01-25 15:00:12

标签: c++ design-patterns cross-platform platform

我有一个用C ++编写的游戏引擎设计,其中独立于平台的游戏对象包含在特定于平台的Application对象中。

我试图解决的问题是我需要将特定于操作系统的数据从应用程序传递到游戏。在这种情况下,我需要将Windows的主要HWND从DirectX或其他平台的OpenGL上下文传递给我正在使用的渲染器。不幸的是,我几乎无法控制渲染器,渲染器可以期待特定于平台的数据。

我意识到我可以在应用程序端初始化渲染器,但我宁愿让游戏决定何时何地进行渲染。一般来说,我可以控制应用程序端而不是游戏端。游戏作者可能会选择使用不同的渲染器。

我也想到了拥有某种“物业经理”的想法,我可以通过字符串传递数据,但我不太喜欢这个想法。

有什么想法吗?

4 个答案:

答案 0 :(得分:3)

请记住,您只需要在编译时了解目标平台。有了这些信息,您可以“交换”组件以获得正确的平台。

在一个好的设计中,游戏应该需要有关它平台的任何信息;它应该只包含逻辑和相关组件。

您的“引擎”课程应该担心该平台。

Game类应该只通过不是特定于平台的公共函数与Engine对象接口;您可以为每个平台提供多个版本的Engine对象,并选择在编译时使用哪个版本。

例如,您可以拥有一个纹理'引擎'类来表示游戏中的纹理。如果你支持OS X和Windows,你可以有一个“Texture.h”,它包括“Windows / Texture.h”或“OSX / Texture.h”,具体取决于你正在编译的平台。两个头文件都将定义一个具有相同接口的Texture类(即它们都具有相同参数的相同公共函数),但它们的实现将是特定于平台的。

为了澄清,游戏应告诉应用程序初始化渲染器;游戏逻辑和实现细节之间应该有一条严格的界限。渲染器是一个实现细节,不是游戏逻辑的一部分。游戏类应该对系统一无所知,只关注游戏世界。

答案 1 :(得分:1)

请参阅模板模式(使用具有可在派生类中配置的纯虚函数的抽象基类。)

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

如果您更喜欢更可控(更少面向对象)的方式,Game部分应调用Application部分中的可配置回调函数,以执行特定于平台的配置。

E.g:

// 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);
}

答案 2 :(得分:1)

传递的SystemContext类怎么样?你有一个Win32Context,LinuxContext等。这就是OGRE处理它的方式(在它的情况下是RenderContext)。

Renderer类采用SystemContext指针。

在内部,DirectXRenderer(Renderer的后代)dynamic_casts(一次)指向Win32Context的指针,并从中获取所有与平台相关的数据。

答案 3 :(得分:0)

我喜欢做的是让所有实现与公共数据成员共享一个基类。然后我有一个本机类,基类本身包含特定于平台的信息。这需要特定的目录结构。例如,你有:

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都是本机实现。因为你真的有一个实现,所以你不需要指向基类和新的本机类。当你真正拥有一个给定平台的实现时,这可以避免使用基本虚函数。