提示:个人学习总结
格子类:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
/// <summary>
/// 格子类型
/// </summary>
public enum E_Node_Type
{
Walk,//可以走的地方
Stop,//不能走的地方
}
/// <summary>
/// A*格子类
/// </summary>
public class AStarNode
{
//格子对象的坐标
public int x;
public int y;
public float f;//寻路消耗
public float g;//离起点的距离
public float h;//离终点的距离
public AStarNode father;//父对象
public E_Node_Type type;//格子的类型
/// <summary>
/// 构造函数,传入坐标和类型
/// </summary>
/// <param name="x"></param>
/// <param name="y"></param>
/// <param name="type"></param>
public AStarNode(int x,int y,E_Node_Type type)
{
this.x = x;
this.y = y;
this.type = type;
}
}
A*管理器:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
/// <summary>
///A*寻路管理类
/// </summary>
public class AStarMgr:Singleton<AStarMgr>
{
//地图的宽高
private int mapW;
private int mapH;
//地图相关的所有的格子对象容器
public AStarNode[,] nodes;
//开启列表
private List<AStarNode> openList=new List<AStarNode>();
//关闭列表
private List<AStarNode> closeList=new List<AStarNode>();
private Canvas canvas;
/// <summary>
/// 初始化地图信息
/// </summary>
/// <param name="w"></param>
/// <param name="h"></param>
public void InitMap(int w, int h)
{
canvas=GameObject.Find("Canvas").GetComponent<Canvas>();
mapW = w;
mapH = h;
nodes=new AStarNode[w, h];
//根据宽高创建格子 随机阻挡
for (int i = 0; i < w; i++)
{
for (int j = 0; j < h; j++)
{
AStarNode node = new AStarNode(i, j, Random.Range(0, 100)<20?E_Node_Type.Stop:E_Node_Type.Walk);
nodes[i,j] = node;
}
}
}
/// <summary>
/// 寻路方法
/// </summary>
/// <param name="startPos"></param>
/// <param name="endPos"></param>
/// <returns></returns>
public List<AStarNode> FindPath(Vector2 startPos,Vector2 endPos)
{
//判断这些格子是否是边界 是否是阻挡点 是否在开启关闭列表中 符合条件放入开启列表
//判断传入的两个点是否合法
//1.是否在地图范围内
//不合法 返回null
if (startPos.x<0 || startPos.x>=mapW ||
startPos.y<0 || startPos.y>=mapH ||
endPos.x < 0 || startPos.x >= mapW ||
endPos.y < 0 || startPos.y >= mapH )
return null;
//合法 得到起点和终点对应的格子
AStarNode start = nodes[(int)startPos.x,(int)startPos.y];
AStarNode end = nodes[(int)endPos.x, (int)endPos.y];
//2.是否是阻挡点
if (start.type == E_Node_Type.Stop ||
end.type == E_Node_Type.Stop)
{
Debug.Log("开始或结束点是阻挡");
return null;
}
//清空上一次相关的数据 避免他们影响这一次的寻路计算
closeList.Clear();
openList.Clear();
//把开始点放入关闭列表
start.father = null;
start.f = 0;
start.g = 0;
start.h = 0;
closeList.Add(start);
while (true)
{
//从起点开始 找周围的点 并放入开启列表中
//左上 x-1 y-1
FindNearlyNodeToOpenList(start.x - 1, start.y - 1, 1.4f, start, end);
//上 x y-1
FindNearlyNodeToOpenList(start.x, start.y - 1, 1, start, end);
//右上 x+1 y-1
FindNearlyNodeToOpenList(start.x + 1, start.y - 1, 1.4f, start, end);
//左 x-1 y
FindNearlyNodeToOpenList(start.x - 1, start.y, 1f, start, end);
//右 x+1 y
FindNearlyNodeToOpenList(start.x + 1, start.y, 1f, start, end);
//左下 x-1 y+1
FindNearlyNodeToOpenList(start.x - 1, start.y + 1, 1.4f, start, end);
//下 x y+1
FindNearlyNodeToOpenList(start.x, start.y + 1, 1, start, end);
//右下 x+1 y+1
FindNearlyNodeToOpenList(start.x + 1, start.y + 1, 1.4f, start, end);
if (openList.Count == 0)
{
Debug.Log("是死路");
return null;
}
//选出开启列表中 寻路消耗最小的点
//找出最优的点 从开启列表中拿出放入关闭列表 在开启列表中移除
openList.Sort(SortOpenList);
closeList.Add(openList[0]);
//找到这个点 又变成新的起点 进行下一次寻路计算了
start = openList[0];
openList.RemoveAt(0);
//判断这个点是否是终点 是就返回 不是继续找
if (start == end)
{
//找完了 找到路径了
List<AStarNode> path = new List<AStarNode>();
path.Add(end);
while(end.father != null)
{
path.Add(end.father);
end = end.father;
}
//列表翻转
path.Reverse();
return path;
}
}
//return null;
}
private int SortOpenList(AStarNode a, AStarNode b)
{
if (a.f > b.f) return 1;
else if (a.f == b.f) return 1;
else return -1;
}
/// <summary>
/// 把附近点放入开启列表中的函数
/// </summary>
/// <param name="x"></param>
/// <param name="y"></param>
private void FindNearlyNodeToOpenList(int x, int y, float g,AStarNode father,AStarNode end)
{
//判断格子是否是边界
if(x<0 || x>=mapW ||
y<0 || y>=mapH)
return;
AStarNode node = nodes[x, y];
//判断这些格子是否是边界 是否是阻挡点 是否在开启关闭列表中 符合条件放入开启列表
if (node == null ||
node.type == E_Node_Type.Stop||
closeList.Contains(node)||
openList.Contains(node))
return;
//计算f值 f=g+h
//记录father父对象
node.father = father;
node.g = father.g + g;
if (openList.Contains(node))
{
float gThis = father.g + g;
if (gThis < node.g)
{
node.g = gThis;
node.f = node.g + node.h;
node.father = father;
return;
}
else
{
return;
}
}
node.h = Mathf.Abs(end.x - node.x) + Mathf.Abs(end.y - node.y);
node.f=node.g+node.h;
//符合条件就放入开启列表中
openList.Add(node);
}
}
测试类:
using System.Collections;
using System.Collections.Generic;
using UnityEditor;
using UnityEngine;
public class TastAStar : MonoBehaviour
{
//左上角第一个立方体的位置
public int baginX=-3;
public int baginY=5;
//之后每一个立方体之间的 偏移位置
public int offsectX=2;
public int offsectY=2;
//地图格子的宽高
public int mapW=10;
public int mapH=10;
private Vector2 beginPos = Vector2.right * -1;
private Dictionary<string, GameObject> cubes = new Dictionary<string, GameObject>();
private List<AStarNode> list;
public Material red;
// Start is called before the first frame update
void Start()
{
AStarMgr.GetInstance().InitMap(mapW, mapH);
for (int i = 0; i < mapW; i++)
{
for (int j = 0; j < mapH; j++)
{
GameObject obj = GameObject.CreatePrimitive(PrimitiveType.Cube);
obj.transform.position= new Vector3(baginX + i * offsectX, baginY + j * offsectY, 0);
obj.name=i+"_"+j;
cubes.Add(obj.name, obj);
AStarNode node = AStarMgr.GetInstance().nodes[i, j];
if (node.type == E_Node_Type.Stop)
{
obj.GetComponent<MeshRenderer>().material = red;
}
}
}
}
// Update is called once per frame
void Update()
{
if(Input.GetMouseButtonDown(0))
{
RaycastHit info;
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
if (Physics.Raycast(ray, out info, 1000))
{
if (beginPos == Vector2.right * -1)
{
//清理上一次的路径
if (list != null)
{
for (int i = 0; i < list.Count; i++)
{
cubes[list[i].x + "_" + list[i].y].GetComponent<MeshRenderer>().material.color = Color.white;
}
}
string[] strs = info.collider.gameObject.name.Split('_');
//得到行列位置 即开始位置
beginPos=new Vector2(int.Parse(strs[0]), int.Parse(strs[1]));
info.collider.gameObject.GetComponent<MeshRenderer>().material.color=Color.yellow;
}
else//有起点 那就是终点 进行寻路
{
string[] strs = info.collider.gameObject.name.Split('_');
Vector2 endPos = new Vector2(int.Parse(strs[0]), int.Parse(strs[1]));
list = AStarMgr.GetInstance().FindPath(beginPos, endPos);
//不为空 证明成功了
if (list != null)
{
for (int i = 0; i < list.Count; i++)
{
cubes[list[i].x + "_" + list[i].y].GetComponent<MeshRenderer>().material.color = Color.green;
}
}
//初始化开始点的位置
beginPos=Vector2.right * -1;
}
}
}
}
}
测试效果:
原码项目:
UnityA*寻路参考项目资源-CSDN文库
四.参考视频
原视频链接:【【手把手教你】Unity中实现A星寻路算法】https://www.bilibili.com/video/BV147411u7r5?vd_source=42f4411b8309016e682f2d58e46aaba7