多数のキューブを操作します。パフォーマンスを改善しますか?


12

編集:質問を要約すると、ボクセルベースの世界(Minecraftスタイル(共産主義のダックに感謝))があり、それはパフォーマンスが悪いことに苦しんでいます。私はその情報源には前向きではありませんが、それを取り除く方法についての可能なアドバイスが欲しいです。

私は世界が大量のキューブで構成されているプロジェクトに取り組んでいます(私はあなたに数字を与えますが、それはユーザー定義の世界です)。私のテスト1は、約(48 x 32 x 48)ブロックです。

基本的に、これらのブロックはそれ自体では何もしません。彼らはただそこに座っています

プレーヤーとのやり取りに関しては、使用が開始されます。

ユーザーがマウスで操作するキューブ(マウスオーバー、クリックなど)を確認する必要があります。また、プレーヤーの移動に伴う衝突の検出も必要です。

今では、最初は膨大な量の遅延があり、すべてのブロックをループしていました。

すべてのブロックをループし、どのブロックがキャラクターの特定の範囲内にあるかを見つけて、衝突検出などのためにそれらのブロックのみをループすることで、その遅れを減らすことができました。

しかし、私はまだ憂鬱な2fpsに向かっています。

このラグを減らす方法について他にアイデアはありますか?

ところで、私はXNA(C#)を使用しています。はい、それは3Dです。


Minecraftのような意味ですか?つまり、ボクセル?
共産主義のダック

4
octreeを調べましたか?en.wikipedia.org/wiki/Octree
bummzack

1
ゲームのプロファイリングを試みましたか?ほとんどの時間が費やされているいくつかの重要な領域が表示される場合があります。それはあなたが思うとは違うかもしれません。
減速

2
各キューブの6つの面すべてを描画するのではなく、何にも接触していない面のみを描画することができます
David Ashmore

1
@David:ええ、または彼は最初にキューブごとに1つの描画呼び出しを行うのをやめ、その後個々のポリゴンについて心配することができます。
-Olhovsky

回答:


20

あなたは木について学びたいと思っているようですね!

そして、私が真剣に取り組んでいるのは、現在すべてのキューブの配列をループしている場合、さまざまな空間データ構造を実際に調べる必要があるからです。この場合、キューブの世界を再想像する最良の方法はツリーとしてです。

理由を説明する前に、問題について考えてみましょう。できるだけ少ないコストで、プレーヤーが衝突する可能性のある近くのキューブのリストを取得できるソリューションを探しています。このリストはできるだけ小さく、できるだけ正確にすべきです。

このゾーンを決定するには、プレイヤーの座標空間をキューブマップの座標空間にマッピングする必要があります。つまり、プレーヤーの浮動小数点位置をキューブの多次元配列の個別のインデックスにマッピングする必要があります(表記例は、たとえばworld[31][31][31]64 * 64 * 64多次元配列の正確な中央)。

この同じ離散インデックスを使用して周囲のブロックを単純に計算できます。おそらく、近くのキューブのみをサンプリングしますが、これは常に再計算を必要とし、配置が離散していないオブジェクトを許可しません地図)。

理想的な状況は、キューブマップの特定のセクションのキューブのセットを含むバケットのセットを均等に分割し、周囲の領域を再計算する代わりに、これらのゾーンに出入りするだけです。自明ではない計算の場合、このようなデータを保持することで、すべてのキューブの反復と、近くにあるこれらの個々のセットの反復を排除できます。

問題は、これをどのように実装するかです。

64 * 64 * 64の世界では、8 * 8 * 8 ゾーンに分割されると想像してください。つまり、あなたの世界では、軸ごとに8つのゾーン(X、Y、Z)があります。これらの各ゾーンには8つのキューブが含まれ、この新しい単純化されたインデックスによって簡単に取得できます。

ワールド内のすべてのキューブを反復するのではなく、近くのキューブのセットで操作を実行する必要がある場合は、これらのゾーンを単純に反復して、元の64 * 64 * 64(262144)からわずか520(8 * 8 * 8 + 8)。

このゾーンの世界からズームアウトし、ゾーンをより大きなスーパーゾーンに配置します。各スーパーゾーンには2 * 2 * 2のレギュラーゾーンが含まれます。あなたの世界は現在、512(8 * 8 * 8)が含まれていたようゾーンを、私たちは、8×8×8つの破ることができるゾーンを 64(4 * 4 * 4)にスーパーゾーン 8つの分割することによってゾーンを 2つのでゾーンごとのスーパーゾーン。上記と同じロジックを適用すると、最大反復を512から8に分割しスーパーゾーンを見つけます。次に、最大64で進行中のゾーンを見つけます(合計最大72)!これにより、すでに多くの反復を節約できていることがわかります(262144:72)。

これで、ツリーの有用性がおわかりいただけると思います。各ゾーンはツリー上のブランチであり、各スーパーゾーンは先行するブランチです。あなたは単にあなたが必要なものを見つけるためにツリーを横断しています。より小さなデータセットを使用して、全体的なコストを最小限に抑えます。

次の図は、概念を視覚化するのに役立ちます。(Wikipedia:Octreesからの画像): オクトリー

免責事項:

ボクセル世界が既に固定サイズの多次元配列でレイアウトされている上記の理想的な設定では、プレーヤーの位置を照会し、O(1)コストで周囲のブロックにインデックスを付けることができます!(Olhovskysの説明を参照)しかし、ボクセルゲームでは世界のサイズがめったに固定されないことを考えると、これはより難しくなります。また、HDDからメモリにスーパーゾーン全体をロードできるようにするために、データ構造が必要になる場合があります。固定サイズの多次元配列とは異なり、ツリーは組み合わせアルゴリズムにあまり時間を費やすことなく、これを容易に許可します。


私はそれを得ると言うでしょうが、残念ながら私はしません。ブロックのコリジョンボックスは移動せず、プレイヤーのコリジョンボックスのみが移動します。そして、私は今(すべてのブロックをループすることなく)プレーヤーの5ブロック半径内にあるすべてのブロックを返すメソッドを持っています。面倒ですが申し訳ありませんが、説明はありますか?ところで、簡単にするために、世界を64 x 64 x 64と仮定できますか?
ジョエル

そして、私はまだ5fpsを取得しています:(
ジョエル

私は答えを書き直し、それが助けになったかどうか教えてください。
減速

そこで、この八分木テクニックを使用して、プレーヤーが入ることができるブロックを絞り込みます。私はそれを得るとかなり確信しています。しかし、ブロックのほんの小さな選択に絞り込んだ後、衝突検出を使用することを提案していますか、または他の方法がありますか?
ジョエル

2
プレイヤーがキューブのサイズに対して非常に大きい場合を除き、プレイヤーの周りの衝突をチェックするのがおそらく最も速い方法です。たとえば、プレイヤーが占有するスペースが1つのキューブよりも少ない場合、衝突を見つけるために、周囲の27個のキューブのみをチェックする必要があります。これらのキューブの場所に直接インデックスを付けることができるため、ツリーを必要としません。キューブを、インデックスを作成できる配列に格納し、可能なすべてのキューブの場所に1つのスロットを割り当てます。
-Olhovsky

13

ダニエルズの回答に同意します。大量のボックスを繰り返すことが最も可能性の高い原因であり、空間分割を使用するとゲームを大幅に高速化できますが、問題他の場所にもあり、時間を無駄にする可能性があります。

ゲームの速度を大幅に向上させるには、コードのプロファイルを作成する必要があります。ボトルネックがどこにあるかを特定します。これにより、最大の改善が可能になります。

コードをプロファイリングする方法はたくさんありますが、独自のパフォーマンス分析クラス(Stopwatchクラス(MSDN)を使用できます)を実行したり、PIXを使用してCPU / GPUがどれほどビジーであるかの一般的なアイデアを得ることができます。

コードにPIXイベントマーカーを配置することもできます。これは、PIXの読み出しで色付きの領域として表示されます。これらの関数に対する公式のC#インターフェイスはありませんが、このスレッドでは、C#インターフェイスを自分で作成する方法を示しています。


2
+1、完全に同意します。アルゴリズムを検討するのではなく、コードのプロファイリングを検討する必要があります。私が推測するのは、ブロックを繰り返し処理しているという事実とは無関係であり(それほど多くはない)、各ブロックで行っていることです。最終的には、ブルートフォースよりも優れた方法が必要ですが、48x32x48キューブで単純な反復を処理できない場合は、ループの方法ではなく、各キューブで何をしているのかを再考する必要があります。
ティム・ホルト

4
@Tim:彼のプレーヤーが48x32x48のスペースを占有するのに十分な大きさでない限り、彼はその多くのキューブの近くを繰り返してはいけません。彼がフレームあたり73000キューブを繰り返し処理している場合、プロファイリングを行わずに、それを修正する価値があることを伝えることができます。が必要です。これは、マイクロ最適化や時期尚早な最適化とは呼ばないものです。
-Olhovsky

私のプレイヤーは1キューブのサイズよりも小さいですが、ある段階では大きくなる場合があります(ただしそれほどではありません)
ジョエル

Randomman159:衝突を見つけるには、周囲の27個のキューブに対してテストするだけです。私の答えをご覧ください。
オルホフスキー

6

プレーヤーがキューブのサイズに比べて大きい場合は、おそらく他のものが示唆しているように、オクトツリーまたは他の空間パーティション構造が必要です。

ただし、キューブのサイズに対してプレイヤーが小さい場合、おそらくキューブとの衝突を検出する最も速い方法は、プレイヤーの周囲の領域を単純に線形検索することです。

プレーヤーは1キューブよりも小さいため、テストする必要があるのは、最大で隣接する27キューブとの衝突のみです。

これは、インデックスを作成できる配列にキューブを格納し、各キューブに対して配列内に1つのスロットがあることを前提としています。

他の人が指摘しているように、あなたは実際にあなたを遅くしているものを見るためにコードをプロファイルする必要があります。

ただし、推測しなければならない場合は、おそらくすべてのキューブに対して描画呼び出しを行っていると思いますが、これははるかにボトルネックになります。これを修正するには、ジオメトリのインスタンス化を調べる必要があります。


または、プレーヤーを囲む「バウンディングボックス」を用意し、衝突するオブジェクトを確認して、プレーヤーが衝突するオブジェクトを決定することもできます。優れた物理エンジンがすべての最適化を実行できます。また、衝突する「ブロック」以上のものを許可します。
減速

個人的には、物理​​エンジンの広範なフェーズに依存して73000キューブに対するテストを行いたくはありませんが、衝突を効率的にテストするためにコードを20行ほど書くだけで十分です。また、彼はおそらく現時点で利用する物理エンジンを持っていません。
-Olhovsky

1

物事をスピードアップするためのもう1つの提案:ブロックはほぼ固定されています。つまり、プレーヤーがほとんどのブロックと衝突することはできません。ブロックが公開されているかどうかを示すブール値を追加します。(これは、近隣を調べることで再計算できます。)露出していないブロックは、衝突をチェックする必要はありません。

Minecraftがこれと似たようなことをしていることは明らかです-一度、ロードされていないチャンクにぶつかると、世界を眺めることができました-しっかりとした地面を通して見えるのは、オープンスペース(それらは露出した表面であったためレンダリングされました。)


-1

ボクセルエンジンでその問題が発生しました。

解決策:(octreesよりもはるかに単純)すべてのブロックをループする代わりに、方程式を使用して、ブロック配列内のブロックの位置を決定します。

BlockIndex = (x * WorldWidth * WorldHeight) + (z * WorldHeight) + y;

次に、ブロックが存在するかどうかを確認する場合:

Blocks[BlockIndex].Type > -1;

または、ブロックが存在するかどうかを判断します。


3Dワールドでテストするためのマウスの座標は2つしかないため、ここでの問題はより複雑です。ビューがトップダウンである場合、マウス位置の3つの正しいインデックスのうち2つを見つけ、上から始めてカメラに近い高さでループし、ブロックの最初の出現を見つけることができます。
マーカスフォンブロードー
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.