質問

があった もう一つ質問 データ指向設計とは何かということについては、次のようなものがあります。 記事 これはよく言及されます(私はすでに5、6回ほど読みました)。この一般的な概念は理解しています。特に、たとえば 3D モデルを扱う場合、すべての頂点をまとめておき、法線などで顔を汚さないようにしたい場合に当てはまります。

ただし、最も些細なケース (3D モデル、パーティクル、BSP ツリーなど) 以外の場合にデータ指向設計がどのように機能するかを視覚化するのは困難です。データ指向設計を実際に採用し、これが実際にどのように機能するかを示す良い例はありますか?必要に応じて、大規模なコードベースを調査できます。

私が特に興味があるのは、「一つあるところにはたくさんある」というマントラですが、ここでは他の部分とあまり結びつかないように思えます。はい、常に複数の敵がいますが、それでも各敵を個別に更新する必要があります。なぜなら、敵は同じように動いていないからです。上記の質問に対する受け入れられた回答の「ボール」の例にも同じことが当てはまります(実際、その回答へのコメントでこれを質問しましたが、まだ返答は得られていません)。レンダリングでは速度ではなく位置のみが必要ですが、ゲーム シミュレーションでは両方が必要でマテリアルは必要ないというだけでしょうか?それとも何かが足りないのでしょうか?おそらく私はすでにそれを理解していて、思っていたよりもはるかに単純な概念です。

ご指摘をいただければ幸いです。

役に立ちましたか?

解決

では、国防総省とは一体何なのでしょうか?もちろんパフォーマンスが重要ですが、それだけではありません。また、読みやすく、理解しやすく、さらには再利用可能な、適切に設計されたコードも重要です。さて、オブジェクト指向設計とは、カプセル化された仮想「オブジェクト」に適合するコードとデータを設計することです。各オブジェクトは、オブジェクトが持つ可能性のあるプロパティの変数と、それ自体または世界の他のオブジェクトに対してアクションを実行するメソッドを備えた別個のエンティティです。OO 設計の利点は、私たちの周りの (現実の) 世界全体が同じように機能するように見えるため、コードをオブジェクトに精神的にモデル化するのが簡単であることです。相互作用できるプロパティを持つオブジェクト。

ここで問題は、コンピュータの CPU がまったく異なる方法で動作することです。同じことを何度も繰り返すと最も効果的です。何故ですか?キャッシュと呼ばれる小さなもののためです。最近のコンピューターで RAM にアクセスするには、100 または 200 CPU サイクルがかかることがあります (そして CPU はその間ずっと待機する必要があります!) これは長すぎます。つまり、CPU には非常に高速にアクセスできるメモリの小さな部分、つまりキャッシュ メモリが存在します。問題は、それがほんの数 MB のトップであることです。したがって、キャッシュにないデータが必要になるたびに、RAM まで長い道のりを進む必要があります。これはデータだけでなく、コードについても同様です。命令キャッシュにない関数を実行しようとすると、コードが RAM からロードされるときにストールが発生します。

オブジェクト指向プログラミングに戻ります。オブジェクトは大きいですが、ほとんどの関数はそのデータのほんの一部しか必要としないため、不要なデータをロードすることでキャッシュを無駄にしています。メソッドが他のメソッドを呼び出し、そのメソッドが他のメソッドを呼び出すため、命令キャッシュがスラッシングされます。それでも、私たちは同じことを何度も繰り返すことがよくあります。ゲームの弾丸を例に挙げてみましょう。単純な実装では、各箇条書きは個別のオブジェクトになる可能性があります。Bullet Manager クラスがあるかもしれません。最初の箇条書きの更新関数を呼び出します。方向/速度を使用して 3D 位置を更新します。これにより、オブジェクトからの他の多くのデータがキャッシュにロードされます。次に、World Manager クラスを呼び出して、他のオブジェクトとの衝突をチェックします。これにより、他の多くのものがキャッシュにロードされ、元のバレット マネージャー クラスのコードが命令キャッシュから削除される可能性もあります。ここで弾丸の更新に戻ります。衝突はなかったので、弾丸マネージャーに戻ります。何らかのコードを再度ロードする必要がある場合があります。次は、第 2 弾のアップデートです。これにより、大量のデータがキャッシュにロードされ、世界が呼び出されます...等したがって、この仮想の状況では、コードのロードに 2 つのストールがあり、データのロードに 2 つのストールがあるとしましょう。これは、1 つの弾丸に対して少なくとも 400 サイクルが無駄になっており、他のものに当たった弾丸は考慮されていません。現在、CPU は 3 GHz 以上で動作しているため、1 発の弾丸には気付かないでしょうが、100 発の弾丸がある場合はどうなるでしょうか?それともそれ以上ですか?

ここにはたくさんの物語があるのです。はい、オブジェクト、マネージャー クラス、ファイル アクセスなどしか取得できない場合があります。しかし、多くの場合、同様のケースがたくさんあります。単純な、または単純ではないオブジェクト指向設計でも、多くの問題が発生します。そこでデータ指向設計に入ります。DOD の鍵は、OO 設計のようにその逆ではなく、データに基づいてコードをモデル化することです。これは設計の最初の段階から始まります。最初に OO コードを設計してからそれを最適化するわけではありません。データをリストして調べ、それをどのように変更するかを考えることから始めます (実際の例についてはすぐに説明します)。コードがデータをどのように変更するかがわかったら、できるだけ効率的に処理できるようにデータをレイアウトできます。これでは、あらゆる場所にひどいコードとデータのスープができるだけだと思う​​かもしれませんが、それは設計が間違っている場合にのみ当てはまります (OO プログラミングでは、設計が悪い場合も同様に簡単です)。適切に設計すれば、特定の機能を中心にコードとデータをきちんと設計でき、コードが非常に読みやすく、さらには再利用可能になります。

さて、箇条書きに戻ります。箇条書きごとにクラスを作成する代わりに、箇条書きマネージャーのみを保持します。各弾丸には位置と速度があります。各箇条書きの位置を更新する必要があります。各弾丸には衝突チェックが必要で、何かに当たったすべての弾丸はそれに応じて何らかのアクションを実行する必要があります。したがって、この説明を見るだけで、このシステム全体をより良い方法で設計できるようになります。すべての弾丸の位置を配列/ベクトルに入れてみましょう。すべての弾丸の速度を配列/ベクトルに入れてみましょう。次に、これら 2 つの配列全体を反復処理し、各位置の値を対応する速度で更新することから始めましょう。データ キャッシュにロードされたすべてのデータが、これから使用するデータになります。スマートなプリロード コマンドを使用して、配列データを事前にプリロードして、データに到達したときにデータがキャッシュに存在するようにすることもできます。次に衝突チェックです。ここでは詳しく説明しませんが、すべての箇条書きを次々に更新することがどのように役立つか想像できるでしょう。また、衝突があった場合、新しい関数を呼び出したり、何も実行したりしないことにも注意してください。衝突があったすべての弾丸を含むベクトルを保持するだけで、衝突チェックが完了したら、すべての弾丸を次々に更新できます。データのレイアウトを変えることで、メモリ アクセスが大量にあった状態から、メモリ アクセスがほとんどない状態になったのがわかりますか?また、OO の方法で設計されていないにもかかわらず、私たちのコードとデータが依然として理解しやすく、再利用しやすいことにも気づきましたか?

それで、「一あるところにたくさんある」という話に戻ります。OO コードを設計するときは、プロトタイプ/クラスという 1 つのオブジェクトについて考えます。弾丸には速度があり、弾丸には位置があり、弾丸はその速度によってフレームごとに移動し、弾丸は何かに当たる可能性があります。それについて考えるとき、速度、位置、および弾丸を移動して衝突をチェックする更新関数を備えたクラスについて考えるでしょう。ただし、複数のオブジェクトがある場合は、それらすべてについて考える必要があります。弾丸には位置と速度があります。一部の弾丸は衝突する可能性があります。私たちがもはや個々のオブジェクトについて考えていないことがわかりますか?私たちはそれらすべてを考慮し、現在はかなり異なる方法でコードを設計しています。

これが質問の 2 番目の部分の答えに役立つことを願っています。それぞれの敵をアップデートする必要があるかどうかではなく、敵をアップデートする最も効率的な方法が重要です。また、DOD を使用して敵だけを設計してもパフォーマンスはそれほど向上しないかもしれませんが、これらの原則に基づいてゲーム全体を設計すると (該当する場合のみ!)、パフォーマンスが大幅に向上する可能性があります。

質問の最初の部分は、国防総省の他の例です。申し訳ありませんが、そこにはそれほど多くはありません。ただし、本当に良い例が 1 つあります。私は少し前にこれを見つけました。Bjoern Knafla によるビヘイビア ツリーのデータ指向設計に関するシリーズです。 http://bjoernknafla.com/data-owned-behavior-tree-overview おそらく 4 つのシリーズの最初の記事から始めるとよいでしょう。リンクは記事自体にあります。古い質問にもかかわらず、これが役立つことを願っています。あるいは、他の SO ユーザーがこの質問に遭遇し、この回答を活用できるかもしれません。

他のヒント

私はあなたにリンクされている質問や記事を読みます。

私はデータ駆動型のテーマに一冊の本を読んだデザインます。

私はあなたとほとんど同じ船にいるよ。

私はノエルの記事を理解する方法は、あなたが一般的なオブジェクト指向の方法であなたのゲームをデザインするということです。あなたはクラスやメソッドを持つクラスの仕事ます。

あなたのデザインをやった後は、

、あなた自身に次の質問をお願いします:

をどのように私は私は1つの巨大なブロブ?

に設計されてきたデータのすべてを手配することができます 下位方法の多くと、一つの機能の方法として、あなたの全体のデザインを書くの面で考えてください。それは私の青春の大規模な50万ラインCOBOLプログラムのことを思い出すます。

さて、あなたはおそらく一つの巨大な機能の方法として、ゲーム全体を書き込みません。本当に、記事では、ノエルは、ゲームのレンダリング部分について話しています。ゲームエンジン(一つの巨大な機能法)、ゲームエンジン(OOPコード)を駆動するためのコードと考える。

  私は特にに興味があることは、私は本当にここに残りの部分と接続するように見えることはできません「多くがあるものがあります」マントラ、です。はい、常に複数の敵、まだ、あなたはまだ彼らは同じように動いていない原因を今、彼らをしている、個々の敵を更新するが必要とされますか?

あなたは、オブジェクトの観点で考えています。機能性の観点から考えてみます。

各敵更新がループの反復である。

重要なのは敵のデータを1つのメモリ・ロケーションではなく、敵オブジェクトのインスタンス間で普及して可能に構成されていることです。

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top