AStar アルゴリズム
A* アルゴリズム、A* (A-Star) アルゴリズムは、静的な道路網で最短経路を解決するための最も効果的な直接探索方法であり、多くの探索問題を解決するための効果的なアルゴリズムでもあります。アルゴリズムの距離推定値が実際の値に近いほど、最終的な検索は高速になります。
注: AStar クラスは呼び出しメソッドのみを提供するシングルトン クラスとして使用する必要があり、ノードの初期化はノード クラスで行う必要があります。
アルゴリズムのアイデア:
1. ノードを維持するための 2 つのリスト、openList と closeList を作成します。openList は、
保存されているが検査されていないすべてのノードを格納するために使用されます。closeList は、
訪問したノードを格納するために使用されます
。2. openList に未検査のノードがある限り、現在のノードとしてコストが最も低いノード。
3. 現在のノードがターゲット ノードである場合、適切なパスが見つかったことを意味します。そうでない場合は、ノードの理由を開始します。
4. closeList リストにないノードのコストを再度維持し、openList に入れます。
5 は 2->4 のプロセスを続けます。
举例理解
まず、私たちは知る必要があります評価関数: F(N)=G(N) + H(N)AStar アルゴリズムは、ノードのコストを常に維持して、最小コストのパスを取得します.
その中で: G はからのパスです.初期状態到着状態n最小コスト
H は状態n到着目標状態の最小推定コスト
正方形ノードを例にとると、
単位正方形の辺の長さは 1 で、対角コストは √2 ≒ 1.1414 ですが、
コスト × 10 を計算するために、移動に必要なコストは水平方向と垂直方向は 10, 対角方向に移動するために必要なコストは 14.
正方形ノードのコストを次の形式でラベル付けします.
では、A* アルゴリズムを使用してパスを見つけるとどうなりますか? (0,0,) から (2,2)?
最初の検索:
最初は、openList にノード (0,0) が 1 つしかないため、(0,0) の隣接ノードを直接検索します。赤い部分が (0,0) の隣接ノードです (正方形は 8 つの隣接ノードを持つ必要がありますが、斜め方向のノードは隣接ノードではないと規定することもできますが、これは一般的には行われません。)
この 3 つの隣接ノードは、最初の closeList に存在しないため、これら 3 つを配置します。近隣ノードを openList に追加します (ノードを保存する場合、保存されたノードのコストは考慮されません。現時点でコストが高いノードは、数回の検索後に openList 内の最小のノードになる可能性があるため、アルゴリズムは「悔い改めることができます。 " 通った 道 )
2回目の検索:
このとき、openListには3つのノードがあり、(1,1)のコストが最も小さいこともわかるので、(1,1)ノードとその隣接ノードを取り出し
ます(1,1) のうち: ( 0,0)、(1,0)、(2,0)、(0,1)、(2,1)、(0,2)、(1,2)、 (2,2)
(0,0) ノードは既に closeList にあり、(1,0) と (0,1) は openList にあるため、これら 3 つのノードは再計算されません (後退も意図的な迂回路もありません)。
実際、このステップを検索すると、(2,2) ノードが見つかったときに結果が直接返されます。A* アルゴリズムで見つかったノード コストは最小ですが、パスは必ずしも一意ではありません(上記の正方形のノードで、対角線方向のノードが隣接ノードと見なされない場合、(0,0) から (2,2) に至る 2 つのパスが存在する可能性があります)
正方形ノードで F を計算すると、
曼哈顿方法
Vector2
dist =new Vector2Int(Mathf.Abs((int)pos.x - (int)other.pos.x), Mathf.Abs((int)pos.x- ( int) other .pos.y));
int 最低 =Mathf.Min(dist.x, 距離.y);
int 最高 =Mathf.Max(dist.x, 距離.y);
int horizontalMoveRequired =最高 - 最低;
最低を返す *14 + horizontalMoveRequired *10;
これは、直角三角形のパスの場合、最初に短い直角辺を持つ直角二等辺三角形の対角線をたどり、次に 2 つの直角辺の間の距離を歩くことができることを意味します。
コアコード
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public static class AStar{
public static List<NodeBase> FindPath(NodeBase startNode,NodeBase endNode){
//打开列表和关闭列表
List<NodeBase> _openList = new List<NodeBase>(){
startNode}; //起始需要在打开列表中填入开始节点
List<NodeBase> _closeList =new List<NodeBase>();
//初始将所有节点的G代价调为最大,否侧起始节点到终点的花费将会因为是0而无法计算。
foreach(var node in GridManger.Instance.Tiles.Values) node.G = int.MaxValue;
startNode.G = 0;
startNode.H = startNode.GetDistance(endNode.coords);
while(_openList.Count > 0){
NodeBase currentNodeBase = GetLowestFCostNode(_openList);
if(currentNodeBase == endNode) return CalculatePath(endNode);
_openList.Remove(currentNodeBase);
_closeList.Add(currentNodeBase);
//遍历邻居
foreach(NodeBase n in currentNodeBase.Neighbours){
//如果这个节点已经存在于关闭列表或者打开列表中就直接跳过
if(_closeList.Contains(n) || _openList.Contains(n)) continue;
//暂时的G代价
var tantativeGCost = currentNodeBase.G + currentNodeBase.GetDistance(n.coords);
if(tantativeGCost < n.G){
n.father =currentNodeBase;
n.G= tantativeGCost;
n.H= n.GetDistance(endNode.coords);
_openList.Add(n);
}
}
}
return null; //没有路径
}
//得到Fcost最低的一个点
private static NodeBase GetLowestFCostNode(List<NodeBase> nodes){
NodeBase lowestFcostNode = nodes[0];
foreach(Node n in nodes){
if(n.F < lowestFcostNode.F) lowestFcostNode = n;
}
return lowestFcostNode;
}
//计算结果路径路径
private static List<NodeBase> CalculatePath(NodeBase node){
List<NodeBase> nodeBases = new List<NodeBase>();
while(node != null){
nodeBases.Add(node);
node=node.father;
}
nodeBases.Reverse();
return nodeBases;
}
}