グリッド内のどのセルが特定の三角形と交差するかを判断するにはどうすればよいですか?


10

現在2D AIシミュレーションを作成していますが、エージェントの位置が別の視野内にあるかどうかを確認する方法が完全にわかりません。

現在、私の世界のパーティション分割は、単純なセル空間パーティション分割(グリッド)です。三角形を使用して視野を表現したいのですが、三角形と交差するセルを計算するにはどうすればよいですか?

この写真のように: ここに画像の説明を入力してください

赤い領域は、三角形がそれらのセルと交差するかどうかをチェックすることによって計算したいセルです。

前もって感謝します。

編集:

混乱を増すためだけに(あるいはおそらくそれをさらに簡単にするために)。各セルには最小ベクトルと最大ベクトルがあり、最小は左下隅、最大は右上隅です。


セルを三角形に分割して、三角形と三角形をテストできませんか?
共産主義者のダック

セルは物理的なポリゴンではなく、単なる空間表現であり、配列のO(1)アクセス時間を利用します。エージェントの周りに近傍円がある場合、セルを近似するために、円の半径を使用してAABBを作成し、交差を簡単に見つけることができます。ここでの問題は、私の目の前にある細胞だけが欲しいということです。助けるためにいくつかの幾何学的方程式があると私は確信しています、私は私の人生のために何も考えることができません。
Ray Dey

ここの答えは好きではありません。ただし、この質問にはいくつかの本当に良い答えがあります。gamedev.stackexchange.com
Andrew

回答:


6

fov三角形の3つのコーナーを計算し、それらを正しい方向などに向くように回転させてから、次のいずれかを実行します。

1)すべての潜在的なターゲットに対して三角形のポイントテストを実行する

2)この三角形のバウンディングボックスを計算し、このバウンディングボックス内のセル内のすべての潜在的なターゲットに対してポイントイントライアングルテストを実行します。これは、デバッグが非常に簡単なコードになります

同様のアプローチは、グリッドではなく四分木を使用し、その上で交差を行うことです。O(1)タイルアクセスが高速化している場合、fov三角形の境界内のすべてのセルを三角形内でテストするだけで、瞬時に高速になるはずです。あなたが他のオプションを見ているように、私はそれを想定しておらず、キャッシュをスラッシングするときにO(1)が実際に大量のキャッシュミスコストを取っていると思います。もちろん、おそらくあなたのバウンディングボックスウォークに注釈を付けるためのプリフェッチ命令を見ることができます...

3)この三角形を「ラスタライズ」して、それが「ペイント」するセルを確認します-おそらく最も効率的なコードですが、おそらくキャッシュミスタイムによって支配されるすべてを推測し、セルの複雑度と占有率に依存しているため、ごくわずかです彼らです。

別の方法は、視野をオフスクリーンのビットマップにレンダリングし、各オブジェクトのピクセル値を読み取ることです。「ペイントをアンミックス」することはできませんが、オブジェクトの数が限られているため、ペイントを慎重に選択することで、誰が誰を見たのかを推測できます。このアプローチは、ユーザーがクリックしたものをいくつのゲームがうまくいくかに似ています-彼らはヒット領域に無地を使用して画面外にシーンを描画します。GPUは三角形の塗りつぶしが非常に高速です...


おかげで、私は三角形の境界ボックスを使用して適切なセルをすばやく選択し、三角形のポイントテストを使用して、これらのセルのどのメンバーが視野内にあるかを判断しました:)
レイデイ

3

ソフトウェアレンダラの標準的なソリューション(三角形をラスタライズするたびにこの正確なアルゴリズムを実行する必要があります)は、三角形を一度に1行のピクセルでスキャンすることです。各行の左端と右端は、ブレゼンハムを使用して三角形の辺を下って歩いて計算された後、それらの間の行を塗りつぶします。

私は多くの詳細についてつや消ししていますが、それが基本的な考え方です。「ソフトウェアレンダー」と「三角形のラスタライズ」を探していると、さらに詳細が見つかるはずです。これは十分に解決された問題です。あなたのグラフィックカードはこれを何百万回もフレームで実行しています。

あなたはより多くのローグライク固有のソリューションをしたい場合は、ここだ、私は鉱山でFOVを実装する方法。それはかなり迅速に動作するようです。これは基本的にシンプルなシャドウキャスターで、一度にオクタントを操作し、プレーヤーから外側に向かってスキャンします。


1
それはかなりクールなアプローチです。
Notabene、2011年

2

まったく同じ問題を解決するために、スキャンラインアルゴリズムのバリエーションを使用しています。まず、三角形の3つのポイントを高さで並べ替えました。次に、基本的に2つのエッジが左側にあるか右側にあるかを確認します。2つのエッジのある側では、行を区切るエッジを変更する場所に行をマークする必要があります。片方の辺はいつでも使えます。

したがって、各行について、どの2つのエッジがそれを区切っているかがわかり、x方向の上限と下限を計算できます。かなり複雑に聞こえますが、数行のコードに要約されています。1つのエッジが完全に水平である特殊なケースを処理することを確認してください。


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.