2B'de bir noktaya en yakın nesneyi nasıl verimli bir şekilde bulurum?


35

Oldukça büyük bir oyun motorum var ve en yakın puan listesine ulaşabilmek için bir özellik istiyorum.

Her mesafeyi bulmak ve en az olanı seçmek için Pisagor teoremini basitçe kullanabilirdim , ama bu hepsinden yinelemeyi gerektirir.

Ayrıca, aslında nesneleri daha küçük bir ızgaradaki (bir mini harita gibi) daha küçük nesnelere dönüştürdüğüm ve yalnızca aynı ızgara alanındaki nesneler varsa, çarpışmaları kontrol edeceğim bir çarpışma sistemine sahibim. Bunu yapabilirdim, yakınlığı kontrol etmek için sadece ızgara aralıklarını büyütürüm. (Her bir nesneyi denetlemek yerine.) Ancak, bu benim temel sınıfımda ek kurulum gerektiriyor ve zaten dağınık nesneyi karıştırıyordu. Buna değer mi?

Hangi nesnenin en yakın olduğunu belirlemek için kullanabileceğim, puanlar ve boyutlar listesine göre kullanabileceğim verimli ve doğru bir şey var mı?


Sonunda pahalı sqrt yapmak zorunda kalmadan pythagoras teoremini yapabilmek için x ve y pozisyonlarının kare versiyonlarını saklayın.
Jonathan Connell

3
Buna en yakın komşu araması denir . İnternette pek çok yazı var. Alışılmış çözüm, bir çeşit boşluk ayırma ağacı kullanmaktır.
BlueRaja - Danny Pflughoeft

Yanıtlar:


38

En yakın komşu aramalarında dörtlü / sekizgenli sorun , en yakın nesnenin düğümler arasındaki bölünmenin tam karşısında durması olabilir. Çarpışmalar için bu sorun değil, çünkü düğümde değilse, umrumda değil. Ancak bu 2B örneği bir dörtlü ile düşünün:

Quadtree örneği

Burada, siyah madde ile yeşil madde aynı düğümde olmasına rağmen, siyah madde mavi öğeye en yakın olanıdır. ultifinitus'un cevabı yalnızca en yakın komşuya garanti verebilir , yalnızca ağacınızdaki her öğe onu içerebilecek mümkün olan en küçük düğüme veya benzersiz bir düğüme yerleştirilir - bu daha verimsiz dörtlüklere neden olur. (Dört / octree olarak adlandırılabilecek bir yapıyı uygulamanın birçok farklı yolu olduğunu unutmayın; bu uygulamada daha katı uygulamalar daha iyi işe yarayabilir.)

Daha iyi bir seçenek bir kd ağacı olacaktır . Kd-ağaçları , uygulayabileceğiniz çok etkili bir komşu arama algoritmasına sahiptir ve herhangi bir sayıda boyut içerebilir (dolayısıyla "k" boyutları.)

Vikipedi'den harika ve bilgilendirici bir animasyon: kd-tree en yakın komşu araması

Doğru ağaçları hatırlıyorsam, kd-ağaçları kullanmayla ilgili en büyük sorun, dengeyi korurken eşyaları takma / çıkarma konusunda daha zor olmaları. Bu nedenle, dengelenmiş evler ve ağaçlar gibi statik nesneler için bir tane kd-ağacı ve düzenli olarak dengelenmesi gereken oyuncular ve araçlar içeren bir tane kullanmanızı tavsiye ederim. En yakın statik nesneyi ve en yakın mobil nesneyi bulun ve bu ikisini karşılaştırın.

Son olarak, kd-ağaçları uygulamak oldukça kolaydır ve onlarla çok sayıda C ++ kütüphanesi bulabildiğinizden eminim. Hatırladığım kadarıyla, R-ağaçlar çok daha karmaşık ve ihtiyacınız olan tek şey basit bir komşu arama ise, muhtemelen overkill.


1
Büyük cevap, küçük detay "sadece en yakın komşunun garantisi sadece ağacınızdaki her öğenin mümkün olan en küçük düğüme yerleştirilmesi" Demek istediğim, aynı ve komşu düğümlerdeki tüm öğeler üzerinde yinelenen cevabım, yani 10'dan fazla döngü yapıyorsunuz. 10.000.
Roy T.

1
Çok doğru - Sanırım "sadece" oldukça sert bir kelime. Onları nasıl uyguladığınıza bağlı olarak, dörtlü kenarları en yakın komşu aramalarına koaksiye etmenin kesinlikle bir yolu vardır, ancak bunları daha önce başka nedenlerle kullanmazsanız (çarpışma tespiti gibi) daha optimize edilmiş kd-ağacı ile yapışırdım.
dlras2

Siyah yeşil mavi sorunla ilgilenen bir uygulama yaptığımı not etmek istedim. Altındaki kontrol edin.
clankill3r

18

sqrt() negatif olmayan argümanlar için monoton veya siparişi korumaktır;

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

Ve tam tersi.

Bu nedenle, yalnızca iki mesafeyi karşılaştırmak istiyor ancak gerçek değerleri ile sqrt()ilgilenmiyorsanız, Pythagoras-şeylerinizden -step'i kesebilirsiniz :

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!
}

Ekim ağacı olayı kadar verimli değildir, ancak uygulanması kolaydır ve hızı en azından biraz arttırır


1
Bu metrik ayrıca kare öklid mesafesi olarak da adlandırılır .
moooeeeep

10

Mekansal bölümleme yapmanız gerekir, bu durumda verimli bir veri yapısı (genellikle bir octree) yaparsınız. Bu durumda, her nesne bir veya daha fazla boşluğun içindedir (küpler) Ve eğer hangi alanlarda olduğunuzu biliyorsanız, hangi alanlarda komşularınız O (1) 'e bakabilirsiniz.

Bu durumda, en yakın nesne ilk önce en yakın olanı bulmak için kendi alanınızdaki tüm nesneler üzerinde yinelenerek bulunabilir. Eğer orada kimse yoksa, ilk komşularınızı kontrol edebilirsiniz, eğer kimse yoksa komşularını vb. Kontrol edebilirsiniz.

Bu şekilde, dünyanızdaki tüm nesnelerde yineleme yapmak zorunda kalmadan en yakın nesneyi kolayca bulabilirsiniz. Her zaman olduğu gibi, bu hız kazancı biraz defter tutma gerektirir, ancak her türlü malzeme için gerçekten yararlıdır, bu nedenle büyük bir dünyaya sahipseniz kesinlikle mekansal bölümlendirme ve bir oktree uygulamasına değer.

Her zamanki gibi, ayrıca wikipedia makalesine de bakınız: http://en.wikipedia.org/wiki/Octree


7
@ multifinitus Buna eklemek için: Oyununuz 2D ise, Octrees yerine QuadTrees kullanabilirsiniz.
TravisG


0

İşte benim quadTree'den en yakın olanı bulmak için java uygulamam. Dlras2'nin tanımladığı problemle ilgileniyor:

görüntü tanımını buraya girin

Operasyonun gerçekten verimli olduğunu düşünüyorum. Mevcut durumda en yakın olandan dörtlü olarak arama yapmaktan kaçınmak için dörtlü mesafeye dayanır.

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

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 hala bir kd ağacı ya da başka bir şey kullanmanın daha iyi olduğunu düşünüyorum, ancak bu insanlara yardımcı olabilir.
clankill3r

ayrıca şuna da bakın: bl.ocks.org/llb4ll/8709363
clankill3r
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.