赞
踩
本文实现的A*算法,未经过大量的优化,后续文章会进一步实现优化
结点类:
- using System.Collections;
- using System.Collections.Generic;
- using UnityEngine;
-
- public enum E_Node_Type
- {
- /// <summary>
- /// 可行走
- /// </summary>
- Normal,
- /// <summary>
- /// 障碍
- /// </summary>
- Obstacles
- }
-
-
- public class AStarNode
- {
- /// <summary>
- /// x坐标
- /// </summary>
- public int x;
- /// <summary>
- /// y坐标
- /// </summary>
- public int y;
-
- /// <summary>
- /// 寻路消耗
- /// </summary>
- public float f;
- /// <summary>
- /// 距离起点距离
- /// </summary>
- public float g;
- /// <summary>
- /// 距离终点距离
- /// </summary>
- public float h;
-
- /// <summary>
- /// 父结点
- /// </summary>
- public AStarNode father;
-
- /// <summary>
- /// 结点类型
- /// </summary>
- public E_Node_Type type;
-
- public AStarNode(int xPos, int yPos, E_Node_Type t)
- {
- this.x = xPos;
- this.y = yPos;
- this.type = t;
- }
-
- }
结点管理类:
- using System.Collections;
- using System.Collections.Generic;
- using UnityEngine;
-
- public class AStarManger : BaseManger<AStarManger>
- {
- public int mapW;
- public int mapH;
-
- private AStarNode startNode;
- private AStarNode endNode;
-
- //private Vector2 endVec = Vector2.right * -1;
-
- public AStarNode[,] nodes;
-
- List<AStarNode> openList = new List<AStarNode>();
- List<AStarNode> closeList = new List<AStarNode>();
-
- /// <summary>
- /// 初始化
- /// </summary>
- public void InitAStarManger(int mapW, int mapH)
- {
- if(mapH < 0 || mapW < 0)
- {
- Debug.LogError("地图尺寸存在问题");
- return;
- }
- this.mapW = mapW;
- this.mapH = mapH;
-
- nodes = new AStarNode[mapW, mapH];
-
- for(int i = 0; i < this.mapW; i++)
- {
- for(int j = 0; j < this.mapH; j++)
- {
- AStarNode an = new AStarNode(i, j, Random.Range(0, 100) < 20 ? E_Node_Type.Obstacles : E_Node_Type.Normal);
- nodes[i, j] = an;
- }
- }
-
- }
-
- /// <summary>
- /// 开始寻径
- /// </summary>
- /// <returns></returns>
- public List<AStarNode> FindPath(Vector2 startPos, Vector2 endPos)
- {
- if(startPos.x < 0 || startPos.y < 0 || endPos.x < 0 || endPos.y < 0
- || startPos.x >= this.mapW || startPos.y >= this.mapH
- || endPos.x >= this.mapW || endPos.y >= this.mapH)
- {
- Debug.LogError("坐标超出范围");
- return null;
- }
-
- startNode = nodes[(int)startPos.x, (int)startPos.y];
- endNode = nodes[(int)endPos.x, (int)endPos.y];
-
- if(startNode.type is E_Node_Type.Obstacles || endNode.type is E_Node_Type.Obstacles)
- {
- Debug.LogError("起始结点或目标结点为障碍");
- return null;
- }
-
- startNode.father = null;
- startNode.f = 0;
- startNode.g = 0;
- startNode.h = 0;
-
- openList.Clear();
- closeList.Clear();
-
- closeList.Add(startNode);
-
- while(true)
- {
- //找附近点,并将符合条件的添加进openList
- AddNearlyNode2OpenList(startNode.x - 1, startNode.y -1, 1.4f, startNode, endPos);
- AddNearlyNode2OpenList(startNode.x, startNode.y - 1, 1f, startNode, endPos);
- AddNearlyNode2OpenList(startNode.x + 1, startNode.y - 1, 1.4f, startNode, endPos);
- AddNearlyNode2OpenList(startNode.x - 1, startNode.y, 1f, startNode, endPos);
- AddNearlyNode2OpenList(startNode.x + 1, startNode.y, 1f, startNode, endPos);
- AddNearlyNode2OpenList(startNode.x - 1, startNode.y + 1, 1.4f, startNode, endPos);
- AddNearlyNode2OpenList(startNode.x, startNode.y + 1, 1f, startNode, endPos);
- AddNearlyNode2OpenList(startNode.x + 1, startNode.y + 1, 1.4f, startNode, endPos);
-
- if(openList.Count == 0)
- {
- Debug.LogWarning("终点不可达");
- return null;
- }
-
- //排序
- openList.Sort(SortRuler);
- //选择新的父节点
- startNode = openList[0];
- //添加到closeList
- closeList.Add(startNode);
- openList.RemoveAt(0);
- //判断是否到达终点
- if(startNode == endNode)
- {
- List<AStarNode> path = new List<AStarNode>();
- path.Add(endNode);
- while(endNode.father != null)
- {
- path.Add(endNode.father);
- endNode = endNode.father;
- }
- path.Reverse();
- return path;
- }
- }
- }
-
- private int SortRuler(AStarNode a, AStarNode b)
- {
- if (a.f >= b.f) return 1;
- else return -1;
- }
-
- /// <summary>
- /// 将父结点周围符合条件结点,添加进openList
- /// </summary>
- private void AddNearlyNode2OpenList(int x, int y, float g, AStarNode father, Vector2 endPos)
- {
- //判断传入坐标是否超范围
- if(x < 0 || y < 0 || x >= this.mapW || y >= this.mapH)
- {
- return;
- }
-
- AStarNode an = nodes[x, y];
-
- //如果该结点是障碍结点 或 ol,cl里有,忽略
- //如果结点为null,或是上下左右都有障碍物,忽略
- if(an.type is E_Node_Type.Obstacles
- || an is null || closeList.Contains(an)
- || nodes[x, father.y].type is E_Node_Type.Obstacles
- || nodes[father.x, y].type is E_Node_Type.Obstacles)
- {
- return;
- }
-
- //单独判断是否在ol里,优化。如果结点已经在ol里,并且下一个最小f值的点也可以算到该点,那么该点的父节点不会变为这个最小值点,导致不是最短路径
- if(openList.Contains(an))
- {
- float gThis = father.g + g;
- if(gThis < an.g)
- {
- an.g = gThis;
- an.f = an.g + an.h;
- an.father = father;
- }
- return;
- }
-
- //父结点距离起点距离,加上自己距离父结点距离
- an.g = father.g + g;
- an.h = Mathf.Abs(endPos.x - x) + Mathf.Abs(endPos.y - y);
- //计算曼哈顿距离
- an.f = an.g + an.h;
-
- //将结点加入openList
- openList.Add(an);
-
- //设置父结点
- an.father = father;
- }
- }
-
单例模板:
- using System.Collections;
- using System.Collections.Generic;
- using UnityEngine;
-
- public class BaseManger<T> where T : new()
- {
- private static T instance;
-
- public static T Getinstance()
- {
- if(instance is null)
- {
- instance = new T();
- }
- return instance;
- }
- }
测试脚本:
- using System.Collections;
- using System.Collections.Generic;
- using UnityEngine;
- using UnityEngine.UIElements;
-
- public class Test : MonoBehaviour
- {
- public float BeginX;
- public float BeginY;
-
- public float offsetX;
- public float offsetY;
-
- public int mapW;
- public int mapH;
-
- private Vector2 beginPos = Vector2.right * -1;
- private Dictionary<string, GameObject> cubes = new Dictionary<string, GameObject>();
- List<AStarNode> list = new List<AStarNode>();
-
- private GameObject tmpStart;
- private GameObject tmpEnd;
- // Start is called before the first frame update
- void Start()
- {
- Init();
- }
-
- // Update is called once per frame
- void Update()
- {
- if(Input.GetMouseButtonDown(0))
- {
- Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
- RaycastHit info;
- 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]));
-
- if (tmpEnd) tmpEnd.GetComponent<MeshRenderer>().material.color = Color.white;
- if (tmpStart) tmpStart.GetComponent<MeshRenderer>().material.color = Color.white;
-
- tmpStart = info.collider.gameObject;
- tmpStart.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]));
-
- tmpEnd = info.collider.gameObject;
- tmpEnd.GetComponent<MeshRenderer>().material.color = Color.blue;
- list = AStarManger.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;
- }
- }
- }
- }
-
- public void Init()
- {
- AStarManger.Getinstance().InitAStarManger(mapW, mapH);
- tmpEnd = null;
- tmpStart = null;
- list = null;
-
- if(cubes.Count != 0)
- {
- foreach (var obj in cubes)
- {
- Destroy(obj.Value.gameObject);
- }
- cubes.Clear();
- }
- 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);
- obj.transform.name = i + "_" + j;
-
- cubes.Add(obj.name, obj);
-
- AStarNode an = AStarManger.Getinstance().nodes[i, j];
- if (an.type == E_Node_Type.Obstacles)
- {
- MeshRenderer mr = obj.GetComponent<MeshRenderer>();
- mr.material.color = Color.red;
- }
- }
- }
- }
- }
新建一个场景,将测试脚本挂载在任意物体上
新建一个画布,并添加一个按钮。其它ui元素可随意设定
将按钮关联Init方法
后续优化文章:
本文仅为学习阶段的一个总结,按需浏览。存在一定不足的情况
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。