# unity：完成A星寻路算法的基本实现

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using SGF;
using UnityEngine.Tilemaps;

//测试A星寻路算法

public class TestAstar : MonoBehaviour
{
public Tilemap tilemap;
public Tile tile0;
public Tile tile1;
public Tile tile2;
public List<AStar> PathList = new List<AStar>();
public List<GameObject> path = new List<GameObject>();
// Start is called before the first frame update
void Start()
{
AStarManeger aStarManeger = new AStarManeger();
aStarManeger.InitAStarManeger(new Vector2(20,20),tilemap,tile0);
PathList = aStarManeger.FindPath(aStarManeger.Nodes[5,2], aStarManeger.Nodes[12, 15]);

tilemap.SetTile(new Vector3Int(5,2,0), tile1);
tilemap.SetTile(new Vector3Int(12, 15, 0), tile1);
for (int i=0;i<PathList.Count;i++)
{
tilemap.SetTile(new Vector3Int((int)PathList[i].Postion.x, (int)PathList[i].Postion.y, 1), tile2);
Debug.Log(PathList[i].Postion);
}

}

// Update is called once per frame
void Update()
{

}
}
//格子类型
public enum E_Node_Type
{
Walk,
Stop,
}

public class AStar
{
public Vector2 Postion;
public float f;
public float g;
public float h;
public AStar parent;
public E_Node_Type Type;
public AStar(Vector2 postion,E_Node_Type type)
{
Postion = postion;
Type = type;
}
}

public class AStarManeger : Singleton<AStarManeger>
{
public AStar[,] Nodes;//所有寻路格子
private List<AStar> OpenList = new List<AStar>();
private List<AStar> CloseList = new List<AStar>();

public Vector2 MapSize;

public void InitAStarManeger(Vector2 mapSize, Tilemap tilemap, Tile tile)
{
//生成地图，生成寻路格子
MapSize = mapSize;
Nodes = new AStar[(int)mapSize.x, (int)mapSize.y];
for (int i = 0; i < mapSize.x; i++)
{
for (int j = 0; j < mapSize.y; j++)
{
Nodes[i, j] = new AStar(new Vector2(i, j), Random.Range(0, 10) < 4 ? E_Node_Type.Stop : E_Node_Type.Walk) { f = 10000, g = 0, h = 0, parent = null };
if ((i == 5 && j == 2) || (i == 12 && j == 15))
{
Nodes[i, j].Type = E_Node_Type.Walk;
}
if (Nodes[i, j].Type == E_Node_Type.Walk)
{
tilemap.SetTile(new Vector3Int(i, j, 0), tile);
}
else
{
tilemap.SetTile(new Vector3Int(i, j, 0), null);
}
}
}
}

public List<AStar> FindPath(AStar start, AStar end)
{
OpenList.Clear();
CloseList.Clear();
//判断起点和终点是否是阻挡格子
if (start.Type == E_Node_Type.Stop|| end.Type == E_Node_Type.Stop)
{
return null;
}
//判断起点和终点是否在地图范围内
if (0 < start.Postion.x && start.Postion.x < MapSize.x && 0 < start.Postion.y && start.Postion.y < MapSize.y)
{

}
else
{
this.LogWarning("起点不在地图范围内，MapSize:{0}，{1}    Start：{2}，{3}", MapSize.x, MapSize.y, start.Postion.x, start.Postion.y);
return null;
}
if (0 < end.Postion.x && end.Postion.x < MapSize.x && 0 < end.Postion.y && end.Postion.y < MapSize.y)
{

}
else
{
this.LogWarning("终点不在地图范围内，MapSize:{0}，{1}    Start：{2}，{3}", MapSize.x, MapSize.y, start.Postion.x, start.Postion.y);
return null;
}
//添加起点到开启列表
var point = start;
//寻路循环
while (point.Postion != end.Postion)
{
//添加当前格子四周的格子到开启列表
AroundToOpenList4(point,start, end);

//死路判断（开启列表是否为空）
if (OpenList.Count == 0)
{
Debug.Log("死路");
break;
}

//将开启列表排序并将f最小的格子放入关闭列表
OpenList.Sort(SortOpenList);
var min = OpenList[0];
point = min;           //min.parent = point;//错误代码，二次赋值父物体，造成回溯时错误，出现多余路径

//寻路完成判断
if (min == end)
{
break;
}
OpenList.RemoveAt(0);
}

//回溯最终路径
var aStar = end;
var PathList = new List<AStar>();
while (aStar.parent != null)
{
aStar = aStar.parent;
}
PathList.Reverse();
return PathList;
}

int SortOpenList(AStar a, AStar b)
{
if (a.f >= b.f)
{
return 1;
}
else
{
return -1;
}
}

void AddToOpenList(int i, int j, float g, AStar father,AStar start, AStar end)
{
//判断是否在地图外
if (i < 0 || j < 0 || i >= MapSize.x || j >= MapSize.y)
{
return;
}

//判断是否阻挡
if (Nodes[i, j].Type == E_Node_Type.Stop)
{
return;
}
//判断是否是起点
if (Nodes[i,j]==start)
{
return;
}

//判断是否在开启列表和关闭列表
if (OpenList.Contains(Nodes[i, j]) || CloseList.Contains(Nodes[i, j]))
{
return;
}
Nodes[i, j].parent = father;
Nodes[i, j].g = g;
Nodes[i, j].h = Mathf.Abs((Nodes[i, j].Postion - end.Postion).x) + Mathf.Abs((Nodes[i, j].Postion - end.Postion).y);
Nodes[i, j].f = Nodes[i, j].g + Nodes[i, j].h;
}

//添加周围8格到开启列表
void AroundToOpenList8(AStar point,AStar start,AStar end)
{
AddToOpenList((int)point.Postion.x - 1, (int)point.Postion.y - 1, 1.4f, point, start, end);
AddToOpenList((int)point.Postion.x - 1, (int)point.Postion.y, 1, point, start, end);
AddToOpenList((int)point.Postion.x - 1, (int)point.Postion.y + 1, 1.4f, point, start, end);
AddToOpenList((int)point.Postion.x, (int)point.Postion.y - 1, 1, point, start, end);
AddToOpenList((int)point.Postion.x, (int)point.Postion.y + 1, 1, point, start, end);
AddToOpenList((int)point.Postion.x + 1, (int)point.Postion.y - 1, 1.4f, point, start, end);
AddToOpenList((int)point.Postion.x + 1, (int)point.Postion.y, 1, point, start, end);
AddToOpenList((int)point.Postion.x + 1, (int)point.Postion.y + 1, 1.4f, point, start, end);
}

//添加周围4格到开启列表
void AroundToOpenList4(AStar point,AStar start,AStar end)
{
AddToOpenList((int)point.Postion.x - 1, (int)point.Postion.y, 1, point, start, end);
AddToOpenList((int)point.Postion.x, (int)point.Postion.y - 1, 1, point, start, end);
AddToOpenList((int)point.Postion.x, (int)point.Postion.y + 1, 1, point, start, end);
AddToOpenList((int)point.Postion.x + 1, (int)point.Postion.y, 1, point, start, end);
}

}

• 死路
• 只能从x,y方向走
• 可以从斜向方向走
•

1. 一开始，在初始化寻路格子时将f的值设定为了0，并且将起点格子加入了开启列表，而起点格子的f在之后不会再改变，导致在对开启列表排序时起点排在了最小位置，导致对起点的父物体进行了赋值，最终在回溯时形成了死循环，导致内存溢出。解决方法是，不要把起点加入开启列表，或者将起点的f值设的尽量大；
2. 对开启列表中的f最小的格子的父物体进行了二次赋值，造成回溯时错误，出现多余路径；（这里是对A星寻路的最终路径回溯的原理没有完全理解导致的）。
posted @ 2021-01-09 00:11  墨色-星辰  阅读(408)  评论(0编辑  收藏  举报