격자의 어떤 셀이 주어진 삼각형과 교차하는지 확인하는 방법은 무엇입니까?


10

현재 2D AI 시뮬레이션을 작성하고 있지만 에이전트의 위치가 다른 사람의 시야 내에 있는지 확인하는 방법을 완전히 확신하지는 못합니다.

현재 세계 분할은 단순한 셀 공간 분할 (그리드)입니다. 시야를 나타 내기 위해 삼각형을 사용하고 싶지만 삼각형과 교차하는 셀을 어떻게 계산할 수 있습니까?

이 사진과 유사합니다 : 여기에 이미지 설명을 입력하십시오

빨간색 영역은 삼각형이 해당 셀과 교차하는지 여부를 확인하여 계산하려는 셀입니다.

미리 감사드립니다.

편집하다:

혼란을 더하기 위해 (또는 아마도 더 쉽게 만들 수도 있습니다). 각 셀에는 최소 및 최대 벡터가 있으며 여기서 최소값은 왼쪽 하단 모서리이고 최대 값은 오른쪽 상단 모서리입니다.


셀을 삼각형으로 나누고 삼각형 삼각형을 테스트 할 수 없습니까?
공산주의 오리

셀은 물리적 다각형이 아니라 공간 표현 일 뿐이며 배열의 O (1) 액세스 시간을 활용합니다. 에이전트 주위에 주변 원이 있으면 셀을 근사화하기 위해 원의 반지름을 사용하여 AABB를 만들고 교차점을 쉽게 찾을 수 있습니다. 여기서 문제는 내 앞에있는 세포 만 원한다는 것입니다. 도움이 될 기하학적 방정식이 있다고 확신합니다. 저는 제 인생에 대해 전혀 생각할 수 없습니다.
Ray Dey

여기에 답이 마음에 들지 않습니다. 그러나이 질문에는 정말 좋은 답변이 있습니다 : gamedev.stackexchange.com/q/81267/63053
Andrew

답변:


6

fov 삼각형의 세 모서리를 계산하고 올바른 방향을 향하도록 회전 한 후 다음 중 하나를 수행하십시오.

1) 모든 잠재적 목표에 대해 포인트-인-삼각형 테스트 수행

2)이 삼각형의 경계 상자를 계산 하고이 경계 상자의 셀에있는 모든 잠재적 대상에 대해 삼각 점 테스트를 수행하십시오-이것은 디버깅하기 매우 간단한 코드입니다

비슷한 접근 방식은 그리드 대신 쿼드 트리를 사용하고 그에 대한 교차점을 수행하는 것입니다. O (1) 타일 액세스 속도가 빨라지면 fov 삼각형의 경계에있는 모든 셀을 삼각 관계에 대해 테스트하는 것만으로도 매우 빨라야합니다. 다른 옵션을 살펴볼 때, 나는 그 옵션이 아니라고 가정하고 캐시를 스 래시 할 때 O (1)이 실제로 큰 캐시 미스 비용을 소비한다고 가정합니다. 물론 경계 상자 보행에 주석을 달기 위해 프리 페치 지침을 볼 수 있습니다 ...

3)이 삼각형을 '래스터 화'하고 셀이 '페인트'되어 있는지 확인하십시오-아마도 가장 효율적인 코드 일 것입니다.하지만 캐시 미스 타임이 지배적이며 아마도 셀이 얼마나 복잡한 지, 어떻게 점령되는지에 따라 거의 한계가 있습니다. 그들은.

다른 방법은 FOV를 오프 스크린 비트 맵으로 렌더링 한 다음 각 객체의 픽셀 값을 읽는 것입니다. 페인트 혼합을 해제 할 수는 없지만 개체 수가 제한되어 있고 페인트를 신중하게 선택하면 누가 누가 보았는지 추론 할 수 있습니다. 이 방법은 사용자가 클릭 한 것을 해결하는 게임의 수와 비슷합니다. 히트 영역에 단색을 사용하여 화면을 화면 밖으로 그립니다. GPU는 삼각형 채우기 속도가 매우 빠릅니다 ...


이에 대한 한 덕분에, 나는 적절한 셀을 선택 신속에있는 삼각형의 경계 상자를 사용했는데 그 세포의 구성원이 시야 :) 내에있는 결정하는 포인트 인 삼각형 테스트를 사용했다
Ray Dey

3

소프트웨어 렌더러의 정식 솔루션 (삼각형을 래스터 화 할 때 마다이 정확한 알고리즘을 수행해야 함)은 삼각형을 한 번에 한 행의 픽셀로 스캔하는 것입니다. 각 행의 왼쪽과 오른쪽 가장자리는 Bresenham을 사용하여 삼각형의 측면을 걷고 계산 한 다음 그 사이에 행을 채 웁니다.

나는 많은 세부 사항에 대해 글을 쓰고 있지만 이것이 기본 아이디어입니다. "소프트웨어 렌더"와 "삼각형 래스터 화"를 찾으면 더 자세한 정보를 얻을 수 있습니다. 이것은 잘 해결 된 문제입니다. 그래픽 카드가 프레임 수백만 번 수행하고 있습니다.

좀 더 불량한 솔루션을 원한다면 여기 FOV를 구현 한 방법이 있습니다. 꽤 빨리 작동하는 것 같습니다. 본질적으로 간단한 그림자 캐스터로 한 번에 옥타 트로 작업하고 플레이어에서 바깥쪽으로 스캔합니다.


1
꽤 멋진 접근법입니다.
Notabene

2

똑같은 문제를 해결하기 위해 변형 된 스캔 라인 알고리즘을 사용하고 있습니다. 세 개의 삼각형 점을 높이별로 정렬하여 시작했습니다. 그런 다음 기본적으로 두 모서리가 왼쪽에 있는지 오른쪽에 있는지 확인합니다. 모서리가 두 개인 변의 경우 행을 구분하는 모서리를 변경하는 위치에 모서리를 표시해야합니다. 한쪽 가장자리가있는면에는 항상 사용할 수 있습니다.

따라서 각 행에 대해 어느 두 가장자리가 그것을 구분하는지 알고 x 방향에서 상한과 하한을 계산할 수 있습니다. 상당히 복잡해 보이지만 몇 줄의 코드로 요약됩니다. 한쪽 가장자리가 완전히 수평 인 특수 케이스를 다루십시오!


2

삼각형에있는 각 행의 열 범위를 유지하는 것은 어떻습니까? 당신이 할 수있는 일은 각 점이있는 각 행과 각 삼각형 선이 가로 행 구분선을 가로 지르는 각 행에 대해 최소 및 최대 열을 설정하는 것입니다.

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

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.