3

2D平面上を移動するスプライトの一般的な衝突検出に関する投稿がたくさんあることは知っていますが、私の質問は少し異なります。

2D平面に円を挿入しています。円の半径は可変です。平面上にある他の円と衝突することなく新しい円を挿入できる、平面内のランダムな位置を見つける方法を最適化しようとしています。現在、私は非常に「最適化されていない」アプローチを使用しています。このアプローチでは、平面内にランダムな点を生成し、それを平面上の他のすべての円と照合します。

これを最適化する方法はありますか?この特定のアプリの場合、平面の境界は一度に20〜25個の円しか保持できず、通常は5〜10個の円が存在します。ご想像のとおり、円の数が収まる最大数に近づくと、機能するものを見つける前に多くのポイントをテストする必要があります。非常に遅くなります。

注:safeDistanceは、平面に追加する円の半径です。

コードは次のとおりです。

- (CGPoint)getSafePosition:(float)safeDistance {
// Point must be far enough from edges
// Point must be far enough from other sprites

CGPoint thePoint;
BOOL pointIsSafe = NO;

int sd = ceil(safeDistance);

while(!pointIsSafe) {

    self.pointsTested++; // DEBUG

    // generate a random point inside the plane boundaries to test
    thePoint = CGPointMake((arc4random() % ((int)self.manager.gameView.frame.size.width - sd*2)) + sd, 
                           (arc4random() % ((int)self.manager.gameView.frame.size.height - sd*2)) + sd);

    if(self.manager.gameView.sprites.count > 0) {
        for(BasicSprite *theSprite in self.manager.gameView.sprites) {

            // get distance between test point and the sprite position
            float distance = [BasicSprite distanceBetweenPoints:thePoint b:theSprite.position];

            // check if distance is less than the sum of the min safe distances of the two entities
            if(distance < (safeDistance + [theSprite minSafeDistance])) {
                // point not safe
                pointIsSafe = NO;
                break;
            }

            // if we get here, the point did not collide with the last tested point
            pointIsSafe = YES;
        }
    }
    else {
        pointIsSafe = YES;
    }
}

return thePoint;
}
4

4 に答える 4

3

ウィンドウをwブロックhごとに分割します。wbyh配列を維持しますdistdist[x][y](x、y)を中心とすることができる最大の円のサイズが含まれます。(ピクセルをブロックとして使用できますが、各円を配置して配列全体を更新するため、パッキング密度をわずかに下げる代わりに、速度を向上させるために大きなブロックを選択することをお勧めします。)

初期化

最初に、すべてdist[x][y]をに設定しmin(x, y, w - x, h - y)ます。これは、ウィンドウであるバウンディングボックスによって指定された制限をエンコードします。

更新手順

ウィンドウに円を追加するたびに、たとえば(a, b)半径で配置された円を追加するたびに、のすべての要素rを更新する必要があります。dist

各ポジションに必要な更新(x, y)は次のとおりです。

dist[x][y] = min(dist[x][y], sqrt((x - a)^2 + (y - b)^2) - r);

(明らかに、^2ここではXORではなく二乗を意味します。)基本的に、「状況がすでにそれより悪い場合を除いて、dist[x][y]を配置したばかりの円までの最小距離に設定します。」 dist配置したばかりの円の内側のポイントの値は負になりますが、それは問題ではありません。

次の場所を探す

次に、半径の次の円を挿入する場合はq、スキャンして値>=distの場所を探します。(そのような場所をランダムに選択する場合は、有効な場所の完全なリストを見つけてから、ランダムに1つ選択してください。)distq

于 2009-05-17T16:59:34.623 に答える
2

正直なところ、円が 20 ~ 25 個しかない場合は、手の込んだアルゴリズムやデータ構造 (例: quadtreekd-tree )を使用しても速度が大幅に向上することはありません。 小さな n ではすべてが高速です

これがアプリケーションのボトルネックであると確信していますか? プロファイリングしましたか?はいの場合、これを高速化する方法は、高度なアルゴリズムではなく、マイクロ最適化によるものです。ほとんどの平面が安全でないため、while ループを何度も繰り返していますか?

于 2009-05-17T15:56:40.940 に答える
0

このソリューションはかなり複雑であるため、概要のみを示します。

可能であれば、円を配置する場所を常に見つけることを保証したい場合は、次のことができます。既存の各円 C を考えます。新しい円が C に接するように配置できる場所を見つけようとします。C に十分近い円 D (C 以外) ごとに、角度の範囲があります。ここで、C の周りのこれらの角度のいずれかに新しい円を配置すると、D と交差します。ジオメトリによっては、その範囲が得られます。同様に、C に十分近い 4 つの境界のそれぞれについて、それらの角度の 1 つに新しい円を配置すると境界と交差する角度の範囲があります。これらすべての間隔が C の周囲 360 度すべてをカバーする場合、C に隣接して円を配置することはできず、C の候補がなくなるまで次の円を試す必要があります。

于 2009-09-11T06:06:43.080 に答える
0

平面を多数の小さな長方形 (わずかにquadtreeに関連) に分割し、円の少なくとも 1 つがどの長方形に当たるかを保存できます。挿入ポイントを探すときは、いくつかの「空の」ポイントを探すだけです (ランダムなジャンプは必要なく、一定時間で可能です)。

長方形の数とコンスタレーションは、半径によって計算できます。

于 2009-05-17T16:07:20.877 に答える