Bir ızgaradaki hangi hücrelerin belirli bir üçgenle kesiştiği nasıl belirlenir?


10

Şu anda bir 2D AI simülasyonu yazıyorum, ancak bir ajanın konumunun başka birinin görüş alanı içinde olup olmadığını nasıl kontrol edeceğimi tam olarak bilmiyorum.

Şu anda benim dünya bölümleme basit hücre-uzay bölümleme (ızgara). Görüş alanını temsil etmek için bir üçgen kullanmak istiyorum, ancak üçgenle kesişen hücreleri nasıl hesaplayabilirim?

Bu resme benzer: resim açıklamasını buraya girin

Kırmızı alanlar, üçgenin bu hücrelerle kesişip kesişmediğini kontrol ederek hesaplamak istediğim hücrelerdir.

Şimdiden teşekkürler.

DÜZENLE:

Sadece karışıklığa eklemek (veya belki de kolaylaştırmak). Her hücrenin min ve max vektörleri vardır, burada min sol alt köşedir ve max sağ üst köşedir.


Hücreleri üçgenlere bölüp üçgen üçgeni test edemez misiniz?
Komünist Ördek

Hücreler fiziksel çokgenler değil, sadece uzamsal bir gösterimdir ve bir dizinin O (1) erişim sürelerinden yararlanır. Ajanın etrafında bir mahalle çemberi olsaydı, hücrelere yaklaşmak için, dairenin yarıçapını kullanarak bir AABB oluşturabilir ve kesişimleri kolayca bulabilirdim. Buradaki sorun, sadece önümdeki hücreleri istemektir. Eminim yardımcı olacak bazı geometrik denklemler vardır, sadece hayatım boyunca düşünemiyorum.
Ray Dey

Buradaki cevapların hiçbirini beğenmeyin; ancak, bu sorunun gerçekten iyi cevapları vardır: gamedev.stackexchange.com/q/81267/63053
Andrew

Yanıtlar:


6

Fov üçgeninizin üç köşesini hesaplayın, doğru yöne bakacak şekilde döndürün ve bu şekilde yapın:

1) tüm potansiyel hedefler için üçgen içinde nokta testi yapın

2) bu üçgenin sınırlama kutusunu hesaplayın ve bu sınırlama kutusundaki hücrelerdeki tüm potansiyel hedefler için bir üçgen içinde nokta testi yapın - bu hata ayıklamak için çok basit bir kod olacaktır

Benzer bir yaklaşım, bir ızgara yerine dörtlü kullanmak ve bunun üzerindeki kavşakları yapmaktır. O (1) döşeme erişimi sizi hızlandırıyorsa, sadece fov üçgenin sınırlarındaki tüm hücreleri üçgen içinde test etmek, anlık olacak kadar hızlı olmalıdır. Diğer seçeneklere bakarken, sanmıyorum ve O (1) aslında önbelleğinizi attığınız için büyük bir önbellek özledim maliyeti alıyor. Elbette sınırlayıcı kutu yürüyüşünüze açıklama eklemek için önceden getirme talimatlarına bakabilirsiniz ...

3) Bu üçgeni 'rasterleştirin' ve hücreleri 'boyadığı' kontrol edin - muhtemelen en verimli kod, ama belki sadece marjinal olarak önbellek kaçırma zamanlarının baskın olduğu ve hücrelerinizin ne kadar karmaşık olduğuna ve ne kadar meşgul olduğuna bağlı olarak onlar.

Alternatif bir yaklaşım, FOV'yi bir ekran dışı bitmap'e dönüştürmek ve daha sonra nesnelerinizin her biri için piksel değerini okumaktır; boyayı karıştıramazsınız, ancak sınırlı sayıda nesne ile ve boyayı dikkatlice seçerek kimin kim olduğunu gördünüz. Bu yaklaşım, kaç oyunun kullanıcının tıkladığı şeyi çalıştırdığına benzer - hit alanlar için düz renkler kullanarak sahneyi ekrandan çeker. GPU'lar üçgen doldurmada çok hızlı ...


Bunun için +1 teşekkürler, üçgenin uygun hücreleri hızlı bir şekilde seçmek için bir sınırlayıcı kutu kullandım ve bu hücrelerin hangi üyelerinin Görüş Alanı içinde olduğunu belirlemek için bir üçgen noktası testi kullandım :)
Ray Dey

3

Yazılım oluşturuculardaki kanonik çözüm (bir üçgeni rasterleştirdiklerinde bu tam algoritmayı yapmak zorundadır), bence üçgeni bir kerede bir piksel satırı taramaktır. Her satırın sol ve sağ kenarları ve Bresenham kullanarak üçgenin kenarlarından aşağı doğru yürüterek hesaplanır ve daha sonra aralarındaki satırı doldurursunuz.

Ben çok parlıyorum ayrıntıya göz ama temel fikir bu. "Yazılım oluşturma" ve "üçgen rasterleştirme" için uğraşırsanız, muhtemelen daha fazla ayrıntı bulacaksınız. Bu iyi çözülmüş bir sorundur. Grafik kartınız bunu milyonlarca kez bir kare yapıyor.

Eğer daha roguelike özgü çözüm istiyorlarsa, burada ben de benimkinde FOV uygulamaya nasıl. Oldukça hızlı çalışıyor gibi görünüyor. Temelde, bir seferde oktan üzerinde çalışan, oynatıcıdan dışarı doğru tarayan basit bir gölge tekeri.


1
Bu oldukça havalı bir yaklaşım.
Notabene

2

Tam olarak aynı sorunu çözmek için tarama çizgisi algoritmasının bir varyasyonunu kullanıyorum. Üç üçgen noktasını boylarına göre sıralayarak başladım. Daha sonra temel olarak iki kenarın solda mı yoksa sağda mı olduğunu kontrol ediyorum. İki kenarı olan taraf için, satırları hangi kenarı sınırlandıracağınızı değiştirdiğiniz satırları işaretlemelisiniz. Bir kenarı olan taraf için, bunu her zaman kullanabilirsiniz.

Böylece her satır için, hangi iki kenarın onu sınırladığını biliyorum ve üst ve alt sınırları x yönünde hesaplayabilirim. Kulağa oldukça karmaşık geliyor, ancak sadece birkaç satır kod kadar yoğunlaşıyor. Bir kenarın tamamen yatay olduğu özel kasayı kullandığınızdan emin olun!


2

Üçgendeki her satır için bir dizi sütun tutmaya ne dersiniz? Yapabileceğiniz şey, her noktanın olduğu ve her üçgen çizgisinin yatay bir satır ayırıcı çizgisini geçtiği her satır için min ve maks sütunu ayarlamaktır.

public class Point
{
    public float X;
    public float Y;
    public Point(float x, float y) { this.X = x; this.Y = y; }
}

public class Line
{
    float ROW_SIZE = 100f;
    float COL_SIZE = 100f;

    public Point P1, P2; // P1 has the lowest Y
    public float Slope, Intercept; // set in constructor
    public bool IsVertical;

    public Line(Point p1, Point p2)
    {
        if (p1.Y > p2.Y) { P1 = p2; P2 = p1; } // p1 has lowest Y
        else { P1 = p1; P2 = p2; }
        IsVertical = (p1.X == p2.X);
        if (!IsVertical) { Slope = (p2.Y - p1.Y) / (p2.X - p1.X); Intercept = p1.Y - Slope * p1.X; }
    }

    public void ExpandRanges(int[] minCol, int[] maxCol)
    {
        // start out at row, col where P1 is, which has lowest Y
        int row = (int)(P1.Y / ROW_SIZE);
        int col = (int)(P1.X / COL_SIZE);
        int lastRow = (int)(P2.Y / ROW_SIZE);
        int lastCol = (int)(P2.X / COL_SIZE);

        // expand row to include P1
        minCol[row] = Math.Min(col, minCol[row]); maxCol[row] = Math.Max(col, maxCol[row]);

        // now we find where our line intercepts each horizontal line up to P2
        float currY = P1.Y;
        float currX = P1.X;
        while (row < lastRow)
        {
            row = row + 1;
            float rowY = row * ROW_SIZE;
            float diffY = rowY - currY;
            float diffX = IsVertical ? 0f : diffY / Slope;
            currY = currY + diffY;
            currX = currX + diffX;
            col = (int)(currX / COL_SIZE);

            // expand rows above and below dividing line to include point
            minCol[row - 1] = Math.Min(col, minCol[row - 1]);
            maxCol[row - 1] = Math.Max(col, maxCol[row - 1]);
            minCol[row] = Math.Min(col, minCol[row]);
            maxCol[row] = Math.Max(col, maxCol[row]);
        }

        // expand last row to include P2
        minCol[lastRow] = Math.Min(lastCol, minCol[lastRow]);
        maxCol[lastRow] = Math.Max(lastCol, maxCol[lastRow]);
    }

    public static void Test()
    {
        Point p1 = new Point(160, 250);
        Point p2 = new Point(340, 250);
        Point p3 = new Point(250, 40);
        Line l1 = new Line(p1, p2);
        Line l2 = new Line(p2, p3);
        Line l3 = new Line(p3, p1);

        Line[] lines = { l1, l2, l3 };

        int rowCount = 4;
        int[] minCol = new int[rowCount];
        int[] maxCol = new int[rowCount];
        for (int i = 0; i < rowCount; i++)
        {
            minCol[i] = int.MaxValue;
            maxCol[i] = int.MinValue;
        }

        for (int i = 0; i < lines.Length; i++)
            lines[i].ExpandRanges(minCol, maxCol);

        for (int i = 0; i < rowCount; i++)
            Console.WriteLine("Row {0}:  {1} - {2}", i, minCol[i], maxCol[i]);
    }
}

Çıktı:

Row 0:  2 - 2
Row 1:  1 - 3
Row 2:  1 - 3
Row 3:  2147483647 - -2147483648

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.