一、实现效果:给一个起点、终点和障碍物,会绕过障碍物自动生成路径,如下图,起点为(0,0),终点为(7,7),图中红色方块,黑色为障碍物,绿色为可行走区域,灰色为自动生成的路径,起点和终点在代码中可以进行修改
二、游戏框架
Plane:一个平面,用来存放节点
New sprite:代表节点,用像素图片来代表节点,通过修改图片的颜色来表示起点,终点,障碍物和路径
三、游戏实现
1、首先创建一个plane,调整他和摄像机的位置,使它全屏显示 ,给它添加背景图片或者材质使它美观,背景图片或者材质的颜色尽量避开绿色,红色,黑色和灰色
2、创建一个sprite,赋予颜色和外观
3、编写代码,赋予平面,代码如下:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Astar : MonoBehaviour
{
public const int width = 10;
public const int height = 10;
public Point[,] map = new Point[height, width];
public SpriteRenderer[,] sprites = new SpriteRenderer[height, width];//图片和结点一一对应
public GameObject prefab; //代表结点的图片
public Point start;
public Point end;
void Start()
{
InitMap();
//测试代码
AddObstacle(2, 4);
AddObstacle(2, 3);
AddObstacle(2, 2);
AddObstacle(2, 0);
AddObstacle(6, 4);
AddObstacle(8, 4);
SetStartAndEnd(0, 0, 7, 7);
FindPath();
ShowPath();
}
public void InitMap()//初始化地图
{
for (int i = 0; i < width; i++)
{
for (int j = 0; j < height; j++)
{
sprites[i, j] = Instantiate(prefab, new Vector3(i, j, 0), Quaternion.identity).GetComponent<SpriteRenderer>();
map[i, j] = new Point(i, 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()//显示路径
{
Point temp = end.parent;
while (temp != start)
{
sprites[temp.X, temp.Y].color = Color.gray;
temp = temp.parent;
}
}
public void FindPath()
{
List<Point> openList = new List<Point>();
List<Point> closeList = new List<Point>();
openList.Add(start);
while (openList.Count > 0)//只要开放列表还存在元素就继续
{
Point point = GetMinFOfList(openList);//选出open集合中F值最小的点
openList.Remove(point);
closeList.Add(point);
List<Point> SurroundPoints = GetSurroundPoint(point.X, point.Y);
foreach (Point p in closeList)//在周围点中把已经在关闭列表的点删除
{
if (SurroundPoints.Contains(p))
{
SurroundPoints.Remove(p);
}
}
foreach (Point p in SurroundPoints)//遍历周围的点
{
if (openList.Contains(p))//周围点已经在开放列表中
{
//重新计算G,如果比原来的G更小,就更改这个点的父亲
int newG = 1 + point.G;
if (newG < p.G)
{
p.SetParent(point, newG);
}
}
else
{
//设置父亲和F并加入开放列表
p.parent = point;
GetF(p);
openList.Add(p);
}
}
if (openList.Contains(end))//只要出现终点就结束
{
break;
}
}
}
public List<Point> GetSurroundPoint(int x, int y)//得到一个点周围的点
{
List<Point> PointList = new List<Point>();
if (x > 0 && !map[x - 1, y].isObstacle)
{
PointList.Add(map[x - 1, y]);
}
if (y > 0 && !map[x, y - 1].isObstacle)
{
PointList.Add(map[x, y - 1]);
}
if (x < height - 1 && !map[x + 1, y].isObstacle)
{
PointList.Add(map[x + 1, y]);
}
if (y < width - 1 && !map[x, y + 1].isObstacle)
{
PointList.Add(map[x, y + 1]);
}
return PointList;
}
public void GetF(Point point)//计算某个点的F值
{
int G = 0;
int H = Mathf.Abs(end.X - point.X) + Mathf.Abs(end.Y - point.Y);
if (point.parent != null)
{
G = 1 + point.parent.G;
}
int F = H + G;
point.H = H;
point.G = G;
point.F = F;
}
public Point GetMinFOfList(List<Point> list)//得到一个集合中F值最小的点
{
int min = int.MaxValue;
Point point = null;
foreach (Point p in list)
{
if (p.F < min)
{
min = p.F;
point = p;
}
}
return point;
}
}
public class Point
{
public int X;
public int Y;
public int F;
public int G;
public int H;
public Point parent = null;
public bool isObstacle = false;
public Point(int x, int y)
{
X = x;
Y = y;
}
public void SetParent(Point parent, int g)
{
this.parent = parent;
G = g;
F = G + H;
}
}
4、把new sprite拖到prefab的位置
5、点击运行,生成结果如下,需要修改起始点的话可以在Astar代码中修改,相关的地方都有备注