开新坑了开新坑了~(这应该是个长期坑,现在的想法是把学过的或者有点难度的算法都做一个可视化)
正如标题所说的那样,现在我打算来做一个算法的可视化,除了帮助自己更好的理解算法之外,也帮助其它小伙伴们更好的理解。
那么话不多说,进入今天的主角,A*寻路算法。
A*寻路算法的核心原理说起来十分简单: 每一次都选择综合代价最小的格子,以此实现一定方向的遍历 算法的具体实现思路也很简单:
整个代码部分是我自己编写的,就一个脚本,注释十分详细,但同时也有很多问题(结构有点冗余,而且有个重载操作符的问题还没解决)
现在有想法是把它做成逐帧的,按一个键一跳的那种,看看之后优化一下
其他细节: 脚本导入之后自己建几个物体赋值一下就好了 计算距离使用的是街区距离(之后会开放自选) 你可能需要自己开启是否启用斜向遍历 地图大小和障碍物的比例自定义 斜向和横向移动花费自定义
注意一下自己控制别越界了(目标和起始点的最大值为地图最大值-1) 有一个Coord类和自己循环判空的问题还没有解决,目前是使用函数替代了操作符重载
C#脚本
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class QuadGenerate : MonoBehaviour
{
public GameObject MyQuadPrefab;
public GameObject MyMapHolder;
public GameObject RoadHolder;
public int MapSizeX;
public int MapSizeZ;
public int TargetX;
public int TargetZ;
public int StartX;
public int StartZ;
public float LineCost = 1;
public float ObliqueCost = 2;
public bool isOblique;
[Range(0, 1)] public float obsPercent;
private List<Coord> MyMaps;
private bool[,] mapObstacles;
private float QuadLength;
private Coord TargetQuad;
private Coord StartQuad;
void Awake()
{
MyMaps = new List<Coord>();
Vector3 QuadScale = MyQuadPrefab.transform.localScale;
QuadLength = QuadScale.x;
TargetQuad = new Coord(TargetX, TargetZ);
StartQuad = new Coord(StartX, StartZ);
mapObstacles = new bool[(int)MapSizeX, (int)MapSizeZ];
}
void Start()
{
Vector3 StartPos = new Vector3(-MapSizeX / 2, 0, -MapSizeZ / 2);
for(int i = 0; i < MapSizeX; i++)
{
for(int j = 0; j < MapSizeZ; j++)
{
Vector3 SetPos = new Vector3(i * QuadLength, 0, j * QuadLength);
GameObject Quad = Instantiate(MyQuadPrefab, StartPos + SetPos, Quaternion.Euler(90, 0, 0));
Quad.transform.SetParent(MyMapHolder.transform);
Coord coord = new Coord(i, j);
if (Random.Range(0f,1f) < obsPercent && !coord.Equal(StartQuad) && !coord.Equal(TargetQuad))
{
coord.setIsObs(true);
mapObstacles[i, j] = true;
}
if (coord.Equal(StartQuad) || coord.Equal(TargetQuad) || coord.getIsObs())
{
MeshRenderer meshRenderer = Quad.GetComponent<MeshRenderer>();
Material material = meshRenderer.material;
if (coord.Equal(StartQuad))
{
material.color = new Color(0, 0, 255);
}
else if(coord.Equal(TargetQuad))
{
material.color = new Color(255, 0, 0);
}
else
{
material.color = new Color(0, 255, 0);
}
meshRenderer.material = material;
}
MyMaps.Add(coord);
}
}
List<Coord> closeList = new List<Coord>();
if (isOblique)
{
closeList = FindRoadAStarisOblique();
}
else
{
closeList = FindRoadAStar();
}
Coord CurNode = closeList[closeList.Count - 1];
int count = 0;
if(CurNode!=null)
{
CurNode = CurNode.parent;
}
while (CurNode!=null && CurNode.parent != null)
{
Debug.Log("生成路径中:" + count + "当前瓦片的X和Y为:" + CurNode.x + " " + CurNode.z);
Vector3 SetPos = new Vector3(CurNode.x * QuadLength, 0.1f, CurNode.z * QuadLength);
GameObject Quad = Instantiate(MyQuadPrefab, StartPos + SetPos, Quaternion.Euler(90, 0, 0));
Quad.transform.SetParent(RoadHolder.transform);
MeshRenderer meshRenderer = Quad.GetComponent<MeshRenderer>();
Material material = meshRenderer.material;
material.color = new Color(0, 0, 0);
meshRenderer.material = material;
CurNode = CurNode.parent;
count++;
}
}
void Update()
{
}
List<Coord> FindRoadAStar()
{
List<Coord> openList = new List<Coord>();
List<Coord> closeList = new List<Coord>();
List<Coord> dir = new List<Coord>() { new Coord(0, 1), new Coord(1, 0), new Coord(-1, 0), new Coord(0, -1) };
StartQuad.setCost(CalcuCost(StartQuad));
openList.Add(StartQuad);
while (openList.Count != 0)
{
Coord supCoord = findFmin(openList);
closeList.Add(supCoord);
if (supCoord.Equal(TargetQuad))
{
Debug.Log("找到了终点");
break;
}
for (int i = 0; i < dir.Count; i++)
{
int newx = supCoord.x + dir[i].x;
int newz = supCoord.z + dir[i].z;
if (newx >= 0 && newz >= 0 && newx < MapSizeX && newz < MapSizeZ)
{
if (!mapObstacles[newx, newz] && !InList(new Coord(newx, newz), closeList))
{
openList.Add(new Coord(newx, newz, false, CalcuCost(new Coord(newx, newz)), supCoord));
}
}
}
Debug.Log("一直在循环");
}
return closeList;
}
List<Coord> FindRoadAStarisOblique()
{
List<Coord> openList = new List<Coord>();
List<Coord> closeList = new List<Coord>();
List<Coord> dir1 = new List<Coord>() { new Coord(0, 1), new Coord(1, 0), new Coord(-1, 0), new Coord(0, -1) };
List<Coord> dir2 = new List<Coord>() { new Coord(1, 1), new Coord(-1, -1), new Coord(1, -1), new Coord(-1, 1) };
StartQuad.setCost(CalcuCost(StartQuad));
openList.Add(StartQuad);
while(openList.Count!=0)
{
Coord supCoord = findFmin(openList);
closeList.Add(supCoord);
if (supCoord.Equal(TargetQuad))
{
Debug.Log("找到了终点");
break;
}
for (int i=0;i<dir1.Count;i++)
{
int newx = supCoord.x + dir1[i].x;
int newz = supCoord.z + dir1[i].z;
Coord nextCoord = new Coord(newx, newz);
if (newx >= 0 && newz >= 0 && newx < MapSizeX && newz < MapSizeZ)
{
if (!mapObstacles[newx, newz] && !InList(nextCoord, closeList))
{
if (!InList(nextCoord, openList))
{
float Gcost = supCoord.Gcost + LineCost;
openList.Add(new Coord(newx, newz, false, CalcuCost(nextCoord), supCoord, Gcost));
}
else
{
float Gcost = supCoord.Gcost + LineCost;
checkGcost(nextCoord, Gcost,openList,supCoord);
}
}
}
}
for (int i = 0; i < dir2.Count; i++)
{
int newx = supCoord.x + dir2[i].x;
int newz = supCoord.z + dir2[i].z;
Coord nextCoord = new Coord(newx, newz);
if (newx >= 0 && newz >= 0 && newx < MapSizeX && newz < MapSizeZ)
{
if (!mapObstacles[newx, newz] && !InList(nextCoord, closeList))
{
if (!InList(nextCoord, openList))
{
float Gcost = supCoord.Gcost + ObliqueCost;
openList.Add(new Coord(newx, newz, false, CalcuCost(nextCoord), supCoord, Gcost));
}
else
{
float Gcost = supCoord.Gcost + ObliqueCost;
checkGcost(nextCoord, Gcost, openList, supCoord);
}
}
}
}
}
return closeList;
}
void checkGcost(Coord nextCoord,float Gcost,List<Coord> List,Coord parentCoord)
{
for (int i = 0; i < List.Count; i++)
{
if (nextCoord.Equal(List[i]))
{
if(Gcost<List[i].Gcost)
{
List[i].setGCost(Gcost);
List[i].setParent(parentCoord);
}
}
}
}
Coord findFmin(List<Coord> openList)
{
openList.Sort((x, y) => { return x.RealCost.CompareTo(y.RealCost); });
Coord ans = openList[0];
openList.RemoveAt(0);
return ans;
}
int CalcuCost(Coord myQuad)
{
return Mathf.Abs(TargetX - myQuad.x) + Mathf.Abs(TargetZ - myQuad.z);
}
bool InList(Coord myQuad,List<Coord> List)
{
for(int i=0;i< List.Count;i++)
{
if(myQuad.Equal(List[i]))
{
return true;
}
}
return false;
}
}
public class Coord
{
public Coord(int _x, int _z,bool _isObs = false,float _cost = 0f,Coord _parent=null,float _Gcost = 0)
{
this.x = _x;
this.z = _z;
this.isObs = _isObs;
this.cost = _cost;
this.parent = _parent;
this.Gcost = _Gcost;
this.RealCost = this.cost + this.Gcost;
}
public bool Equal(Coord _c)
{
return (this.x == _c.x) && (this.z == _c.z);
}
public void setIsObs(bool _isObs)
{
this.isObs = _isObs;
}
public bool getIsObs()
{
return this.isObs;
}
public void setCost(float _cost)
{
this.cost = _cost;
}
public void setParent(Coord _Parent)
{
this.parent = _Parent;
}
public void reCalRealCost()
{
this.RealCost = this.cost + this.Gcost;
}
public void setGCost(float _gcost)
{
this.Gcost = _gcost;
reCalRealCost();
}
public int x;
public int z;
public bool isObs;
public float cost;
public Coord parent;
public float Gcost;
public float RealCost;
}
效果展示:(只允许横向移动) 开启斜向移动
|