修复 C# 中的 A* 实现
Fixing A* implementation in C#
所以我在 Unity C# 中实现了一个 A* 算法来做一些测试,以便构建一个基于 2D 的游戏。我知道有几个组件可以帮助你解决这个问题,但我想尝试一下自己来迎接挑战。
我基本上已经阅读了 A* 的行为方式并将行为转化为代码。它几乎可以工作。但是在某些情况下,相邻的图块具有完全相同的分数(曼哈顿距离 + 与原点的距离),您最终会得到一条通往目的地但不是最短的路径。正如你在图片中看到的,那两个相邻的方块有相同的分数,但我在那个点随机拿了一个......(在下图中,起点是猫,红叉是终点.绿色半透明文件为计算路径)
我在想,既然没有太多的瓦片,我可以从最初的4个相邻瓦片计算出4条不同的路径,将有效的存储在一个数组中,然后基本上只使用最短的,但也许开销会太大,还有其他解决方案吗?
要计算距离,我使用的是基本计算:
private int CalculateManhattanDistance(int x1, int x2, int y1, int y2)
{
return Mathf.Abs(x1 - x2) + Mathf.Abs(y1 - y2);
}
我建议根据您的代码检查 A* e.g. on Wikipedia 的伪代码。
基于该伪代码,您将像这样实现算法数据,我怀疑您已经简化了其中的一些:
var closedSet = new HashSet<GraphNode>();
var openSet = new List<GraphNode>{startNode};
var cameFrom = new Dictionary<GraphNode, GraphNode>();
var gScore = new Dictionary<GraphNode, double>();
var fScore = new Dictionary<GraphNode, double>();
当算法对启发式算法做出错误的第一选择时,就像在这个测试用例中一样,它最初会评估错误方向的移动。
但这应该不是问题:
- Select 最低的开放节点
fScore
- 评估所有可达的邻居(例如,在您的示例的第一次迭代中包括起始节点 左侧 的节点)
- 用实际距离更新
gScore
(通过 cameFrom)
- 用实际距离加上到目标的估计(例如曼哈顿)距离更新
fScore
- 将评估的节点从
openSet
移动到 closedSet
这意味着 "wrong" 路径上的节点将计算增加的实际距离和预期距离,算法开始选择其他 open
节点,例如来自第一次迭代的 "right" 节点。
也许这有助于您理解@Zdeněk Jelínek 和@Peter Wishart 所指出的内容。 openSet(也叫frontier),通常是一个PriorityQueue。队列的节点根据它们的优先级排序。节点的优先级计算为到目前为止的成本(您的情况下的步数)和启发式距离(您的情况下的曼哈顿)的总和。因此,一旦 A* 到达优先级为 11 的节点,它将停止探索该路径并继续探索其他路径(蓝色圆圈)
所以我在 Unity C# 中实现了一个 A* 算法来做一些测试,以便构建一个基于 2D 的游戏。我知道有几个组件可以帮助你解决这个问题,但我想尝试一下自己来迎接挑战。
我基本上已经阅读了 A* 的行为方式并将行为转化为代码。它几乎可以工作。但是在某些情况下,相邻的图块具有完全相同的分数(曼哈顿距离 + 与原点的距离),您最终会得到一条通往目的地但不是最短的路径。正如你在图片中看到的,那两个相邻的方块有相同的分数,但我在那个点随机拿了一个......(在下图中,起点是猫,红叉是终点.绿色半透明文件为计算路径)
我在想,既然没有太多的瓦片,我可以从最初的4个相邻瓦片计算出4条不同的路径,将有效的存储在一个数组中,然后基本上只使用最短的,但也许开销会太大,还有其他解决方案吗?
要计算距离,我使用的是基本计算:
private int CalculateManhattanDistance(int x1, int x2, int y1, int y2)
{
return Mathf.Abs(x1 - x2) + Mathf.Abs(y1 - y2);
}
我建议根据您的代码检查 A* e.g. on Wikipedia 的伪代码。
基于该伪代码,您将像这样实现算法数据,我怀疑您已经简化了其中的一些:
var closedSet = new HashSet<GraphNode>();
var openSet = new List<GraphNode>{startNode};
var cameFrom = new Dictionary<GraphNode, GraphNode>();
var gScore = new Dictionary<GraphNode, double>();
var fScore = new Dictionary<GraphNode, double>();
当算法对启发式算法做出错误的第一选择时,就像在这个测试用例中一样,它最初会评估错误方向的移动。 但这应该不是问题:
- Select 最低的开放节点
fScore
- 评估所有可达的邻居(例如,在您的示例的第一次迭代中包括起始节点 左侧 的节点)
- 用实际距离更新
gScore
(通过 cameFrom) - 用实际距离加上到目标的估计(例如曼哈顿)距离更新
fScore
- 将评估的节点从
openSet
移动到closedSet
这意味着 "wrong" 路径上的节点将计算增加的实际距离和预期距离,算法开始选择其他 open
节点,例如来自第一次迭代的 "right" 节点。
也许这有助于您理解@Zdeněk Jelínek 和@Peter Wishart 所指出的内容。 openSet(也叫frontier),通常是一个PriorityQueue。队列的节点根据它们的优先级排序。节点的优先级计算为到目前为止的成本(您的情况下的步数)和启发式距离(您的情况下的曼哈顿)的总和。因此,一旦 A* 到达优先级为 11 的节点,它将停止探索该路径并继续探索其他路径(蓝色圆圈)