クワッドツリー/グリッドベースの衝突-ロジックを実行する


13

まず第一に、私は自分のゲームロジックを短期間しか書いていないので、これが率直に思えるかもしれないことをおizeびします。

私は四分木とグリッドベースの衝突検出についてたくさん読んできました。私はロジックを理解しています-基本的にオブジェクトが近くにない限り、衝突をチェックしないでください。しかし、実際にこれをどのように実行するかについては言及されていません。

私は私の頭の中にいくつかの可能な方法を持っていますが、どれがベストかわからない

一般的な衝突テスト-最適化なし

for(var i:int = 0; i < objects.length; i++){
        //find object A
        var objectA = objects[i];

        for(var j:int = i + 1; j < objects.length; j++){
            //find object B
            var objectB = objects[j];

            if(objectA.collidesWith(objectB){

               //handle collision logic
              }
        }

近隣を保存する(方法1) しかし、衝突を最適化して近くのオブジェクトのみをチェックする場合はどうでしょう。それでもすべてのオブジェクトを実行しますか、それともチェックするオブジェクトの近くに配列を作成しますか?

var objects:Array = new Array();
    var neighbours:Array = new Array();

    for(var i:int = 0; i < objects.length; i++){
        //find object A
        var objectA = objects[i];

        for(var j:int = i + 1; j < objects.length; j++){
            //find object B
            var objectB = objects[j];

            if(objectA.isNear(objectB){

               neighbours.push(objectA, objectB);
              }
        }

    }

    //somewhere else
    for(i:int = 0; i < neighbours.length; i++){
        //only check neighbours

        for(j:int = i + 1; j < neighbours.length; j++){

            if(objectA.collidesWith(objectB){

               //handle collision logic
              }
        }
    }

すべてのオブジェクトをループしますが、コリジョンの隣人だけをチェックします(方法3) もう1つの可能性は、衝突をテストする前にオブジェクトが近くにあるかどうかをチェックしながらすべてをループすることです。

for(var i:int = 0; i < objects.length; i++){
        //find object A
        var objectA = objects[i];

        for(var j:int = i + 1; j < objects.length; j++){
            //find object B
            var objectB = objects[j];

            if(objectA.isNear(objectB){
               //they are near - check collision!
               if(objectA.collidesWith(objectB){

               //handle collision logic
              }
            }
        }

    }

オブジェクトをタイルデータに保存する(方法3) タイルベースのシステムを使用すると、別のオプションを使用できます。特定のタイルにあるオブジェクトをタイルデータ自体に保存します。オブジェクトがどのタイル上にあるかを確認するには、周囲のタイルが衝突する可能性のあるオブジェクトを含んでいます:

var ObjectA;

for(var i:int = 0; i < 4; i ++){
//check 4 surrounding tiles from object A

   if(Object.currentTile + surroundingTile[i] CONTAINS collidable object){
   //check collision!
    if(objectA.collidesWith(surroundingTile.object){

    //handle collision logic
     }

}
}

私はいつも実世界を例として見ようとしています。同じ色のアイテムを比較したい場合、色が一致していなくてもすべてのアイテムをチェックするのは非論理的なようです(方法2、各アイテムをチェックします)。すべてをチェックするのではなく、おそらく同じ色のアイテム(互いに近くにあるオブジェクト)を収集し、それらをチェックします(方法1)。

衝突チェックの項目は常に移動しているため、順序が混同されるため、これは適切な比較ではありません。それは私を混乱させるものです。

各アイテムをチェックする方が効率的で、近隣の配列を生成し続ける負担を取り除きますか?

それとも、衝突をチェックするためにそれほど多くのオブジェクトをループする必要がないように、隣人を見つける方が効率的ですか?

各タイルのデータを変更し続けることも非常に集中的に思えるので、それが良いアイデアかどうかはわかりません。

射撃前にオブジェクトが範囲内にある場合、タワーがオブジェクトを検出する必要があるタワー防衛ゲームを考えてきました。また、すべてのアイテムをチェックするのはばかげているように見えますが、時には近くにオブジェクトがまったくないこともあります。

長い投稿をおaびします。いつも自分自身の説明に苦労しています!


これは何語ですか?Javascript?
カイルC

2
Actionscript 3、しかし言語はまったく無関係です。これを処理して構造化する最良の方法を見つけたいだけです:)
omgnoseat

回答:


8

実際の衝突チェックの回数を減らす必要があります。うん、分かります。これについて詳しく説明しましょう。

最適化なしの最初の衝突チェックアルゴリズム:1回の実行で何回のチェックを行いますか?nがオブジェクトの数である場合、これはおよそn *(n-1)チェックになります。多くのオブジェクト(> 100)では、これは恐ろしく遅くなります。

隣人チェック方法はそれほど速くありません。オブジェクトが静的ではなく、頻繁に移動する場合は、ゲームループ内で毎回、オブジェクトごとにこれらの隣接リストを作成する必要があります。n *(n-1)を実行して近隣リストを作成し、各オブジェクトがその近隣の1つと衝突するかどうかをチェックしているため、これは実際には最初のアルゴリズムよりも悪いです。

ゲームスペースを分割する必要があります。ゲーム空間の幅が400x400ピクセルだとしましょう。これをサイズがそれぞれ200x200の4つのサブスペースに分割し、各サブスペースが属する各オブジェクトをチェックできます(1つのオブジェクトが複数のサブスペースの中にある場合があります)。次に、各サブスペース内のオブジェクトが同じサブスペース内の他のオブジェクトと衝突するかどうかを確認するだけです。

したがって、実行時コストは次のようになります。n 4つのサブスペースリストを構築するためのn +(n / 4)*((n-1)/ 4)これは、以前の実行時コストよりもはるかに優れています。これは、サブスペースを小さくすることでさらに削減できます(例:50x50)。

したがって、アルゴリズムは次のようになります。

for each (object in objects)
  check into which subspace the object belongs

for each (subspace in subspaces)
  for each (object in subspace)
    check object for collision with other objects in same subspace

これは、タイルデータのアイデアに多少似ています。ただし、サブスペースはタイルと同じサイズである必要はありません。

クアッドツリーを使用して、これをさらに最適化する最後のステップを実行できます。kを部分空間のカウントとします。スペースオブジェクトリストを作成するために、k * nチェックを行っており、ゲームワールドが大きくなった場合に多くのチェックを行います。

このコストを削減するには、クアッドツリーを使用します。クアッドツリーは、ゲーム空間を分割する別の方法です。400x400スペースを64個の50x50サブスペースに分割し、現在の64個のサブスペースの各オブジェクトをチェックする代わりに、ゲームスペースはゲームスペースのサイズの半分(200x200)の4つのサブスペースに分割されます。小さいサブスペース(100x100)。これは再び50x50のサブスペースに再び分割されます。これがクワッドツリーです。

オブジェクトがどの50x50サブスペースに属しているかを知りたい場合は、200x200サブスペースのどれに属しているかをチェックします。次に、クアッドツリーの1つ下のレベルに進み、見つけたばかりの200x200サブスペース内にある4つの100x100サブスペースをチェックします。これは、オブジェクトがどの50x50サブスペースに属するかがわかるまで繰り返されます。では、いくつのチェックが必要になったのでしょうか?クワッドツリーの各レベルに対して4(オブジェクトは2つの部分空間の端にある可能性があることに注意してください)。したがって、オブジェクトを正しい50x50サブスペースに割り当てるには4 * 3チェックが必要です。64個のチェックよりもはるかに優れています。

そのため、4分木アルゴリズムでは、サブスペースリストを作成するために4 * 3 * nチェックが必要であり、次にk *(n / k)チェックのようなものが各オブジェクトの衝突をチェックする必要があります。


それは非常に明確で理解しやすいオーナ、ありがとうございます!オブジェクトがどの部分空間でどのように処理されますか?サブスペースはオブジェクトとして処理されますか?サブスペース上のオブジェクトは配列として保存され、互いにチェックされますか?または、メインループ内のXとYをチェックして、それがどのサブスペースであるかを確認しますか?アレイを常に構築および変更することは、非常に効率が悪いようです。したがって、適切な方法を使用することを確認したいと思います。最小のサブスペースは最大のオブジェクトと同じくらい大きいはずですか?
-omgnoseat

私は助けてくれてうれしいです:)サブスペースは、そこに含まれる4つのサブスペースを保持するオブジェクトになります。最小のサブスペースは、サブスペースを含まず、オブジェクトのリストを含むため、異なります。更新コストを考慮すると、サブスペースツリーはゲーム開始時に1回構築されます。メインループの各ループでは、最小のサブスペースのオブジェクトリストがクリアされ、再び入力されます。したがって、リストの追加/削除のみが必要です。配列を変更する必要はありません。最小のサブスペースは、複数のゲームオブジェクトを含めるのに十分な大きさでなければなりません。大きさはゲームによって異なり、後で調整できます。
スティーブン

おかげで、最小のサブスペースにある実際のオブジェクト間の衝突もチェックすることを忘れてしまったので、複数のオブジェクトを保持できるはずです。テストを開始しましたが、順調に進んでいます。私が抱えている最大の問題は、オブジェクトがどのサブスペースに属しているかを検出することです。サブスペースの子のX値とY値をループし続けますが、これは適切な方法ですか?
omgnoseat

複数のサブスペースに及ぶオブジェクト(つまり、その場所が境界上または境界付近)をどのように処理しますか?
mghicks

シンプル:しません。オブジェクトは、各サブスペースの境界上にある場合、複数のサブスペースの一部になることができます。したがって、1つのオブジェクトを最大4つのサブスペースに含めることができます。しかし、これは少数派です。
スティーブン
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.