2Dでは、ポイントに最も近いオブジェクトを効率的に見つけるにはどうすればよいですか?


35

かなりのゲームエンジンを持っているので、ポイントのリストの中で最も近いものを見つける機能が欲しいのですが。

ピタゴラスの定理を使用して各距離を見つけ、最小距離を選択することができますが、それにはすべての距離を反復する必要があります。

衝突システムもあり、基本的にはオブジェクトを小さなグリッド(ミニマップのようなもの)上の小さなオブジェクトに変え、同じグリッド空間にオブジェクトが存在する場合にのみ衝突をチェックします。グリッド間隔を大きくするだけで、近接性を確認できます。(すべての単一オブジェクトをチェックするのではなく。)ただし、それは私の基本クラスで追加のセットアップが必要になり、既に散らかったオブジェクトを散らかします。その価値はありますか?

ポイントとサイズのリストに基づいて、最も近いオブジェクトを検出するために使用できる効率的かつ正確なものはありますか?


xおよびy位置の2乗バージョンを保存して、最後に高価なsqrtを行わなくてもピタゴラスの定理を実行できるようにします。
ジョナサンコネル

3
これは、最近傍検索と呼ばれます。それについてはインターネット上にたくさんの文章があります。通常の解決策は、ある種の空間分割ツリーを使用することです。
BlueRaja-ダニーPflughoeft

回答:


38

最近傍検索でのクワッド / オクトツリーの問題は、ノード間の分割を挟んで最も近いオブジェクトが座っている可能性があることです。衝突の場合、これは問題ありません。ノードにない場合は気にしません。しかし、クアッドツリーを使用したこの2Dの例を考えてみましょう。

クアッドツリーの例

ここでは、黒のアイテムと緑のアイテムは同じノードにありますが、黒のアイテムは青のアイテムに最も近いです。ultifinitusの答えは、ツリー内のすべてのアイテムがそれを含む可能性のある最小のノード、または一意のノードに配置される場合にのみ、最近傍を保証することができます。(クワッド/オクツリーと呼ばれる可能性のある構造を実装するには、さまざまな方法があることに注意してください。このアプリケーションでは、より厳密な実装がより適切に機能する場合があります。

より良いオプションはkd-treeです。Kdツリーには、実装可能な非常に効率的な最近傍検索アルゴリズムがあり、任意の数の次元(したがって、 "k"次元)を含めることができます。

ウィキペディアのすばらしい有益なアニメーション: kd-tree最近傍検索

kd-treeを使用する際の最大の問題は、正確に思い出すと、バランスを維持しながらアイテムを挿入/削除することがより難しいことです。したがって、家や木などの静的オブジェクトに1つのkdツリーを使用することをお勧めします。最も近い静的オブジェクトと最も近いモバイルオブジェクトを見つけ、それら2つを比較します。

最後に、kdツリーは比較的簡単に実装でき、それらを使用して多数のC ++ライブラリを見つけることができると確信しています。私が覚えていることから、Rツリーははるかに複雑であり、必要なのが単純な最近傍検索だけである場合はおそらく過剰です。


1
すばらしい答え、細部10.000。
ロイT.

1
非常に真実-「ただ」という言葉はかなり厳しい言葉だったと思います。実装方法に応じて、四分木を最近傍検索に導く方法は間違いなくありますが、既に他の理由(衝突検出など)でそれらを使用していない場合は、より最適化されたkdツリーに固執します。
dlras2

ブラックグリーンブルーの問題を処理する実装を作成したことに注意してください。下部を確認してください。
clankill3r

18

sqrt() 負でない引数の場合、単調、または順序を維持します。

sqrt(x) < sqrt(y) iff x < y

およびその逆。

したがって、2つの距離を比較するだけで、実際の値に関心がない場合はsqrt()、ピタゴラスのものから-stepを切り取ることができます。

pseudoDistanceB = (A.x - B.x + (A.y - B.y
pseudoDistanceC = (A.x - C.x + (A.y - C.y
if (pseudoDistanceB < pseudoDistanceC)
{
    A is closest to B!
}
else
{
    A is closest to C!
}

オクトツリーのものほど効率的ではありませんが、実装が簡単で、少なくとも少し速度を上げます


1
そのメトリックは、ユークリッド距離二乗とも呼ばれます。
moooeeeep 14

10

空間分割を行う必要があります。この場合、効率的なデータ構造(通常はoctree)を作成します。この場合、各オブジェクトは1つまたは複数のスペース(立方体)内にあり、どのスペースにいるのかがわかっている場合は、O(1)を調べて、どのスペースが隣人かを調べます。

この場合、最初に自分のスペース内のすべてのオブジェクトを反復処理することで、最も近いオブジェクトを見つけて見つけることができます。そこに誰もいない場合は最初の隣人を確認でき、誰もいない場合は隣人を確認できます...

これにより、世界のすべてのオブジェクトを反復処理することなく、最も近いオブジェクトを簡単に見つけることができます。いつものように、この速度の向上には多少の簿記が必要ですが、あらゆる種類のものに本当に役立ちます。したがって、大きな世界がある場合は、空間分割と八分木を実装する価値があります。

いつものように、ウィキペディアの記事もご覧ください:http : //en.wikipedia.org/wiki/Octree


7
@ultifinitusこれに追加するには:ゲームが2Dの場合、Octreesの代わりにQuadTreesを使用できます。
TravisG


0

quadTreeから最も近いものを取得するためのJava実装を次に示します。dlras2が記述している問題を扱います:

ここに画像の説明を入力してください

操作は本当に効率的だと思います。それは、現在の最も近い場所からさらに離れた場所でクワッドを検索することを避けるために、クワッドまでの距離に基づいています。

// . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

public T getClosest(float x, float y) {

    Closest closest = new Closest();
    getClosest(x, y, closest);

    return closest.item;
}

// . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

protected void getClosest(float x, float y, Closest closestInfo) {


    if (hasQuads) {

        // we have no starting point yet
        // so get one
        if (closestInfo.item == null) {
            // check all 4 cause there could be a empty one
            for (int i = 0; i < 4; i++) {
                quads[i].getClosest(x, y, closestInfo);
                if (closestInfo.item != null) {
                    // now we have a starting point
                    getClosest(x, y, closestInfo);
                    return;
                }

            }
        }
        else {

            // we have a item set as closest
            // we should check if this quad is
            // closer then the current closest distance
            // let's start with the closest from index

            int closestIndex = getIndex(x, y);

            float d = quads[closestIndex].bounds.distToPointSQ(x, y);

            if (d < closestInfo.dist) {
                quads[closestIndex].getClosest(x, y, closestInfo);
            }

            // check the others
            for (int i = 0; i < 4; i++) {
                if (i == closestIndex) continue;

                d = quads[i].bounds.distToPointSQ(x, y);

                if (d < closestInfo.dist) {
                    quads[i].getClosest(x, y, closestInfo);
                }

            }

        }

    }
    else {

        for (int i = 0; i < items.size(); i++) {

            T item = items.get(i);

            float dist = distSQ(x, y, getXY.x(item), getXY.y(item));

            if (dist < closestInfo.dist) {
                closestInfo.dist = dist;
                closestInfo.item = item;
                closestInfo.tree = this;
            }

        }
    }

}

// . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .


class Closest {

    QuadTree<T> tree;
    T item;
    float dist = Float.MAX_VALUE;

}

ps私はまだkdツリーか何かを使う方が良いと思いますが、これは人々を助けるかもしれません。
clankill3r

これも見てください:bl.ocks.org/llb4ll/8709363-clankill3r 15
1
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.