参考视频
//节点类
//节点类型枚举
public enum NodeTyp
{
//可用格子
Able,
//不可用格子
Unable
}
public class AStartNode
{
//格子坐标
public int x;
public int y;
//寻路消耗
public float f;
//起点距离
public float g;
//终点距离
public float h;
//父对象
public AStartNode father;
//格子类型
public NodeTyp type;
public AStartNode(int x,int y, NodeTyp type)
{
this.x = x;
this.y = y;
this.type = type;
}
}
//单例基类
public class BaseManager
where T:new()//限定T有一个无参数的构造函数
{
private static T instence;
public static T Instence
{
get
{
if (instence == null)
instence = new T();
return instence;
}
}
}
//管理器类(单例)
public class AStartManager :BaseManager
{
/*private static AStartManager instence;
public static AStartManager Instence
{
get
{
if (instence == null)
instence = new AStartManager();
return instence;
}
}
*/
//地图大小
private int mapw;
private int maph;
//所有格子容器
public AStartNode[,] nodes;
//开启类表
private List
//关闭类表
private List
///
/// 初始化地图信息
///
///
///
public void InitMapInfo(int mapw, int maph)
{
this.mapw = mapw;
this.maph = maph;
nodes = new AStartNode[mapw, maph];
for (int i = 0; i < mapw; i++)
{
for (int j = 0; j < maph; j++)
{
//格子20%概率为阻挡
AStartNode node = new AStartNode(i, j, Random.Range(0, 101) < 20 ? NodeTyp.Unable : NodeTyp.Able);
nodes[i, j] = node;
}
}
}
/// <summary>
/// 寻路方法
/// </summary>
/// <param name="startPos"></param>
/// <param name="endPos"></param>
/// <returns></returns>
public List<AStartNode> FindPath(Vector2 startPos, Vector2 endPos)
{
/*/判断传入顶点是否合法*/
//1.1传入顶点噪mapz范围内
if (startPos.x < 0 || startPos.x >= mapw || startPos.y < 0 || startPos.y >= maph
|| endPos.x < 0 || endPos.x >= mapw || endPos.y < 0 || endPos.y >= maph)
{
Debug.Log("OutRange");
return null;
}
AStartNode start = nodes[(int)startPos.x, (int)startPos.y];
AStartNode end = nodes[(int)endPos.x, (int)endPos.y];
//2.传入顶点不为阻挡
//不合法则返回null
if (start.type == NodeTyp.Unable ||
end.type == NodeTyp.Unable)
{
Debug.Log("非法位置");
return null;
}
//清空上一次相关数据,避免影响此次计算
//清空开始/关闭类表
openList.Clear();
closeList.Clear();
//把开始点放入关闭类表中
start.father = null;
start.g = 0;
start.h = 0;
start.f = 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, 1.4f, start, end);
//判断周围的点是否合法,是否在开启或关闭类表,不是则放入开始了表
//死路判定:开启类表为空时中非那不可达
if (openList.Count == 0)
{
Debug.Log("死路");
return null;
}
//选出开启类表中寻路消耗最小的点
openList.Sort(SortOpenList);//排序后openList[0]为f值最小的点
//放入关闭类表中然后从开始了表中移除
closeList.Add(openList[0]);
//找到的点作为下一次寻找开始的起点
start = openList[0];
openList.RemoveAt(0);
//如果这个点是终点则返回出去
//不是终点则继续寻找
if (start == end)
{
List<AStartNode> path = new List<AStartNode>();
path.Add(end);
while (end.father != null)
{
path.Add(end.father);
end = end.father;
}
path.Reverse();
return path;
}
}
}
/// <summary>
/// 排序函数
/// </summary>
/// <param name="a"></param>
/// <param name="b"></param>
/// <returns></returns>
private int SortOpenList(AStartNode a, AStartNode 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, AStartNode fatherNode, AStartNode endNode)
{
//边界判定
if (x < 0 || x >= mapw ||
y < 0 || y >= maph)
return;
//在map范围内在取点
AStartNode node = nodes[x, y];
if (node == null || node.type == NodeTyp.Unable
|| openList.Contains(node) || closeList.Contains(node))
return;
//计算f值:f=g+h
//记录父对象
node.father = fatherNode;
//计算g(距离起点距离)值:父对象里欠点距离+距离父对象距离
node.g = fatherNode.g + g;
//计算h值(距离终点距离):曼哈顿街区算法二维坐标差值
node.h = Mathf.Abs(endNode.x - node.x) + Mathf.Abs(endNode.y - node.y);
node.f = node.g + node.h;
//通过验证则存入开始了表中
openList.Add(node);
}
}
//测试脚本
public class TextA : MonoBehaviour
{
public int beginX = -2;
public int beginY = -2;
public int offSetX = 2;
public int OffSetY = 2;
public int mapW = 5;
public int mapY = 5;
public Material bnary;
public Material start;
public Material path;
public Material normal;
//存储格子容器(x_y,GameObject)
private Dictionary<string, GameObject> cubes = new Dictionary<string, GameObject>();
private Vector2 beginPos = Vector2.right * -1;
private Vector2 endPos;
//存储路径节点
List<AStartNode> pathList = new List<AStartNode>();
// Start is called before the first frame update
void Start()
{
AStartManager.Instence.InitMapInfo(mapW, mapY);
for (int i = 0; i < mapW; i++)
{
for (int j = 0; j < mapY; j++)
{
GameObject obj = GameObject.CreatePrimitive(PrimitiveType.Cube);
obj.transform.position = new Vector3(beginX + i * offSetX, beginY + j * OffSetY, 0);
obj.name = i + "_" + j;
cubes.Add(obj.name, obj);
//判断格子是否阻挡
AStartNode node = AStartManager.Instence.nodes[i, j];
if(node.type == NodeTyp.Unable)
{
obj.GetComponent<MeshRenderer>().material= bnary ;
Debug.Log("Br");
}
}
}
}
// Update is called once per frame
void Update()
{
if(Input.GetMouseButtonDown(0))
{
//射线检测返回值
RaycastHit hit;
//相机视角从鼠标位置发出的射线
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
//射线检测,检测到物体返回true
if (Physics.Raycast(ray, out hit, 1000))
{
//得到点击到的立方体
//hit.collider.gameObject;
//记录开始点与结束点
if(beginPos == Vector2.right*-1)
{
//清理上一次路径
if (pathList != null)
{
for (int i = 0; i < pathList.Count; i++)
{
cubes[pathList[i].x + "_" + pathList[i].y].GetComponent<MeshRenderer>().material = normal;
}
}
string[] str = hit.collider.gameObject.name.Split('_');
beginPos = new Vector2(int.Parse(str[0]), int.Parse(str[1]));
hit.collider.gameObject.GetComponent<MeshRenderer>().material = start;
}
else
{
string[] str = hit.collider.gameObject.name.Split('_');
endPos = new Vector2(int.Parse(str[0]), int.Parse(str[1]));
//hit.collider.gameObject.GetComponent<MeshRenderer>().material = end;
pathList = AStartManager.Instence.FindPath(beginPos, endPos);
if(pathList !=null)
{
for (int i = 0; i < pathList.Count; i++)
{
cubes[pathList[i].x + "_" + pathList[i].y].GetComponent<MeshRenderer>().material = path;
}
}
//清除开始点
beginPos = Vector2.right * -1;
}
}
}
}
}