本篇文章的算法来源于这里
在原来的实现基础上,改为使用UI中的Image表现路径,并且加上了F,G,H的数值显示
A*的核心公式,F=G+H,F表示到达这个点的总代价,G表示从起点到达这个点的代价,H表示这个点到终点的代价
还有两个集合,open和close,分别代表待检查的点集合和已经检查过的点集合
大概流程
1.将待检查的点从open中拿出来,放入close,一般是open里面F最小的点
2.获得这个点周围所有的点
3.检查这些周围点中是否有障碍物,如果有,直接去掉
4.检查周围点中是否有已经在open中的,如果是,需要检查从待检查点到这个点的G值是够比这个点本身的G更小,如果是,更新这个点的F,G信息,并将两个点设为父子
5.如果不是,就更新周围点的F,G,H
6.如果open里面包含了终点,则结束
7.否则继续遍历open
public class AStarFix : MonoBehaviour
{
public const int width=1000;
public const int height=1000;
public PointFix[,] MAP=new PointFix[height,width];
public Image[,] sprites=new Image[height,width];
public GameObject BG;
public PointFix START;
public PointFix END;
public Transform canvas;
private void Start()
{
InitMap();
AddObstacle(200, 300);
AddObstacle(200, 400);
AddObstacle(200, 500);
AddObstacle(200, 600);
AddObstacle(200, 700);
AddObstacle(200, 800);
SetStartAndEnd(0, 500, 400, 500);
FindPath();
ShowPath();
}
public void InitMap() //初始化地图
{
for (int i = 0; i < width; i+=100)
{
for (int j = 0; j < height; j+=100)
{
sprites[i,j]=Instantiate(BG,new Vector3(i,j,0),Quaternion.identity,canvas).GetComponent<Image>();
MAP[i, j] = sprites[i, j].gameObject.GetComponent<PointFix>();
sprites[i, j].gameObject.GetComponent<PointFix>().X = i;
sprites[i, j].gameObject.GetComponent<PointFix>().Y = j;
}
}
}
public void AddObstacle(int x,int y) //添加障碍物
{
MAP[x,y].isObstacle=true;
sprites[x,y].color=Color.black;
}
public void SetStartAndEnd(int startX,int startY,int endX,int endY) //设置起点和终点
{
START=MAP[startX,startY];
sprites[startX,startY].color=Color.green;
END=MAP[endX,endY];
sprites[endX,endY].color=Color.red;
}
public void ShowPath() //显示路径
{
PointFix temp=END.parent;
while(temp!=null && temp!=START)
{
sprites[temp.X,temp.Y].color=Color.yellow;
temp=temp.parent;
}
}
public int GetG(PointFix p,PointFix other)
{
if(Mathf.Abs(p.Y-other.Y)==100 && Mathf.Abs(p.X-other.X)==100)
{
return 14+other.G;
}
return 10+other.G;
}
public void GetF(PointFix point)
{
int G=0;
int H=Mathf.Abs(point.X-END.X)+Mathf.Abs(point.Y-END.Y);
if(point.parent!=null)
{
//G=1+point.parent.G;
G=GetG(point,point.parent);
}
int F=G+H;
point.H=H;
point.G=G;
point.F=F;
point.Htxt.text="H="+H.ToString();
point.Gtxt.text="G="+G.ToString();
point.Ftxt.text="F="+F.ToString();
}
public List<PointFix> GetSurroundPoint(int x,int y) //得到周围的点,这里是八方向
{
List<PointFix> list=new List<PointFix>();
if(x>0 && !MAP[x-100,y].isObstacle)
{
list.Add(MAP[x-100,y]);
}
if(y>0 && !MAP[x,y-100].isObstacle)
{
list.Add(MAP[x,y-100]);
}
if(x<height-100 &&!MAP[x+100,y].isObstacle)
{
list.Add(MAP[x+100,y]);
}
if(y<width-100 && !MAP[x,y+100].isObstacle)
{
list.Add(MAP[x,y+100]);
}
/// // //
if(x<height-100 && y<width-100 && !MAP[x+100,y+100].isObstacle)
{
list.Add(MAP[x+100,y+100]);
}
if(x<height-100 && y>0 && !MAP[x+100,y-100].isObstacle)
{
list.Add(MAP[x+100,y-100]);
}
if(x>0 && y<width-100 && !MAP[x-100,y+100].isObstacle)
{
list.Add(MAP[x-100,y+100]);
}
if(x>0 && y>0 && !MAP[x-100,y-100].isObstacle)
{
list.Add(MAP[x-100,y-100]);
}
return list;
}
public void FindPath() //寻路
{
List<PointFix> openList=new List<PointFix>();
List<PointFix> closeList=new List<PointFix>();
openList.Add(START);
while(openList.Count!=0)
{
openList.Sort((x,y)=>x.F.CompareTo(y.F));
PointFix point=openList[0];
openList.RemoveAt(0);
closeList.Add(point);
List<PointFix> SPoints=GetSurroundPoint(point.X,point.Y);
foreach(PointFix p in closeList)
{
if(SPoints.Contains(p))
{
SPoints.Remove(p);
}
}
foreach(PointFix p in SPoints)
{
if(openList.Contains(p))
{
//int newG=1+point.G;
int newG=GetG(p,point);
if(newG<p.G)
{
p.SetParent(point,newG);
}
}
else
{
p.parent=point;
GetF(p);
openList.Add(p);
}
}
if(openList.Contains(END))
{
break;
}
}
}
}
public class PointFix : MonoBehaviour //节点类
{
public int X;
public int Y;
public int G;
public int H;
public int F;
public PointFix parent=null;
public bool isObstacle=false;
public Text Ftxt;
public Text Gtxt;
public Text Htxt;
public PointFix(int x,int y)
{
X = x;
Y = y;
}
public void SetParent(PointFix parent,int g)
{
this.parent = parent;
G = g;
F = G + H;
Gtxt.text="G="+ G.ToString();
Ftxt.text="F="+ F.ToString();
}
}