首先非常感谢唐老师的课程,讲的非常好
添加链接描述
三个变量:
G值 = 父节点的G值 + 父节点到当前点的移动代价
(父节点的G是0,如果斜着走就是根号2,直着走就是1)
H值 = 当前点到结束点的曼哈顿距离
寻路消耗公式 F:=G+H
还有两个列表
开放列表: 吧当前所有可以走的节点都放进开放列表
关闭列表:每一次走过一个节点之后都把这个节点放到关闭列表里面
具体步骤
a、将开始点记录为当前点P
b、将当前点P放入关闭列表
c、搜寻点P所有邻近点,假如某邻近点既没有在开放列表或关闭列表里面,则计算出该邻近点的F值,并设父节点为P,然后将其放入开放列表
d、判断开放列表是否已经空了,如果没有说明在达到结束点前已经找完了所有可能的路径点,寻路失败,算法结束;否则继续。
e、从开放列表拿出一个F值最小的点,作为寻路路径的下一步。
f、判断该点是否为结束点,如果是,则寻路成功,算法结束;否则继续。
g、将该点设为当前点P,跳回步骤c。
找出路径
从终点节点开始,找其父节点,顺藤摸瓜,一直找到起始节点,这便是一条路径
总结:
一下跟着写的代码,一个小demon:、
优化的地方:
格子类:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class AStarNode
{
//格子对象坐标
public int x;
public int y;
//寻路消耗
public float f;
//距离起点距离
public float g;
//距离终点的距离
public float h;
//父对象
public AStarNode fataher;
//格子类型
public ENodeType type;
//构造函数,传入格子坐标和格子类型
public AStarNode(int x,int y,ENodeType type)
{
this.x = x;
this.y = y;
this.type = type;
}
}
///
/// 格子类型
///
public enum ENodeType
{
Walk,
Stop,
}
管理类:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class AStartMgr
{
#region 构造单例模式
private AStartMgr() {
}
public static AStartMgr Instance
{
get
{
return Nested._instance;
}
}
private class Nested
{
internal static readonly AStartMgr _instance = new AStartMgr();
}
#endregion
//地图相关所有格子的对象容器
private AStarNode[,] nodes;
public AStarNode[,] Nodes
{
get {
return nodes; }
}
//开启列表
private List<AStarNode> openList=new List<AStarNode>();
//关闭列表
private List<AStarNode> closeList = new List<AStarNode>();
//地图的宽高
private int mapW;
private int mapH;
///
/// 初始化地图信息
///
///
///
public void InitMapInfo(int w,int h)
{
this.mapW = w;
this.mapH = h;
nodes = new AStarNode[w, h];
for(int i = 0; i < w; i++)
{
for(int j = 0; j < h; j++)
{
//15%的可能性是阻塞,
AStarNode node = new AStarNode(i, j, Random.Range(0, 100) < 40 ? ENodeType.Stop : ENodeType.Walk);
nodes[i, j] = node;
}
}
}
///
/// 寻路方法 当openlist所有的点都完了的话就是思路一条
///
///
///
///
public List<AStarNode> FindPath(Vector2 startPos, Vector2 endPos)
{
///实际项目中 传入的点往往是坐标系的位置,我们这里省略换算的步骤,认为就是传进来的格子坐标
///首先判断传入的两个点是否合法
///要是不合法直接return
if (startPos.x < 0 || startPos.y >= mapW || startPos.y < 0 || startPos.y >= mapH
|| endPos.x < 0 || endPos.y >= mapW || endPos.y < 0 || endPos.y >= mapH)
{
return null;
}
///在范围之内并且不是阻挡
AStarNode start = nodes[(int)startPos.x, (int)startPos.y];
AStarNode end = nodes[(int)endPos.x, (int)endPos.y];
if (start.type == ENodeType.Stop || end.type == ENodeType.Stop)
{
return null;
}
///还得清空脏数据
openList.Clear();
closeList.Clear();
///开始点放到关闭列表中
start.fataher = null;
start.f = 0;
start.g = 0;
start.h = 0;
closeList.Add(start);
while (true)
{
///从起点开始找,找周围的点,
///左上
FindNearlyNodeToOpenList(start.x - 1, start.y - 1, 1.4f, start, end);
///上
FindNearlyNodeToOpenList(start.x, start.y - 1, 1f, start, end);
///右上
FindNearlyNodeToOpenList(start.x + 1, start.y - 1, 1.4f, start, end);
///左
FindNearlyNodeToOpenList(start.x - 1, start.y, 1f, start, end);
///右
FindNearlyNodeToOpenList(start.x + 1, start.y, 1f, start, end);
///左下
FindNearlyNodeToOpenList(start.x - 1, start.y + 1, 1.4f, start, end);
///下
FindNearlyNodeToOpenList(start.x, start.y + 1, 1f, start, end);
///右下
FindNearlyNodeToOpenList(start.x + 1, start.y + 1, 1f, start, end);
///死路
if (openList.Count == 0)
{
Debug.Log("死路一条");
return null;
}
///选出开启列表中寻路消耗最小的点
openList.Sort((AStarNode a, AStarNode b) => {
if (a.f > b.f)
return 1;
else if (a.f == b.f)
return 1;
return -1;
});
///放入关闭列表,然后从开启列表中移除,这个点变成新的起点
start = openList[0];
closeList.Add(openList[0]);
openList.RemoveAt(0);
///如果这个点是终点,则得到结果,否则继续寻找
if (start == end)
{
//找出路径
List<AStarNode> path = new List<AStarNode>();
path.Add(end);
while (end.fataher != null)
{
path.Add(end.fataher);
end = end.fataher;
}
path.Reverse();
return path;
}
}
return null;
}
///
/// 吧临近附近的点放到开启列表之中
///
///
///
///
///
///
private void FindNearlyNodeToOpenList(int x, int y,float g, AStarNode fatherNode,AStarNode end)
{
//边界判断 是否阻挡,是否在开启列表或者关闭列表
if (x < 0 || x >= mapW || y < 0 || y >= mapH)
return;
AStarNode node = nodes[x, y];
if (node == null || node.type == ENodeType.Stop || closeList.Contains(node) || openList.Contains(node))
return;
//计算f 放到开启列表
node.h = Mathf.Abs(end.x - node.x) + Mathf.Abs(end.y - node.y);
node.g = fatherNode.g + g;
node.f = node.g = node.h;
node.fataher = fatherNode;
openList.Add(node);
}
}
测试类:吧这个类挂载一下运行就可以了
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class AStarTest : MonoBehaviour
{
//左上角第一个位置
public int beginX;
public int beginY;
//偏移量
public int offsetX;
public int offsetY;
//地图格子的宽高
public int mapW;
public int mapH;
//阻挡物材质
public Material stopMaterial;
public Material beginMaterial;
public Material endMaterial;
private Dictionary<string, GameObject> cubes = new Dictionary<string, GameObject>();
private Vector2 beginPos = new Vector2(-1, -1);
private Vector2 endPos = new Vector2(-1, -1);
private List<AStarNode> _path = new List<AStarNode>();
private void Start()
{
AStartMgr.Instance.InitMapInfo(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(beginX + i * offsetX, beginY + j * offsetY, 0);
AStarNode node = AStartMgr.Instance.Nodes[i, j];
obj.name = i + "_" + j;
cubes.Add(obj.name, obj);
//格子是否阻挡
if (node.type == ENodeType.Stop)
{
obj.GetComponent<MeshRenderer>().material = stopMaterial;
}
}
}
}
public void Update()
{
if (Input.GetMouseButtonDown(0))
{
RaycastHit info;
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
if(Physics.Raycast(ray,out info, 100))
{
if (info.collider.gameObject == null)
return;
//起点
if (beginPos.x==-1 && beginPos.y == -1)
{
string[] strs = info.collider.gameObject.name.Split('_');
beginPos = new Vector2(int.Parse(strs[0]), int.Parse(strs[1]));
info.collider.gameObject.GetComponent<MeshRenderer>().material = beginMaterial;
}
//终点
else if(endPos.x==-1&& endPos.y == -1)
{
string[] strs = info.collider.gameObject.name.Split('_');
endPos = new Vector2(int.Parse(strs[0]), int.Parse(strs[1]));
info.collider.gameObject.GetComponent<MeshRenderer>().material = endMaterial;
//路径
_path = AStartMgr.Instance.FindPath(beginPos, endPos);
if (_path != null)
{
foreach (AStarNode node in _path)
{
string itsNmae = node.x + "_" + node.y;
cubes[node.x + "_" + node.y].GetComponent<MeshRenderer>().material = endMaterial;
Debug.Log(itsNmae);
}
}
}
}
}
}
}