我目前正在编写2D AI模拟,但是我不确定如何检查代理的位置是否在另一个代理的视野内。
目前,我的世界分区是简单的单元空间分区(网格)。我想使用三角形表示视野,但是如何计算与三角形相交的像元?
与此图片相似:
红色区域是我要计算的单元格,方法是检查三角形是否与这些单元格相交。
提前致谢。
编辑:
只是增加了混乱(甚至可能使其更简单)。每个像元都有一个最小值和最大值向量,其中最小值是左下角,最大值是右上角。
我目前正在编写2D AI模拟,但是我不确定如何检查代理的位置是否在另一个代理的视野内。
目前,我的世界分区是简单的单元空间分区(网格)。我想使用三角形表示视野,但是如何计算与三角形相交的像元?
与此图片相似:
红色区域是我要计算的单元格,方法是检查三角形是否与这些单元格相交。
提前致谢。
编辑:
只是增加了混乱(甚至可能使其更简单)。每个像元都有一个最小值和最大值向量,其中最小值是左下角,最大值是右上角。
Answers:
计算您的fov三角形的三个角,将它们旋转为面向正确的方向,然后执行以下操作之一:
1)对所有潜在目标进行三角测试
2)计算该三角形的边界框,并对该边界框内单元中的所有潜在目标进行三角测试-这将是非常简单的调试代码
一种类似的方法是使用四叉树而不是网格,并在其上进行交点。如果O(1)切片访问使您加快了速度,则只需测试fov三角形边界内的所有像元以获取三角形内,就应该如此之快以至于可以立即执行。当您在寻找其他选项时,我认为它不是,并且O(1)实际上在您破坏高速缓存时承担了巨大的高速缓存未命中成本。您当然可以查看预取说明,以注释您的边界框游标...
3)“栅格化”这个三角形并检查它“绘制”的单元格-可能是最有效的代码,但可能只是微不足道,因为我推测其所有内容都由缓存未命中时间决定,并且取决于您的单元格的复杂程度和占用程度他们是。
另一种方法是将FOV渲染到屏幕外的位图,然后读取每个对象的像素值。您不能“取消混合油漆”,而只能使用数量有限的对象,并且通过仔细选择油漆可以推断出谁看见了谁。这种方法类似于有多少游戏可以算出用户点击的内容-他们使用纯色为点击区域在屏幕外绘制场景。GPU的三角形填充速度非常快...
我认为,软件渲染器的规范解决方案(每次渲染三角形时必须执行此精确算法)是一次扫描三角形一行像素。每行的左边缘和右边缘,通过使用Bresenham沿着三角形的边沿走而计算,然后在它们之间填充行。
我在掩盖许多细节,但这是基本思想。如果您四处寻找“软件渲染”和“三角栅格化”,则可能会发现更多详细信息。这是一个很好解决的问题。您的图形卡在一帧中执行了数百万次。
如果您想要一个更像roguelike的解决方案,这就是我在我的方法中实现FOV的方法。它似乎工作很快。从本质上讲,它是一个简单的阴影投射器,一次处理八分圆,从播放器向外扫描。
如何为三角形中的每一行保持一定范围的列?您可以做的是为每个点的位置和每条三角形线与水平行分隔线交叉的每一行设置最小值和最大值列。
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
在基于流派的世界中,在roguelike社区中完成了一些处理FOV / LOS /照明的算法工作。
也许您可以在此页面上找到一些可用来帮助解决问题的内容: http //roguebasin.roguelikedevelopment.org/index.php?title=Field_of_Vision
具体来说,“允许的FOV”可能最适合您的情况。