すべてが動いているときのスペース分割


8

バックグラウンド

私は友達と一緒に、宇宙を舞台にした2Dゲームに取り組んでいます。可能な限り没入型でインタラクティブなものにするために、何千ものオブジェクトが自由に浮遊し、一緒にクラスター化されたり、空のスペースに漂っていたりしたいと思っています。

チャレンジ

レンダリングと物理エンジンに負担をかけないようにするには、ある種の空間分割を実装する必要があります。私たちが克服しなければならない2つの課題があります。最初の課題は、すべてが動いているため、データ構造の再構築/更新はフレームごとに実行する必要があるため、非常に安価でなければならないということです。2つ目の課題は、オブジェクトの分散です。前述のように、オブジェクトのクラスターと膨大な空きスペースがあり、さらに悪いことに、スペースの境界はありません。

既存のテクノロジー

私はBSPツリー、クワッドツリー、kdツリー、さらにはRツリーなどの既存の手法を見てきましたが、他のセルに移動した多くのオブジェクトを更新しているため、これらのデータ構造は完全には適合しません比較的高価です。

私が試したこと

私は、クエリに基づいて可能な最小のヒットを返すよりも、迅速な挿入/更新を目的としたデータ構造が必要だと判断しました。その目的のために、セルを暗黙的にしたので、各オブジェクトは、その位置を指定すると、どのセルにあるかを計算できます。次に、HashMapセル座標をArrayList(セルの内容)にマップするを使用します。「空の」セルでメモリが失われることがなく、検査するセルを簡単に計算できるため、これはかなりうまく機能します。ただし、これらすべてのArrayLists(最悪の場合N)を作成するとコストがかかるためHashMap、多くの時間をかけて成長します(ただし、初期容量を大きくすることで多少軽減されます)。

問題

わかりましたので、これは機能しますが、まだそれほど高速ではありません。これで、JAVAコードをマイクロ最適化することができます。ただし、プロファイラーはセルの格納に使用するすべてのオブジェクトの作成にほとんどの時間を費やしていると私に言っているので、あまり期待していません。これを大幅に高速化する他のトリック/アルゴリズムがいくつかあることを願っています。理想的なデータ構造は次のようになります。

  • 最優先事項は、データ構造全体の高速更新/再構築です
  • オブジェクトを同じサイズのビンに細かく分割することはそれほど重要ではありません。更新が少し速いことを意味する場合、いくつかの追加のオブジェクトを描画し、いくつかの追加の衝突チェックを実行できます。
  • メモリはそれほど重要ではありません(PCゲーム)

「[...]フレームごとに実行する必要があります。」どうして?オブジェクトが近い将来にセルを離れるかどうかを予測できませんか?
API-Beast

プレーヤーから遠く離れているオブジェクトの更新をスキップできませんか?または、少なくとも更新頻度を大幅に減らしますか?
Liosan

@ Mr.BeastとLiosan、これらの2つのアイデアを組み合わせるとうまくいく可能性がありますが、何か重大なことが起こった場合(急激な速度の増加など)、オブジェクトはそれ自体を理解できなければなりません...このアイデアの使用例はありますか?
ロイT.

プロファイラーは、ほとんどの時間がArrayListの作成または含まれているオブジェクトの初期化に費やされていると伝えていますか?これらのオブジェクトを事前に割り当ててプールできませんか?
Fabien

@Fabienが実際にArrayListを割り当てて拡張することが最大の問題であり、プーリングが解決策になる可能性があります。試行錯誤によって、プールの大きさとプールの配列リストの大きさを理解できるかどうか疑問に思います。
ロイT.

回答:


6

使用している手法は、分子動力学と呼ばれる計算物理学の手法と非常によく似ており、原子の軌道(通常、現在100kから10Mの粒子範囲)には非常に小さなタイムステップが続きます。主な問題は、1つのパーティクルに対する力を計算するために、その位置を他のすべてのパーティクルの位置と比較する必要があることです。

私が提案できるトリックはあります。物事が相互作用できる最大距離を選択する必要があります。開始点として、私はあなたのスペースの長い寸法の1/10のようなものから始めて、好みに合わせて調整します(カットオフが長いほど、正確になりますが、計算は増えます)。

その方法は、すべての粒子(i)をループすることです。(I)iの範囲内のすべてのパーティクルが配列に追加される配列を取得します。最終的に得られるのは2次元配列です。ここで、i番目のエントリはiの範囲の粒子の配列です。iの力を計算するには、iの配列のエントリを確認するだけです。

この方法の技術は、カットオフ距離と追加のパディング(たとえば、20%)を選択することです。速度が向上するのは、各パーティクルのいくつかの相互作用を確認するだけで済み、数ステップごとにのみ近傍を再計算することです。やや速い速度を選び、「パディング」領域を通過するのにかかる時間ステップ数を把握することをお勧めします。パディングを大きくすると(カットオフの50%または100%でも)、近傍の再計算間のステップが増えますが、各ステップが少し遅くなります。このバランスはトレードオフです。

距離を計算するもう1つのトリックは、dの代わりにd ^ 2を使用して、pow()およびsqrt()への呼び出しの束を削除することです。

編集:超技術的ではない参照リンクを見つけるのは難しい。これは私が見つけた唯一のものです。


それは有望なアイデアのように思えます、私は確かにそれを調べます!
ロイT.

2

あなた自身のソリューションは、o(n)でデータ構造の構築を達成できればかなり良いように聞こえ、最適化はアルゴリズムではなくデータ構造の選択で行われなければならないと私は言うでしょう。

いくつかの違いはありますが、同様の実装があります。主なデータ構造は、要素への直接アクセスに最適な固定サイズの配列(ArrayListなど)です。配列の各セルには、リンクリストが含まれています。これは、挿入に最適であり、ループする配列リストと同じくらい優れています。後でリンクリストから要素を削除する必要があるため、この操作を非常に高速に行うには、リストの各要素にそれ自体を指すイテレータを格納することをお勧めします(メモリは問題ではないと言っていますよね?)

初期化では、スペースが固定サイズのタイルに分割されていると仮定して、各「パーティクル」は、スペース内のその位置に一致する配列内のセルに対応するリンクリストの最後に挿入されます。したがって、まだo(n)の複雑さがありますが、用途に合わせたコンテナーを使用して全体を最適化します。

各「パーティクル」には、隣接するものへの高速アクセスを提供するために、含まれているリンクリストへの参照があります。

各フレームで、各パーティクルとその近傍のリストとの相互作用を作成できます。また、タイルの境界付近のしきい値効果を回避するために、周囲の8つのタイルを使用することもできます。

各フレームでパーティション全体を再計算する必要はありません。アイテムが所定の距離を超えて移動した場合、またはセキュリティによりXフレームごとに移動した場合にのみ、アイテムを削除して再度配置する必要があります。リンクされたリストに挿入された瞬間の各アイテムの位置を保存し、各フレームで現在の位置を古いものと比較するというアイデアが考えられます。



シミュレートされた原子の位置に基づいてデータを計算する際に、これに似たものを使用しました。数時間/日かかる計算を高速化し、それを分に変えました。設定は少し複雑です。
ブライアンブルーム2013年
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.