当前位置:   article > 正文

Unity A星(A Star/A*)寻路算法_unity a星寻路

unity a星寻路

演示:

 我们知道Unity中的Navigation只能实现3D场景的寻路,不能实现2D的寻路,常见的寻路算法有很多种,其中A星是项目中最常用的寻路方法。在项目中用到了A星,就简单总结一下吧。

原理:

最通俗的原理就是寻找周围的点。选出一个到终点最近的点,再从选出的点为起点寻找下一个点,直到到达目标点。

实现:

如何选出最近的点呢,我们就会利用曼哈顿街区算法公式寻找下一个点。

 

如下图:我们以黄色为起点,黄色的点周围有八个可以移动的点,移动的距离对角移动为1.4,直线移动为1。

我们用f来代表寻路的总代价,g代表从开始点到下一个点的距离,h(此处用到麦哈顿街区算法)为从下一个点到目标点的距离。则f=g+h

因此我们的格子类就基本完成了;

  1. using System.Collections;
  2. using System.Collections.Generic;
  3. using UnityEngine;
  4. /// <summary>
  5. /// 格子的类型
  6. /// </summary>
  7. public enum E_Node_Type {
  8. Walk,
  9. Stop
  10. }
  11. /// <summary>
  12. /// 格子类
  13. /// </summary>
  14. public class AStarNode
  15. {
  16. //格子的坐标
  17. public int x;
  18. public int y;
  19. //寻路消耗
  20. public float f;
  21. //起点距离
  22. public float g;
  23. //终点距离
  24. public float h;
  25. //父对象
  26. public AStarNode father;
  27. public E_Node_Type type;
  28. public AStarNode( int x,int y,E_Node_Type type)
  29. {
  30. this.x = x;
  31. this.y = y;
  32. this.type = type;
  33. }
  34. }

接下来写寻路的控制器,经过分析,应该记录开始点周围的每一个点,经过计算后看看他是否是最近的点。这里我们就需要两个列表,一个列表记录格子周围的点。另一个列表我们要记录最近路径上的格子。另外我们还要一个二维数组来记录地图的信息。

  1. using System.Collections;
  2. using System.Collections.Generic;
  3. using UnityEngine;
  4. /// <summary>
  5. /// A星管理器
  6. /// </summary>
  7. public class AStarMgr : MonoBehaviour
  8. {
  9. public static AStarMgr Instance;
  10. //地图的宽高
  11. private int mapW;
  12. private int mapeH;
  13. //地图相关的所有的格子容器
  14. public AStarNode[,] nodes;
  15. //开启列表
  16. private List<AStarNode> openList=new List<AStarNode>();
  17. //关闭列表
  18. private List<AStarNode> closeList=new List<AStarNode>();
  19. private void Awake()
  20. {
  21. Instance = this;
  22. }
  23. /// <summary>
  24. /// 初始化地图信息
  25. /// </summary>
  26. /// <param name="w"></param>
  27. /// <param name="h"></param>
  28. public void InItMapInfo(int w,int h)
  29. {
  30. this.mapeH = h;
  31. this.mapW = w;
  32. nodes = new AStarNode[w,h];
  33. for (int i = 0; i < w; i++)
  34. {
  35. for (int j = 0; j < h; j++)
  36. {
  37. AStarNode node = new AStarNode(i, j, Random.Range(0, 100) < 20 ? E_Node_Type.Stop : E_Node_Type.Walk);
  38. nodes[i, j] = node;
  39. }
  40. }
  41. }
  42. /// <summary>
  43. /// 寻路的方法
  44. /// </summary>
  45. /// <param name="startPos"></param>
  46. /// <param name="endPos"></param>
  47. /// <returns></returns>
  48. public List<AStarNode>FindPath(Vector2 startPos,Vector2 endPos)
  49. {
  50. //判断起始点是不是在地图的范围内
  51. if (startPos.x < 0 || startPos.x >= mapW
  52. || startPos.y < 0 || startPos.y >= mapeH
  53. || endPos.x < 0 || endPos.x >= mapW
  54. || endPos.y < 0 || endPos.y >= mapeH
  55. )
  56. return null;
  57. //判断起始点是不是不能通行的点
  58. AStarNode start = nodes[(int)startPos.x, (int)startPos.y];
  59. AStarNode end = nodes[(int)endPos.x, (int)endPos.y];
  60. if (start.type == E_Node_Type.Stop || end.type == E_Node_Type.Stop)
  61. return null;
  62. closeList.Clear();
  63. openList.Clear();
  64. //开始点放入关闭列表中
  65. start.father = null;
  66. start.f = 0;
  67. start.g = 0;
  68. start.h = 0;
  69. closeList.Add(start);
  70. while (true)
  71. {
  72. //周围的点
  73. FindNearlyToOpenList(start.x - 1, start.y - 1, 1.4f, start, end);
  74. FindNearlyToOpenList(start.x, start.y - 1, 1.4f, start, end);
  75. FindNearlyToOpenList(start.x + 1, start.y - 1, 1.4f, start, end);
  76. FindNearlyToOpenList(start.x - 1, start.y, 1.4f, start, end);
  77. FindNearlyToOpenList(start.x + 1, start.y, 1.4f, start, end);
  78. FindNearlyToOpenList(start.x - 1, start.y + 1, 1.4f, start, end);
  79. FindNearlyToOpenList(start.x, start.y + 1, 1.4f, start, end);
  80. FindNearlyToOpenList(start.x + 1, start.y + 1, 1.4f, start, end);
  81. if (openList.Count == 0)
  82. return null;
  83. //排序选出最小的点
  84. openList.Sort(SortOpenList);
  85. //放入关闭列表,然后从开启列表中移除
  86. closeList.Add(openList[0]);
  87. //找到这个点,进行下一次寻路
  88. start = openList[0];
  89. openList.RemoveAt(0);
  90. if (start == end)
  91. {
  92. //结束
  93. List<AStarNode> path = new List<AStarNode>();
  94. path.Add(end);
  95. while (end.father != null)
  96. {
  97. path.Add(end.father);
  98. end = end.father;
  99. }
  100. path.Reverse();
  101. return path;
  102. }
  103. }
  104. }
  105. /// <summary>
  106. /// 排序函数
  107. /// </summary>
  108. /// <param name="a"></param>
  109. /// <param name="b"></param>
  110. /// <returns></returns>
  111. private int SortOpenList(AStarNode a,AStarNode b)
  112. {
  113. if (a.f > b.f)
  114. return 1;
  115. else if (a.f == b.f)
  116. return 1;
  117. else
  118. return -1;
  119. }
  120. /// <summary>
  121. /// 临近的点放入开启列表
  122. /// </summary>
  123. /// <param name="x"></param>
  124. /// <param name="y"></param>
  125. private void FindNearlyToOpenList(int x,int y,float g,AStarNode father, AStarNode end)
  126. {
  127. if (x < 0 || x >= mapW || y < 0 || y >= mapeH)
  128. return;
  129. AStarNode node = nodes[x, y];
  130. if (node == null||node.type==E_Node_Type.Stop
  131. ||closeList.Contains(node)
  132. ||openList.Contains(node)
  133. )
  134. return;
  135. //计算f值 f=g+h;
  136. node.father = father;
  137. node.g = father.g + g;
  138. node.h = Mathf.Abs(end.x - node.x) + Mathf.Abs(end.y - node.y);
  139. node.f = node.g + node.h;
  140. openList.Add(node);
  141. }
  142. }

以上的代码就完成了A星算法的核心内容。下边为测试代码:

  1. using System.Collections;
  2. using System.Collections.Generic;
  3. using UnityEngine;
  4. public class TestAStar : MonoBehaviour
  5. {
  6. public int beginX = -3;
  7. public int beginY = 5;
  8. public int offsetX = 2;
  9. public int offsetY = 2;
  10. public int mapW = 5;
  11. public int mapH = 5;
  12. private Vector2 beginPos = Vector2.right * -1;
  13. private Vector2 endPos = Vector2.right * -1;
  14. public Material red;
  15. public Material yellow;
  16. private Dictionary<string, GameObject> cubes = new Dictionary<string, GameObject>();
  17. void Start()
  18. {
  19. AStarMgr.Instance.InItMapInfo(mapW, mapW);
  20. for (int i = 0; i < mapW; i++)
  21. {
  22. for (int j = 0; j < mapH; j++)
  23. {
  24. GameObject obj = GameObject.CreatePrimitive(PrimitiveType.Cube);
  25. obj.transform.position = new Vector3(beginX + i * offsetX, beginY + j * offsetY, 0);
  26. obj.name = i + "_" + j;
  27. cubes.Add(obj.name, obj);
  28. AStarNode node = AStarMgr.Instance.nodes[i, j];
  29. if (node.type == E_Node_Type.Stop)
  30. {
  31. obj.GetComponent<MeshRenderer>().material = red;
  32. }
  33. }
  34. }
  35. }
  36. // Update is called once per frame
  37. void Update()
  38. {
  39. if (Input.GetMouseButtonDown(0))
  40. {
  41. RaycastHit hit;
  42. Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
  43. if (Physics.Raycast(ray, out hit, 1000))
  44. {
  45. if (beginPos == Vector2.right * -1)
  46. {
  47. string[] strs = hit.collider.gameObject.name.Split('_');
  48. beginPos = new Vector2(int.Parse(strs[0]), int.Parse(strs[1]));
  49. hit.collider.gameObject.GetComponent<MeshRenderer>().material = yellow;
  50. }
  51. else
  52. {
  53. string[] strs = hit.collider.gameObject.name.Split('_');
  54. endPos = new Vector2(int.Parse(strs[0]), int.Parse(strs[1]));
  55. Debug.Log(endPos);
  56. List<AStarNode> list = AStarMgr.Instance.FindPath(beginPos, endPos);
  57. Debug.Log(list.Count);
  58. if (list != null)
  59. {
  60. for (int i = 0; i < list.Count; i++)
  61. {
  62. Debug.Log(list[i]);
  63. cubes[list[i].x + "_" + list[i].y].GetComponent<MeshRenderer>().material = yellow;
  64. }
  65. }
  66. }
  67. }
  68. }
  69. }
  70. }

以上为A星寻路算法的核心理念,在用到商业项目中肯定要对算法进行封装。那下边就介绍一个已经封装完善的A星算法插件:

A* Pathfinding Project

下载免费版:点击更多信息-点击download就可以下载免费版。

 

 使用方法看这个视频吧:

Unity 2D AI自动寻路功能 [风农译制]_哔哩哔哩_bilibilihttps://www.youtube.com/watch?v=jvtFUfJ6CP8利用A* 寻路项目做的unity自动寻路。涉及到较多脚本编写,有不明白的部分可以看我之前的脚本教程。A* Pathfinding 项目地址: https://arongranberg.com/astar/https://www.bilibili.com/video/BV1D4411N7FZ

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/笔触狂放9/article/detail/339206
推荐阅读
相关标签
  

闽ICP备14008679号