จะกำหนดเซลล์ใดในกริดที่ตัดกับสามเหลี่ยมที่กำหนด?


10

ฉันกำลังเขียนแบบจำลอง AI 2D แต่ฉันไม่แน่ใจว่าจะตรวจสอบว่าตำแหน่งของตัวแทนอยู่ในมุมมองของคนอื่นหรือไม่

ปัจจุบันการแบ่งส่วนโลกของฉันคือการแบ่งเซลล์แบบง่าย (ตาราง) ฉันต้องการใช้รูปสามเหลี่ยมเพื่อแสดงเขตของมุมมอง แต่ฉันจะคำนวณเซลล์ที่ตัดกับรูปสามเหลี่ยมได้อย่างไร

คล้ายกับภาพนี้: ป้อนคำอธิบายรูปภาพที่นี่

พื้นที่สีแดงเป็นเซลล์ที่ฉันต้องการคำนวณโดยตรวจสอบว่าสามเหลี่ยมตัดกับเซลล์เหล่านั้นหรือไม่

ขอบคุณล่วงหน้า.

แก้ไข:

เพียงเพิ่มความสับสน (หรืออาจทำให้ง่ายขึ้น) แต่ละเซลล์มี min และ max vector โดยที่ min เป็นมุมซ้ายล่างและ max เป็นมุมขวาบน


คุณแบ่งเซลล์ออกเป็นสามเหลี่ยมไม่ได้หรือไม่และทดสอบสามเหลี่ยมสามเหลี่ยม?
The Duck Communist

เซลล์ไม่ใช่รูปหลายเหลี่ยมเชิงกายภาพเป็นเพียงการแสดงเชิงพื้นที่และใช้ประโยชน์จากเวลาในการเข้าถึง O (1) ของอาร์เรย์ หากฉันมีวงกลมใกล้เคียงรอบตัวแทนเพื่อประมาณเซลล์ฉันสามารถสร้าง AABB โดยใช้รัศมีของวงกลมและหาจุดตัดได้อย่างง่ายดาย ปัญหานี่คือต้องการเพียงเซลล์ที่อยู่ข้างหน้าฉัน ฉันแน่ใจว่ามีสมการทางเรขาคณิตที่จะช่วยฉันไม่คิดอะไรเกี่ยวกับชีวิตของฉัน
Ray Dey

ไม่ชอบคำตอบใด ๆ ที่นี่; อย่างไรก็ตามคำถามนี้มีคำตอบที่ดีจริงๆ: gamedev.stackexchange.com/q/81267/63053
Andrew

คำตอบ:


6

คำนวณมุมทั้งสามของสามเหลี่ยม fov ของคุณหมุนมุมเพื่อให้หันหน้าไปทางที่ถูกต้องและจากนั้นทำหนึ่งใน:

1) ทำการทดสอบจุดในสามเหลี่ยมสำหรับเป้าหมายที่เป็นไปได้ทั้งหมด

2) คำนวณกล่องขอบของสามเหลี่ยมนี้และทำการทดสอบแบบจุดในสามเหลี่ยมสำหรับเป้าหมายที่เป็นไปได้ทั้งหมดในเซลล์ในกล่องขอบเขตนี้ - นี่จะเป็นรหัสที่ตรงไปตรงมามากในการดีบัก

วิธีที่คล้ายกันคือการใช้ควอดทรีแทนที่จะเป็นกริดและทำจุดตัดบนนั้น หากการเข้าถึงไทล์ O (1) เพิ่มความเร็วให้คุณเพียงแค่ทดสอบเซลล์ทั้งหมดในขอบเขตของสามเหลี่ยม fov สำหรับสามเหลี่ยมในควรจะเร็วเท่าที่จะเป็นทันที ในขณะที่คุณกำลังดูตัวเลือกอื่น ๆ ฉันคิดว่ามันไม่ใช่และ O (1) จริง ๆ แล้วใช้แคชขนาดใหญ่ที่พลาดค่าใช้จ่ายเมื่อคุณฟาดแคชของคุณ แน่นอนคุณสามารถดูคำแนะนำการดึงข้อมูลล่วงหน้าเพื่อใส่คำอธิบายประกอบในกล่องเดินของคุณด้วย ...

3) 'rasterise' สามเหลี่ยมนี้และตรวจสอบเซลล์ว่า 'สี' - น่าจะเป็นรหัสที่มีประสิทธิภาพมากที่สุด แต่บางทีอาจเป็นเพียงเล็กน้อยเท่านั้นเมื่อฉันคาดเดาว่ามันถูกครอบงำโดยแคชเวลาพลาดและขึ้นอยู่กับความซับซ้อนของเซลล์ของคุณ พวกเขาคือ.

อีกทางเลือกหนึ่งคือการแสดงผล FOV เป็นบิตแมปนอกหน้าจอจากนั้นอ่านค่าพิกเซลสำหรับแต่ละวัตถุของคุณ คุณไม่สามารถ 'unmix paint' ได้ แต่ด้วยจำนวนวัตถุที่ จำกัด และโดยการเลือกสีของคุณอย่างระมัดระวังคุณสามารถอนุมานได้ว่าใครเป็นใคร วิธีนี้คล้ายกับจำนวนเกมที่ผู้เล่นคลิก - พวกเขาวาดฉากนอกจอโดยใช้สีทึบสำหรับพื้นที่ฮิต GPU มีความรวดเร็วในการเติมสามเหลี่ยม ...


+1 ขอบคุณสำหรับสิ่งนี้ฉันได้ใช้กล่องขอบเขตสำหรับสามเหลี่ยมเพื่อเลือกเซลล์ที่เหมาะสมอย่างรวดเร็วและใช้การทดสอบแบบจุดในสามเหลี่ยมเพื่อกำหนดว่าสมาชิกของเซลล์เหล่านั้นอยู่ในเขตการดู :)
Ray Dey

3

วิธีแก้ปัญหาที่ยอมรับในซอฟต์แวร์ renderers (ซึ่งต้องทำอัลกอริทึมที่แน่นอนนี้ทุกครั้งที่พวกเขา rasterize รูปสามเหลี่ยม) คือฉันเชื่อว่าการสแกนสามเหลี่ยมพิกเซลหนึ่งแถวของพิกเซลในเวลา ขอบซ้ายและขวาของแต่ละแถวและคำนวณโดยการเดินลงด้านข้างของสามเหลี่ยมโดยใช้Bresenhamจากนั้นคุณกรอกข้อมูลในแถวระหว่างพวกเขา

ฉันคัดสรรรายละเอียดมากมายแต่นั่นเป็นแนวคิดพื้นฐาน หากคุณค้นหา "ซอฟต์แวร์เรนเดอร์" และ "แรสเตอร์สามเหลี่ยม" คุณอาจพบรายละเอียดเพิ่มเติม นี่เป็นปัญหาที่แก้ไขได้ดี การ์ดกราฟิกของคุณกำลังทำเฟรมเป็นล้าน ๆ ครั้ง

ถ้าคุณต้องการวิธีแก้ปัญหาเฉพาะโร๊คไลค์เพิ่มเติมนี่คือวิธีที่ฉันใช้ FOV ในเหมือง ดูเหมือนว่าจะทำงานได้อย่างรวดเร็ว โดยพื้นฐานแล้วมันคือลูกล้อเงาธรรมดาทำงานกับอ็อกเทนครั้งละหนึ่งตัวสแกนออกจากเครื่องเล่น


1
นั่นเป็นวิธีที่ยอดเยี่ยม
Notabene

2

ฉันกำลังใช้อัลกอริทึม scanline เพื่อแก้ไขปัญหาเดียวกัน ฉันเริ่มต้นด้วยการจัดเรียงสามเหลี่ยมสามจุดตามความสูงของพวกเขา โดยทั่วไปฉันจะตรวจสอบว่ามีสองขอบอยู่ทางซ้ายหรือทางขวา สำหรับด้านที่มีขอบสองด้านคุณจะต้องทำเครื่องหมายที่แถวที่คุณเปลี่ยนซึ่งขอบจะกำหนดขอบเขต สำหรับด้านที่มีขอบเดียวคุณก็สามารถใช้มันได้เสมอ

ดังนั้นสำหรับแต่ละแถวฉันรู้ว่าขอบสองเส้นกั้นเขตนั้นและฉันสามารถคำนวณขอบเขตบนและล่างในทิศทาง x ได้ มันฟังดูค่อนข้างซับซ้อน แต่มันรวมตัวกับโค้ดเพียงไม่กี่บรรทัด ตรวจสอบให้แน่ใจว่าคุณจัดการกับเคสพิเศษที่ขอบด้านหนึ่งอยู่ในแนวนอนอย่างสมบูรณ์!


2

วิธีการเกี่ยวกับการรักษาช่วงของคอลัมน์สำหรับแต่ละแถวที่อยู่ในรูปสามเหลี่ยม? สิ่งที่คุณสามารถทำได้คือการตั้งค่าคอลัมน์ min และ max สำหรับแต่ละแถวที่แต่ละจุดอยู่และที่ที่แต่ละบรรทัดสามเหลี่ยมข้ามเส้นแบ่งแถวแนวนอน

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

เอาท์พุท:

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

1

มีการทำงานของอัลกอริทึมในชุมชนโร๊คไลค์ที่เกี่ยวข้องกับ FOV / LOS / การให้แสงในโลกที่เป็นกระเบื้อง

บางทีคุณอาจพบบางสิ่งในหน้านี้ที่สามารถใช้เพื่อช่วยแก้ปัญหาของคุณ: http://roguebasin.roguelikedevelopment.org/index.php?title=Field_of_Vision

"อนุญาต FOV" โดยเฉพาะอาจเหมาะสมกับสถานการณ์ของคุณมากที่สุด

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.