这几天学习了一下A*寻路,这里记录一下代码,网上有很多详细的教程,代码里注释很详细。这里就不赘述了,直接上代码(萌新一枚,如有错还请指出!一起学习!)
首先是格子对象类
/// /// 格子对象类 /// public class AstarNode { public int x;//格子坐标信息 X Y public int y; public float f;//格子消耗信息 public float g; public float h; public AstarNode parent;//格子父对象 public NodeType nodeType;//格子类型 public AstarNode(){}//构造函数 public AstarNode(int x,int y,NodeType type) { this.x = x; this.y = y; this.nodeType = type; } } public enum NodeType { Wlak, Stop, } 然后是核心管理类
/// /// A*寻路核心类 /// public class AstarManager : MonoBehaviour { private static AstarManager manager; public static AstarManager Manager { get { //if (manager == null) //{ // manager = this; //} return manager; } } private void Awake() { manager = this; } //地图最大高度 public int maxHight; //地图最大宽度 public int maxWidth; //获取场景网格父对象 public Transform NodeParent; //存储格子的二维数组 private AstarNode[,] nodes; //开启列表 private List<AstarNode> openList = new List<AstarNode>(); //关闭列表 private List<AstarNode> closeList = new List<AstarNode>(); //存储场景中的格子对象 private List<RectTransform> nodeObj = new List<RectTransform>(); /// /// 初始化地图大小 /// /// 宽 /// 高 public void InitMap(int w, int h) { //获取场景中的格子父对象 NodeParent = GameObject.Find("Canvas/BG").GetComponent<Transform>(); //设置高 maxHight = h; //设置宽 maxWidth = w; //初始化node二维数组 nodes = new AstarNode[w, h]; for (int i = 0; i < w; i++) { for (int j = 0; j < h; j++) { //生成格子对象,并存入二维数组 nodes[i, j] = new AstarNode(i, j, Random.Range(0, 100) < 20 ? NodeType.Stop : NodeType.Wlak); //------------------在场景中生成对象 GameObject temp = Instantiate<GameObject>(Resources.Load<GameObject>("node")); temp.transform.SetParent(NodeParent); temp.transform.localScale = Vector3.one; //这里*105是因为格子在数值里是与场景里的格子大小不一样,如格子(1,1)位置,场景里一个对象大小我设置的是宽高都是100, //所以这里位置是宽高分别 *100,写105是想保持间隔 ((temp.transform) as RectTransform).anchoredPosition = new Vector3(i * 105, j * 105, 0); nodeObj.Add(temp.GetComponent<RectTransform>()); if (nodes[i, j].nodeType==NodeType.Stop) { //如果格子是障碍 则改变颜色为黑色 temp.GetComponent<Image>().color = Color.black; } //------------------在场景中生成对象 } } } /// /// 开始寻路 /// /// 起点 /// 终点 /// public List<AstarNode> FindPath(Vector2 startPos, Vector2 endPos) { //判断起点终点是否合法,这里是不能超过地图 if (startPos.x >= maxWidth || startPos.y >= maxHight || startPos.x < 0 || startPos.y < 0 || endPos.x >= maxWidth || endPos.y >= maxHight || endPos.x < 0 || endPos.y < 0) { Debug.Log("起点或终点可能已超过地图大小"); return null; } AstarNode starNode = nodes[(int)startPos.x, (int)startPos.y]; AstarNode endNode = nodes[(int)endPos.x, (int)endPos.y]; //这里拿到格子信息后继续判断格子是否合法,起点或终点格子不能是障碍物 if (starNode.nodeType == NodeType.Stop || endNode.nodeType == NodeType.Stop) { Debug.Log("起点或终点可能是障碍物"); return null; } //此方法可能多次调用,所以要清空开启和关闭列表 openList.Clear(); closeList.Clear(); //初始化起点信息,父对象与寻路消耗 并加入关闭列表 starNode.parent = null; starNode.f = 0; starNode.h = 0; starNode.g = 0; closeList.Add(starNode); //-----------------------------这里是在场景中改变其颜色,标记出起点和终点的位置 for (int i = 0; i < nodeObj.Count; i++) { if (new Vector2(starNode.x * 105, starNode.y * 105) == nodeObj[i].anchoredPosition) { nodeObj[i].GetComponent<Image>().color = Color.red; continue; } if (new Vector2(endNode.x * 105, endNode.y * 105) == nodeObj[i].anchoredPosition) { nodeObj[i].GetComponent<Image>().color = Color.blue; continue; } } //----------------------------- //死循环开始分别计算起点周围8个方位的格子, while (true) { //top CheckNode(starNode.x, starNode.y + 1, 1, starNode, endNode); //down CheckNode(starNode.x, starNode.y - 1, 1, starNode, endNode); //left CheckNode(starNode.x - 1, starNode.y, 1, starNode, endNode); //right CheckNode(starNode.x + 1, starNode.y, 1, starNode, endNode); //topleft CheckNode(starNode.x - 1, starNode.y - 1, 1.4f, starNode, endNode); //downleft CheckNode(starNode.x - 1, starNode.y + 1, 1.4f, starNode, endNode); //topright CheckNode(starNode.x + 1, starNode.y + 1, 1.4f, starNode, endNode); //downright CheckNode(starNode.x + 1, starNode.y - 1, 1.4f, starNode, endNode); //死路判断,当开启列表为空时,说明已经找完了地图上所有的格子,此时便是死路,所有退出循环 if (openList.Count == 0)return null; //对开启列表进行排序,找到消耗最小的格子 openList.Sort(SortOpenList); //将其加入关闭列表 closeList.Add(openList[0]); //设置为下一个起点 starNode = openList[0]; //并从开启列表里 移除 openList.RemoveAt(0); //结束判断,如果当前起点等于终点,说明已经寻路完成,开始导出正确路径 if (starNode == endNode) { //这里的思路是新建一个格子列表,终点开始,向其中加入父对象,加入后变更终点对象,以此根据父对象找到正确的路径 //切忌不可使用关闭列表里的格子当做路径,因为关闭列表里除了有正确的路径还有可能存在其它路径 List<AstarNode> path = new List<AstarNode>(); path.Add(endNode); while (endNode.parent != null) { path.Add(endNode.parent); endNode = endNode.parent; } //这里反转数组是因为我们是从最后一个开始找的,如:6 5 4 3 2 1 所以反转过来就是正确的路径 path.Reverse(); //----------------------------------------------这里是在场景中绘制出正确的路径 for (int i = 0; i < path.Count; i++) { for (int j = 0; j < nodeObj.Count; j++) { if (new Vector2(path[i].x * 105, path[i].y * 105) == nodeObj[j].anchoredPosition) { if (nodeObj[j].GetComponent<Image>().color == Color.white) { nodeObj[j].GetComponent<Image>().color = Color.yellow; continue; } } } } //---------------------------------------------- //最后返回路径格子信息 return path; } } } /// /// 查找某个点周围的格子消耗信息,并加入开启列表 /// /// 坐标X /// 坐标Y /// 拟消耗值 /// 其父对象 /// 终点格子信息 public void CheckNode(int x, int y, float g, AstarNode parent, AstarNode endNode) { //node合法判断,是否超出边界 if (x < 0 || x >= maxWidth || y < 0 || y >= maxHight) return; AstarNode node = nodes[x, y]; //node合法判断,是否为空,是否为障碍物,是否已经存在于Openlist或Closelist if (node == null || node.nodeType == NodeType.Stop || openList.Contains(node) || closeList.Contains(node)) return; //赋予父节点 node.parent = parent; //计算自己的g node.g = parent.g + g; //计算自己的h 为终点的x,y减去自己的x,y相加,这里取绝对值,因为可能是负数 node.h = Mathf.Abs(endNode.x - node.x) + Mathf.Abs(endNode.y - node.y); //计算最终消耗 node.f = node.g + node.h; //加入开启列表 openList.Add(node);
管理员