|
第一步:新建3d项目 第二步: Hierarchy->点击+号->Creat Empty 第三步:新建C类文件,命名为AStarPathFinding,拖动到物体上。 第四步:添加障碍物,运行 AStarPathFinding.cs代码如下 using System.Collections;using System.Collections.Generic;using System.Linq;using UnityEngine;public class Node{ public int x; public int y; public float walkedDistance;//走过的距离 public float functionDistance;//函数距离 public Node parent;//父节点,用于最后绘制路径。}public class AStarPathFinding : MonoBehaviour{ [Header("length表示有多少列,width表示有多少行")] public int length = 20; public int width = 20; [Header("设定起始位置和结束位置")] public Vector2Int startPos = new Vector2Int(0, 0); public Vector2Int endPos = new Vector2Int(5, 5); [Header("设置障碍物位置")] public List<vector2int> obstacles = new List<vector2int>(); [Header("可否斜向移动?是的话使用欧几里得距离,否则使用曼哈顿距离")] public bool canMoveDiagonally; [Header("不使用走过的距离(贪心)")] public bool dontUseWalkedDistance; public float animInterval = 0.5f; private Node[,] nodes; private GameObject[,] cubes; private List<vector2int> openList = new List<vector2int>();//开放列表 public HashSet<vector2int> closedList = new HashSet<vector2int>();//关闭列表 private Vector2Int currentPos; private GameObject mark; void Start() { //初始化数据结构 nodes = new Node[length, width]; cubes = new GameObject[length, width]; //障碍物添加到关闭列表 foreach (var obstacle in obstacles) { closedList.Add(obstacle); } //可视化地图 for (int i = 0; i < nodes.GetLength(0); i++) { for (int j = 0; j < nodes.GetLength(1); j++) { nodes[i, j] = new Node(); nodes[i, j].x = i; nodes[i, j].y = j; //创建方块 GameObject cubeGo = GameObject.CreatePrimitive(PrimitiveType.Cube); cubeGo.transform.position = new Vector3(i + 0.5f, 0, j + 0.5f);//设定位置 cubes[i, j] = cubeGo; if (obstacles.Contains(new Vector2Int(i, j))) { var renderer = cubeGo.GetComponent<renderer>(); renderer.material.color = Color.red; } } } cubes[endPos.x, endPos.y].GetComponent<renderer>().material.color = Color.yellow;//终点设置为黄色 //初始位置也需要计算,避免已经在目标位置时还进行寻路.如果不添加的话一开始就在终点的情况下会先向外走一格再走回终点 openList.Add(startPos); //同时需要计算预测距离,不然默认为0,之后会一直找到初始起点而无法前进 UpdateFunctionDistance(nodes[startPos.x, startPos.y]); nodes[startPos.x, startPos.y].walkedDistance = 0; currentPos = startPos; //开始寻路 StartCoroutine(SearchNeighbour()); } //搜寻附近可走位置 IEnumerator SearchNeighbour() { //寻找周围可走格子并放入开放列表 for (int i = -1; i < 2; i++) { for (int j = -1; j < 2; j++) { var distance = Vector2.SqrMagnitude(new Vector2(i, j)); if (!canMoveDiagonally) { //如果不能斜向走就忽略掉斜向的格子,判断斜向的格子是通过平方向量长度是否大于1来判断的 if (distance > 1) { continue; } } if (i == 0 && j == 0)//自己不能重复加入开放列表 continue; if (currentPos.x + i < 0 || currentPos.x + i > length - 1 || currentPos.y + j < 0 || currentPos.y + j > width - 1)//越界判断 { continue; } var v2I = currentPos + new Vector2Int(i, j); if (closedList.Contains(v2I) || openList.Contains(v2I))//不能再次加入关闭列表和开放列表中的节点 { continue; } //更新父节点和走过的距离 nodes[v2I.x, v2I.y].walkedDistance = nodes[currentPos.x, currentPos.y].walkedDistance + distance; nodes[v2I.x, v2I.y].parent = nodes[currentPos.x, currentPos.y];//更新父节点 openList.Add(v2I); } } var openNodeList = new List<node>();//获取开放位置列表对应的开放node列表以根据升序排序找出最优节点 foreach (var pos in openList) { var toAdd = nodes[pos.x, pos.y]; openNodeList.Add(toAdd); //更新预估距离 UpdateFunctionDistance(toAdd); } //OpenList为空,代表所有开放节点走完了 if (openNodeList.Count == 0) { Debug.Log("全图已经遍历完成,无法到达目标位置"); yield break; } //排序 var currentNode = openNodeList.OrderBy(x => dontUseWalkedDistance ? x.functionDistance : x.functionDistance + x.walkedDistance).First(); currentPos = new Vector2Int(currentNode.x, currentNode.y); openList.Remove(new Vector2Int(currentNode.x, currentNode.y));//从开放列表中移除 closedList.Add(new Vector2Int(currentNode.x, currentNode.y));//移动到关闭列表中 cubes[currentNode.x, currentNode.y].GetComponent<renderer>().material.color = Color.black;//当前节点染色 //当前位置标记 Destroy(mark); mark = GameObject.CreatePrimitive(PrimitiveType.Cube); mark.transform.localScale = Vector3.one * 0.5f; mark.GetComponent<renderer>().material.color = Color.blue; mark.transform.position = new Vector3(currentPos.x + 0.5f, 1, currentPos.y + 0.5f); if (currentPos == endPos) { Debug.Log("找到路径"); DrawPath(); } else { yield return new WaitForSeconds(animInterval); StartCoroutine(SearchNeighbour()); } } public void UpdateFunctionDistance(Node node) { var pos = new Vector2(node.x, node.y); //计算距离 if (canMoveDiagonally)//斜向使用欧几里得距离, { node.functionDistance = Vector2.Distance(endPos, pos); } else { //曼哈顿距离 node.functionDistance = Mathf.Abs(endPos.x - pos.x) + Mathf.Abs(endPos.y - pos.y); } } void DrawPath() { var currentNode = nodes[currentPos.x, currentPos.y]; // 通过 parent 进行回溯 // 查找或创建 LineRenderer 组件 LineRenderer lineRenderer = GetComponent<linerenderer>(); if (lineRenderer == null) { lineRenderer = gameObject.AddComponent<linerenderer>(); // 如果没有 LineRenderer,则创建一个 } // 设置 LineRenderer 属性 lineRenderer.startWidth = 0.1f; // 设置起始宽度 lineRenderer.endWidth = 0.1f; // 设置结束宽度 lineRenderer.material = new Material(Shader.Find("Sprites/Default")); // 设置材质(可以自定义) lineRenderer.startColor = Color.cyan; // 设置起始颜色 lineRenderer.endColor = Color.cyan; // 设置结束颜色 lineRenderer.positionCount = 0; // 清除之前的路径点 // 回溯路径并将路径点添加到 LineRenderer List<vector3> pathPoints = new List<vector3>(); while (currentNode.parent != null) { pathPoints.Add(new Vector3(currentNode.x + 0.5f, 0.5f, currentNode.y + 0.5f)); // 添加路径点 currentNode = currentNode.parent; // 回溯到父节点 } pathPoints.Add(new Vector3(currentNode.x + 0.5f, 0.5f, currentNode.y + 0.5f)); // 添加起点 // 设置 LineRenderer 的路径点 lineRenderer.positionCount = pathPoints.Count; for (int i = 0; i < pathPoints.Count; i++) { lineRenderer.SetPosition(i, pathPoints); // 将每个路径点设置到 LineRenderer } }} </vector3></vector3></linerenderer></linerenderer></renderer></renderer></node></renderer></renderer></vector2int></vector2int></vector2int></vector2int></vector2int></vector2int> 免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作! |