3

プラットフォームに依存しないゲーム オブジェクトがプラットフォーム固有の Application オブジェクトに含まれる C++ で記述されたゲーム エンジンの設計があります。

私が解決しようとしている問題は、アプリケーションからゲームに OS 固有のデータを渡す必要がある場合です。この場合、DirectX 用の Windows からのメイン HWND、または他のプラットフォーム用の OpenGL コンテキストを、使用しているレンダラーに渡す必要があります。残念ながら、プラットフォーム固有のデータを期待できるレンダラーをほとんど制御できません。

アプリケーション側でレンダラーを初期化できることはわかっていますが、それを行うタイミングと場所をゲームに決定させたいと考えています。通常、私はアプリケーション側を制御できますが、ゲーム側は制御できません。ゲーム ライターは、別のレンダラーを使用することを選択する場合があります。

また、文字列を介してデータを渡すことができるある種の「Property Manager」を用意するというアイデアも楽しみましたが、そのアイデアはあまり好きではありません。

何か案は?

4

4 に答える 4

4

ターゲット プラットフォームを知っていればよいのは、コンパイル時だけであることを忘れないでください。この情報を使用して、正しいプラットフォームのコンポーネントを「スワップ インおよびスワップ アウト」できます。

優れた設計では、ゲームはそのプラットフォームに関する情報を必要としません。ロジックと関連コンポーネントのみを保持する必要があります。

「Engine」クラスは、プラットフォームについて心配する必要があります。

Game クラスは、プラットフォームに固有ではないパブリック関数を介してエンジン オブジェクトとのみやり取りする必要があります。プラットフォームごとにエンジン オブジェクトの複数のバージョンを用意し、コンパイル時に使用するバージョンを選択できます。

たとえば、ゲーム内のテクスチャを表すテクスチャ「エンジン」クラスを作成できます。OS X と Windows をサポートしている場合は、コンパイルしているプラ​​ットフォームに応じて、「Windows/Texture.h」または「OSX/Texture.h」を含む「Texture.h」を作成できます。どちらのヘッダーも同じインターフェイスを持つ Texture クラスを定義します (つまり、どちらも同じ引数を持つ同じ public 関数を持ちます) が、それらの実装はプラットフォーム固有になります。

明確にするために、ゲームはアプリケーションにレンダラーを初期化するように指示する必要があります。ゲームのロジックと実装の詳細の間には厳密な境界線が必要です。レンダラーは実装の詳細であり、ゲーム ロジックの一部ではありません。ゲーム クラスは、システムについては何も知らず、ゲームの世界だけを知っている必要があります。

于 2010-01-25T15:08:25.193 に答える
1

テンプレート パターンを参照してください (派生クラスで構成できる純粋仮想関数を持つ抽象基本クラスを使用します)。

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

より制御しやすい (オブジェクト指向ではない) 方法を好む場合は、ゲーム パーツがアプリケーション パーツで構成可能なコールバック関数を呼び出して、プラットフォーム固有の構成を実行する必要があります。

例えば:

// 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);
}
于 2010-01-25T15:09:34.567 に答える
1

渡される SystemContext クラスはどうですか? Win32Context、LinuxContext などがあります。これは、OGRE がそれを処理する方法です (この場合は RenderContext)。

Renderer クラスは SystemContext ポインターを受け取ります。

内部的に、DirectXRenderer (Renderer の子孫) は、Win32Context へのポインターを (1 回) dynamic_casts し、そこからプラットフォームに依存するすべてのデータを取り出します。

于 2010-01-25T15:11:58.760 に答える
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つの実装があるため、基本クラスへのポインターとネイティブクラスの新しいポインターを用意する必要はありません。これにより、特定のプラットフォームの実装が実際に 1 つしかない場合に、基本仮想関数を使用する必要がなくなります。

于 2010-01-25T16:51:39.330 に答える