オブジェクトをレンダリングから分離する必要があるのはなぜですか?


11

Disclamer:私はエンティティシステムパターンが何であるかを知っており、それを使用していません

オブジェクトとレンダリングの分離についてたくさん読みました。ゲームロジックは、基盤となるレンダリングエンジンから独立している必要があるという事実について。それはすべてうまく、そしてダンディであり、それは完全に理にかなっていますが、他の多くの苦痛も引き起こします:

  • ロジックオブジェクトとレンダリングオブジェクト(アニメーションの状態を保持するオブジェクト、スプライトなど)間の同期の必要性
  • レンダリングオブジェクトがロジックオブジェクトの実際の状態を読み取るために、ロジックオブジェクトを公開する必要があります(多くの場合、ロジックオブジェクトは、ダムゲッターおよびセッターオブジェクトに簡単に変換されます)

これは私には良い解決策のように聞こえません。一方、オブジェクトをその3D(または2D)表現として想像することは非常に直感的であり、維持も非常に簡単です(そして、はるかに多くのカプセル化も可能です)。

グラフィックス表現とゲームロジックを一緒に維持し(同期の問題を回避)、レンダリングエンジンを抽象化する方法はありますか?または、上記の欠点を引き起こさないゲームロジックとレンダリングを分離する方法はありますか?

(例として、私は抽象的な講演の理解があまり得意ではありません)


1
エンティティシステムパターンを使用していないと言うときの意味の例と、レンダリングの懸念をエンティティの懸念から分離する必要があるかどうかに関連する考えを提供することも役立ちます。ゲームロジック。
michael.bartnett 2013

@ michael.bartnett、システムで処理される再利用可能な小さなコンポーネントでオブジェクトを分離するのではなく、ほとんどのパターンの実装でそうです。私のコードは、代わりにMVCパターンへの試みです。しかし、質問はどのコードにも依存していません(言語にさえ依存していないため)。癌を治すように思われるECSを使用するように説得しようとする人がいると知っていたので、私はディスクラマーを入れました。そして、ご覧のとおり、それはとにかく起こりました。

回答:


13

ワールドプレイヤーボスで構成されるシーンがあるとしますああ、これは3人称ゲームなので、カメラも持っています

したがって、シーンは次のようになります。

class Scene {
    World* world
    Player* player
    Enemy* boss
    Camera* camera
}

(少なくとも、それは基本的なデータです。データをどのように含めるかはあなた次第です。)

シーンを更新してレンダリングするのは、ゲームをプレイしているときだけであり、一時停止しているときやメインメニューではないので、ゲームの状態にアタッチします。

State* gameState = new State();
gameState->addScene(scene);

これで、ゲームの状態にシーンがあります。次に、シーンでロジックを実行して、シーンをレンダリングします。ロジックについては、更新関数を実行するだけです。

State::update(double delta) {
    scene->update(delta);
}

このようにして、すべてのゲームロジックをSceneクラスに保持できます。参考までに、エンティティコンポーネントシステムでは、次のようにすることもできます。

State::update(double delta) {
    physicsSystem->applyPhysics(scene);
}

とにかく、これでシーンを更新することができました。それを表示したい!上記と同様の処理を行います。

State::render() {
    renderSystem->render(scene);
}

どうぞ。renderSystemはシーンから情報を読み取り、適切な画像を表示します。簡略化すると、シーンのレンダリング方法は次のようになります。

RenderSystem::renderScene(Scene* scene) {
    Camera* camera = scene->camera;
    lookAt(camera); // Set up the appropriate viewing matrices based on 
                    // the camera location and direction

    renderHeightmap(scene->getWorld()->getHeightMap()); // Just as an example, you might
                                                        // use a height map as your world
                                                        // representation.
    renderModel(scene->getPlayer()->getType()); // getType() will return, for example "orc"
                                                // or "human"

    renderModel(scene->getBoss()->getType());
}

本当に単純化されているため、たとえば、プレーヤーの位置とプレーヤーが見ている位置に基づいて回転と移動を適用する必要があります。(私の例は3Dゲームです。2Dで行くと、公園の散歩になります)。

これがあなたが探していたものであることを願っていますか?うまくいけば上から思い出せますが、レンダーシステムはゲームのロジックを気にしません。レンダリングにはシーンの現在の状態のみが使用されます。つまり、レンダリングに必要な情報がシーンから取得されます。そして、ゲームロジック?レンダラーが何をするかは関係ありません。表示されても問題ありません。

また、レンダリング情報をシーンに添付する必要もありません。それは、レンダラーがオークをレンダリングする必要があることを知っているだけで十分です。orcモデルはすでにロードされているため、レンダラーはこれを表示することができます。

これは要件を満たすはずです。グラフィック表現とロジック両方とも同じデータを使用するため、結合されています。しかし、どちらも他に依存していないため、それらは別々です。

編集:そしてなぜこれがなぜこのようにするのかを答えるには?一番簡単な理由は簡単だからです。「こういうことが起こったので、グラフィックを更新する必要がある」と考える必要はありません。代わりに、物事を発生させ、フレームごとに、ゲームが現在起こっていることを確認し、何らかの方法で解釈して、画面上の結果を提供します。


7

あなたのタイトルはあなたの体の内容とは異なる質問をします。タイトルでは、ロジックとレンダリングを分離する理由を尋ねますが、本文ではロジック/グラフィックス/レンダリングシステムの実装を求めます。

2番目の質問は以前に扱われたので、最初の質問に焦点を当てます。

ロジックとレンダリングを分離する理由:

  1. オブジェクトが1つのことを行うべきであるという広く受け入れられている概念
  2. 2Dから3Dに移行したい場合はどうなりますか?プロジェクトの途中で、あるレンダリングシステムから別のレンダリングシステムに変更する場合はどうなりますか?すべてのコードをクロールして、ゲームロジックの途中で大きな変更を加える必要はありません。
  3. コードのセクションを繰り返す理由はおそらくありますが、これは一般に悪い考えと見なされています。
  4. 小さなピースと個別に通信することなく、レンダリングまたはロジックの潜在的に巨大なスワスを制御するシステムを構築できます。
  5. 宝石をプレーヤーに割り当てたいが、宝石のファセットがいくつあるためにシステムが遅くなった場合はどうなりますか?レンダリングシステムを十分に抽象化している場合は、異なるレートで更新して、高価なレンダリング操作を考慮できます。
  6. それはあなたがあなたがやっていることに本当に重要なことについて考えることを可能にします。ダブルジャンプの仕組みを実装したり、カードを引いたり、剣を装備したりするのに、マトリックス変換とスプライトオフセットと画面座標に脳を巻き付けるのはなぜですか。装備した剣を明るいピンクでレンダリングするスプライトは、右手から左に動かしたいだけの理由ではありません。

OOP設定では、新しいオブジェクトのインスタンス化にはコストがかかりますが、私の経験では、システムリソースへのコストは、実行する必要のある特定のことを考えて実装する能力を支払うための小さなコストです。


6

この答えは、実際の例を直接提案するのではなく、レンダリングとロジックを分離することが重要である理由を直感的に理解するためだけのものです。

大きな象が1頭いるとします。部屋の中の誰も象全体を見ることができません。多分誰もがそれが実際に何であるかについて意見を異にします。誰もが象の別の部分を見て、その部分しか扱えないからです。しかし、結局のところ、それが大きな象であるという事実は変わりません。

象は、すべての詳細でゲームオブジェクトを表します。しかし、実際に象(ゲームオブジェクト)のすべてを知っていて、その機能を実行できる必要はありません。

ゲームのロジックとレンダリングを組み合わせると、実際にはすべての人に象全体を見ることができます。何かが変わった場合、誰もがそれについて知る必要があります。ほとんどの場合、彼らは自分が関心がある部分だけを見る必要があります。何かがそれを知っている人を変更した場合、その変更の結果について他の人に伝えるだけでよく、それは彼にとってだけ重要です(これはメッセージまたはインターフェースを介した通信と考えてください)。

ここに画像の説明を入力してください

あなたが述べたポイントは欠点ではなく、エンジンにある必要があるよりも多くの依存関係がある場合にのみ欠点です。つまり、システムは象の一部を必要以上に認識します。これは、エンジンが「正しく」設計されていなかったことを意味します。

ロジックとレンダリングを2つの異なるスレッドに配置するマルチスレッドエンジンを使用している場合にのみ、正式な定義との同期が必要です。システム間で多くの同期を必要とするエンジンでさえ、特に大きく設計されていません。

そうでなければ、そのような場合に対処する自然な方法は、システムを入力/出力として設計することです。更新はロジックを実行し、結果を出力します。レンダリングは、更新の結果を含むフィードのみです。本当にすべてを公開する必要はありません。2つのステージ間で通信するインターフェースのみを公開します。エンジンの異なる部分間の通信は、抽象化(インターフェース)やメッセージを介して行われる必要があります。内部ロジックや状態を公開しないでください。

簡単なシーングラフの例を見て、アイデアを説明しましょう。

更新は通常、ゲームループと呼ばれる単一のループを介して行われます(または、それぞれが個別のスレッドで実行される複数のゲームループを介して行われる場合もあります)。ループがゲームオブジェクトを更新すると、メッセージングまたはインターフェースを介して、オブジェクト1と2が更新されたことを通知し、最終的な変換でフィードするだけです。

レンダリングシステムは、最終的な変換のみを行い、オブジェクトについて実際に何が変更されたかを認識しません(たとえば、特定の衝突が発生したなど)。これで、そのオブジェクトをレンダリングするために必要なのは、そのオブジェクトのIDと最終的な変換だけです。その後、レンダラーは何も知らずにメッシュと最終変換をレンダリングAPIに送ります。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.