在平台无关的设计中传递特定于平台的数据?

3
我有一个用C++编写的游戏引擎设计,其中一个跨平台的游戏对象包含在特定于平台的应用程序对象中。
我试图解决的问题是需要从应用程序向游戏传递特定于操作系统的数据的情况。在这种情况下,我需要将Windows的主HWND传递给DirectX或其他平台的OpenGL上下文给使用的渲染器。不幸的是,我对渲染器的控制很少,它可以期望特定于平台的数据。
我意识到我可以在应用程序端初始化渲染器,但我更希望游戏决定何时以及在哪里进行。通常,我对应用程序端有控制,但对游戏端没有控制。游戏作者可能会选择使用不同的渲染器。
我也考虑过使用某种“属性管理器”,通过字符串传递数据,但我不太喜欢那个想法。
有什么好的建议吗?

如果游戏对象是平台无关的,那为什么还需要传递平台相关的变量呢? - Tom Dalling
好问题!简而言之,我们对Application类有控制权,但对渲染器或游戏类没有控制权。这只是我们目前正在探索的一些东西。 - djcouchycouch
4个回答

4

记住,您只需要在编译时知道目标平台。有了这些信息,您可以为正确的平台“切换”组件。

在良好的设计中,游戏不应该需要关于它的平台的任何信息; 它只应该保留逻辑和相关组件。

您的“引擎”类应该担心平台问题。

游戏类只应通过不特定于平台的公共函数与引擎对象进行接口交互; 您可以为每个平台拥有多个版本的引擎对象,并在编译时选择要使用的版本。

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

请注意,游戏应告诉应用程序初始化渲染器; 游戏逻辑和实现细节之间应有严格的界限。渲染器是实现细节,而不是游戏逻辑的一部分。游戏类不应了解系统,只应了解游戏世界。


1
看模板模式(使用带有纯虚函数的抽象基类,在派生类中进行配置)。

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

如果您更喜欢一种更可控的(而不是面向对象的)方式,游戏部分应该调用应用程序部分中的可配置回调函数,以执行特定于平台的配置。

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

+1,但我更喜欢dauphic的方法,因为使用派生类可以一次性封装多个特定于平台的行为。实际上,基类中的每个公共方法都等同于“可配置的回调函数”。 - j_random_hacker

1
如何使用传递的SystemContext类?您可以有Win32Context、LinuxContext等。这是OGRE处理它的方式(在其情况下为RenderContext)。
Renderer类需要一个SystemContext指针。
在内部,DirectXRenderer(Renderer的后代)动态转换(一次)指针到Win32Context,并从中提取所有平台相关数据。

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都是本机实现。您不需要指向基类的指针并创建本机类,因为您实际上只有一个实现。这避免了在您实际上只有给定平台的1个实现时具有基本虚函数的情况。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接