如何让 Follow Ai 在基于环形 2D 网格的地图上工作
How to make a follow Ai work on a toroidal 2D grid based map
我有一些 NPC 在环绕的 2D 矩阵中,我必须使用环形地图让它们跟随敌方派系 NPC。
我已经尝试了几个解决方案,但它们都给我带来了奇怪的行为,只适用于网格的某个 x
或 y
值,然后它们没有跟随它们返回一个图块.
这就是我现在要决定的方式:
public Position GetNextStepTowards(Position origin, Position target)
{
Position nextStep = new Position(0, 0);
float dx = MathF.Abs(target.X - origin.X);
float dy = MathF.Abs(target.Y - origin.Y);
if (dx > mapXSize / 2) nextStep.X = -1;
else if (dx < mapXSize / 2) nextStep.X = 1;
if (dy > mapYSize / 2) nextStep.Y = 1;
else if (dy < mapYSize / 2) nextStep.Y = -1;
return nextStep;
}
职位是:
public struct Position
{
public int X { get; set; }
public int Y { get; set; }
public Position(int x, int y)
{
this.X = x;
this.Y = y;
}
}
NPC 只能移动一个单元格(Moore's),因此移动矢量只能是 -1
和 1
之间的值。
提前感谢您的帮助!
所以过了一段时间,我想出了这个解决方案,在我看来有点笨拙:
public Position GetNextStepTowards(Position origin, Position target)
{
// Returned Position
Position nextStep = new Position(0, 0);
int dx = target.X - origin.X;
int dy = target.Y - origin.Y;
// Toroidal distance
if (dx > mapXSize / 2) dx = mapXSize - dx;
if (dy > mapYSize / 2) dy = mapXSize - dy;
// First verify whether the difference in positions is
// greater on the X or Y axis.
// Then check if the target is lower/higher/forwards/backwards
if (MathF.Pow(dx, 2) > MathF.Pow(dy, 2))
{
if (dx > 0) nextStep.X = 1;
else if (dx < 0) nextStep.X = -1;
}
else if (MathF.Pow(dy, 2) > MathF.Pow(dx, 2))
{
if (dy > 0) nextStep.Y = 1;
else if (dy < 0) nextStep.Y = -1;
}
// If the difference in the X and Y axis are the same,
// move diagonally
// use CompareTo do decide what direction in specific.
else if ((int)MathF.Pow(dx, 2) == (int)MathF.Pow(dy, 2))
{
nextStep.X = 1 * target.X.CompareTo(origin.X);
nextStep.Y = 1 * target.Y.CompareTo(origin.Y);
}
return nextStep;
}
如果我们考虑X-axis,有两种情况如下图所示:
在第一种情况(顶部)中,目标位于原点的右侧。在这种情况下,向右移动是直接的,向左移动是环形的。
在第二种情况(底部)中,目标位于原点的左侧。在这种情况下,向左移动是直接的,向右移动是环形的。
所以代码需要检查原点和目标的相对位置,然后适当地计算左右距离。较小的距离决定了 deltaX 的方向和大小。同样的逻辑适用于 deltaY.
那么如果deltaX和deltaY的大小相同,我们就沿着对角线移动。否则,我们向增量较大的方向移动。
private int ComputeDelta(int src, int dst, int mapSize)
{
int increasing, decreasing;
if (dst >= src)
{
increasing = dst - src; // increasing direction is direct
decreasing = (mapSize + src) - dst; // decreasing direction is toroidal
}
else
{
increasing = (mapSize + dst) - src; // increasing direction is toroidal
decreasing = src - dst; // decreasing direction is direct
}
if (increasing <= decreasing) { return increasing; }
else { return -decreasing; }
}
public Position GetNextStepTowards(Position origin, Position target)
{
Position nextStep = new Position(0, 0);
// compute the distances
int dx = ComputeDelta(origin.X, target.X, mapXSize);
int dy = ComputeDelta(origin.Y, target.Y, mapYSize);
// keep the dominant distance, and clear the other distance
// keep both if they're equal
if (dx*dx > dy*dy) { dy = 0; }
else if (dx*dx < dy*dy) { dx = 0; }
// normalize the distances so they are -1, 0, or 1
nextStep.X = dx.CompareTo(0);
nextStep.Y = dy.CompareTo(0);
return nextStep;
}
我有一些 NPC 在环绕的 2D 矩阵中,我必须使用环形地图让它们跟随敌方派系 NPC。
我已经尝试了几个解决方案,但它们都给我带来了奇怪的行为,只适用于网格的某个 x
或 y
值,然后它们没有跟随它们返回一个图块.
这就是我现在要决定的方式:
public Position GetNextStepTowards(Position origin, Position target)
{
Position nextStep = new Position(0, 0);
float dx = MathF.Abs(target.X - origin.X);
float dy = MathF.Abs(target.Y - origin.Y);
if (dx > mapXSize / 2) nextStep.X = -1;
else if (dx < mapXSize / 2) nextStep.X = 1;
if (dy > mapYSize / 2) nextStep.Y = 1;
else if (dy < mapYSize / 2) nextStep.Y = -1;
return nextStep;
}
职位是:
public struct Position
{
public int X { get; set; }
public int Y { get; set; }
public Position(int x, int y)
{
this.X = x;
this.Y = y;
}
}
NPC 只能移动一个单元格(Moore's),因此移动矢量只能是 -1
和 1
之间的值。
提前感谢您的帮助!
所以过了一段时间,我想出了这个解决方案,在我看来有点笨拙:
public Position GetNextStepTowards(Position origin, Position target)
{
// Returned Position
Position nextStep = new Position(0, 0);
int dx = target.X - origin.X;
int dy = target.Y - origin.Y;
// Toroidal distance
if (dx > mapXSize / 2) dx = mapXSize - dx;
if (dy > mapYSize / 2) dy = mapXSize - dy;
// First verify whether the difference in positions is
// greater on the X or Y axis.
// Then check if the target is lower/higher/forwards/backwards
if (MathF.Pow(dx, 2) > MathF.Pow(dy, 2))
{
if (dx > 0) nextStep.X = 1;
else if (dx < 0) nextStep.X = -1;
}
else if (MathF.Pow(dy, 2) > MathF.Pow(dx, 2))
{
if (dy > 0) nextStep.Y = 1;
else if (dy < 0) nextStep.Y = -1;
}
// If the difference in the X and Y axis are the same,
// move diagonally
// use CompareTo do decide what direction in specific.
else if ((int)MathF.Pow(dx, 2) == (int)MathF.Pow(dy, 2))
{
nextStep.X = 1 * target.X.CompareTo(origin.X);
nextStep.Y = 1 * target.Y.CompareTo(origin.Y);
}
return nextStep;
}
如果我们考虑X-axis,有两种情况如下图所示:
在第一种情况(顶部)中,目标位于原点的右侧。在这种情况下,向右移动是直接的,向左移动是环形的。
在第二种情况(底部)中,目标位于原点的左侧。在这种情况下,向左移动是直接的,向右移动是环形的。
所以代码需要检查原点和目标的相对位置,然后适当地计算左右距离。较小的距离决定了 deltaX 的方向和大小。同样的逻辑适用于 deltaY.
那么如果deltaX和deltaY的大小相同,我们就沿着对角线移动。否则,我们向增量较大的方向移动。
private int ComputeDelta(int src, int dst, int mapSize)
{
int increasing, decreasing;
if (dst >= src)
{
increasing = dst - src; // increasing direction is direct
decreasing = (mapSize + src) - dst; // decreasing direction is toroidal
}
else
{
increasing = (mapSize + dst) - src; // increasing direction is toroidal
decreasing = src - dst; // decreasing direction is direct
}
if (increasing <= decreasing) { return increasing; }
else { return -decreasing; }
}
public Position GetNextStepTowards(Position origin, Position target)
{
Position nextStep = new Position(0, 0);
// compute the distances
int dx = ComputeDelta(origin.X, target.X, mapXSize);
int dy = ComputeDelta(origin.Y, target.Y, mapYSize);
// keep the dominant distance, and clear the other distance
// keep both if they're equal
if (dx*dx > dy*dy) { dy = 0; }
else if (dx*dx < dy*dy) { dx = 0; }
// normalize the distances so they are -1, 0, or 1
nextStep.X = dx.CompareTo(0);
nextStep.Y = dy.CompareTo(0);
return nextStep;
}