Bagaimana cara menentukan sel mana dalam kotak yang bersinggungan dengan segitiga yang diberikan?

10

Saat ini saya sedang menulis simulasi AI 2D, tetapi saya tidak sepenuhnya yakin bagaimana memeriksa apakah posisi agen berada dalam bidang pandang orang lain.

Saat ini, partisi dunia saya adalah partisi ruang-sel sederhana (kotak). Saya ingin menggunakan segitiga untuk mewakili bidang pandang, tetapi bagaimana saya bisa menghitung sel yang bersinggungan dengan segitiga?

Mirip dengan gambar ini: masukkan deskripsi gambar di sini

Area merah adalah sel yang ingin saya hitung, dengan memeriksa apakah segitiga memotong sel-sel itu.

Terima kasih sebelumnya.

EDIT:

Hanya untuk menambah kebingungan (atau bahkan membuatnya lebih mudah). Setiap sel memiliki vektor min dan maks di mana min adalah sudut kiri bawah dan maks adalah sudut kanan atas.

Ray Dey
sumber
Tidak bisakah Anda membagi sel menjadi segitiga, dan menguji segitiga-segitiga?
Bebek Komunis
Sel bukan poligon fisik, hanya representasi spasial, dan mengambil keuntungan dari waktu akses O (1) dari suatu array. Jika saya memiliki lingkaran lingkungan di sekitar agen, untuk memperkirakan sel, saya bisa membuat AABB menggunakan jari-jari lingkaran dan dengan mudah menemukan persimpangan. Masalahnya di sini adalah hanya menginginkan sel-sel yang ada di depan saya. Saya yakin ada beberapa persamaan geometri untuk membantu, saya hanya tidak bisa memikirkan apa pun untuk kehidupan saya.
Ray Dey
Jangan suka salah satu jawaban di sini; namun, pertanyaan ini memiliki beberapa jawaban yang sangat bagus: gamedev.stackexchange.com/q/81267/63053
Andrew

Jawaban:

6

Hitung tiga sudut segitiga fov Anda, putar agar menghadap ke arah yang benar dan semacamnya, lalu lakukan salah satu dari:

1) melakukan tes point-in-triangle untuk semua target potensial

2) menghitung kotak pembatas dari segitiga ini, dan lakukan tes point-in-triangle untuk semua target potensial dalam sel dalam kotak pembatas ini - ini akan menjadi kode yang sangat mudah untuk di-debug

Pendekatan serupa adalah dengan menggunakan quadtree daripada grid dan melakukan persimpangan itu. Jika akses ubin O (1) mempercepat Anda, maka hanya menguji semua sel dalam batas segitiga fov untuk segitiga-in seharusnya sangat cepat untuk menjadi instan. Ketika Anda melihat opsi lain, saya anggap tidak dan bahwa O (1) sebenarnya mengambil biaya kehilangan cache yang besar saat Anda merobohkan cache Anda. Anda tentu saja mungkin dapat melihat instruksi pengambilan awal untuk memberi anotasi berjalan kotak terikat Anda dengan ...

3) 'rasterise' segitiga ini dan periksa sel yang 'dicat' - kemungkinan kode yang paling efisien, tapi mungkin hanya sedikit karena saya berspekulasi semuanya didominasi oleh cache miss times dan tergantung pada seberapa kompleks sel Anda dan seberapa sibuknya mereka.

Pendekatan alternatif adalah membuat FOV menjadi bitmap di luar layar dan kemudian membaca nilai piksel untuk masing-masing objek Anda; Anda tidak dapat 'mencampur cat' tetapi dengan sejumlah objek dan dengan hati-hati memilih cat, Anda dapat menyimpulkan siapa yang melihat siapa. Pendekatan ini mirip dengan berapa banyak permainan yang berhasil diklik pengguna - mereka menarik adegan di luar layar menggunakan warna solid untuk area hit. GPU sangat cepat pada pengisian segitiga ...

Akan
sumber
Terima kasih +1 untuk ini, saya telah menggunakan kotak pembatas untuk segitiga untuk dengan cepat memilih sel yang sesuai dan telah menggunakan tes point-in-triangle untuk menentukan anggota sel-sel mana yang berada dalam Field-of-View :)
Ray Dey
3

Solusi kanonik dalam renderers perangkat lunak (yang harus melakukan algoritma ini tepat setiap kali mereka meraster segitiga) adalah, saya percaya, untuk memindai segitiga satu baris piksel pada satu waktu. Tepi kiri dan kanan dari setiap baris dan dihitung dengan berjalan menyusuri sisi segitiga menggunakan Bresenham , dan kemudian Anda mengisi baris di antara mereka.

Saya membahas banyak detail, tetapi itulah ide dasarnya. Jika Anda mencari-cari "render perangkat lunak" dan "rasterisasi segitiga", Anda mungkin akan menemukan lebih banyak detail. Ini adalah masalah yang terpecahkan. Kartu grafis Anda melakukan ini jutaan kali dalam satu bingkai.

Jika Anda menginginkan solusi yang lebih spesifik untuk roguelike, inilah cara saya menerapkan FOV di tambang. Tampaknya bekerja cukup cepat. Ini pada dasarnya bayangan kastor sederhana, bekerja pada oktan pada suatu waktu, memindai keluar dari pemain.

banyak sekali
sumber
1
Itu pendekatan yang sangat keren.
Notabene
2

Saya menggunakan variasi dari algoritma scanline untuk menyelesaikan masalah yang sama persis. Saya mulai dengan menyortir tiga titik segitiga berdasarkan tinggi mereka. Saya kemudian pada dasarnya memeriksa apakah dua tepi berada di sebelah kiri atau di sebelah kanan. Untuk sisi dengan dua sisi, Anda harus menandai baris di mana Anda mengubah sisi mana yang membatasi baris Anda. Untuk sisi dengan satu sisi, Anda selalu dapat menggunakannya.

Jadi untuk setiap baris, saya tahu dua sisi mana yang membatasi itu, dan saya bisa menghitung batas atas dan bawah dalam arah x. Kedengarannya cukup rumit, tetapi hanya mengembun menjadi beberapa baris kode. Pastikan Anda menangani kasing khusus di mana satu ujungnya benar-benar horizontal!

ltjax
sumber
2

Bagaimana dengan mempertahankan rentang kolom untuk setiap baris yang ada dalam segitiga? Apa yang dapat Anda lakukan adalah mengatur kolom min dan maks untuk setiap baris di mana setiap titik berada, dan di mana setiap garis segitiga melintasi garis pemisah baris horizontal.

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]);
    }
}

Keluaran:

Row 0:  2 - 2
Row 1:  1 - 3
Row 2:  1 - 3
Row 3:  2147483647 - -2147483648
Jason Goemaat
sumber
1

Ada beberapa pekerjaan algoritma yang dilakukan di komunitas roguelike yang berhubungan dengan FOV / LOS / pencahayaan di dunia berbasis ubin.

Mungkin Anda dapat menemukan sesuatu di halaman ini yang dapat digunakan untuk membantu menyelesaikan masalah Anda: http://roguebasin.roguelikedevelopment.org/index.php?title=Field_of_Vision

Secara khusus "FOV Permisif" mungkin yang paling berlaku untuk situasi Anda.

Tetrad
sumber