COPY NAV导航网格寻路(6) -- 寻路实现

本文介绍了一种使用A*算法在三角形网格中寻找最短路径的方法,详细解释了如何构建网格路径,并提供了关键代码片段。通过计算穿入边和穿出边的中点距离作为节点花费,以及终点到路径终点的x和y方向的距离作为估价函数,实现了高效路径搜索。

前面已经介绍了寻路的方法,现在给出我的一个实现。

A*寻找网格路径

A*算法就不说了,网上很多,这里只说下三角形网格如何使用A*算法,如下图,绿色直线代表最终路径和方向,路径线进入三角形的边称为穿入边,路径线出去的边称为穿出边。每个三角形的花费(g值)采用穿入边和穿出边的中点的距离(图中红线),至于估价函数(h值)使用该三角形的中心点(3个顶点的平均值)到路径终点的x和y方向的距离。

NAV导航网格寻路(6) -- 寻路实现 - 竹石 - 游戏...

下面只贴出关键代码

private var m_CellVector:Vector.<Cell>;
  
         private var openList:Heap;     //二叉堆
         private var closeList:Array;

 /**
         * 构建网格路径,该方法生成closeList
         * @param startCell 起始点所在网格
         * @param startPos 起始点坐标
         * @param endCell 终点所在网格
         * @param endPos 终点坐标
         * @return 
         */

 public function buildPath(startCell:Cell, startPos:Vector2f, 
                                  endCell:Cell, endPos:Vector2f):void{
            openList.clear();
            closeList.length = 0;
            
            openList.put(endCell);
            endCell.f = 0;
            endCell.h = 0;
            endCell.isOpen = false;
            endCell.parent = null;
            endCell.sessionId = pathSessionId;
            
            var foundPath:Boolean = false;        //是否找到路径
            var currNode:Cell;                //当前节点
            var adjacentTmp:Cell = null;    //当前节点的邻接三角型
            while (openList.size > 0) {
                // 1. 把当前节点从开放列表删除, 加入到封闭列表
                currNode = openList.pop();
                closeList.push(currNode);
                
                //路径是在同一个三角形内
                if (currNode == startCell) {
                    foundPath = true;
                    break;
                }
                
                // 2. 对当前节点相邻的每一个节点依次执行以下步骤:
                //所有邻接三角型
                var adjacentId:int;
                for (var i:int=0; i<3; i++) {
                    adjacentId = currNode.links[i];
                    // 3. 如果该相邻节点不可通行或者该相邻节点已经在封闭列表中,
                    //    则什么操作也不执行,继续检验下一个节点;
                    if (adjacentId < 0) {                        //不能通过
                        continue;
                    } else {
                        adjacentTmp = m_CellVector[adjacentId];            //m_CellVector 保存所有网格的数组
                    }
                    
                    if (adjacentTmp != null) {
                        if (adjacentTmp.sessionId != pathSessionId) {
                            // 4. 如果该相邻节点不在开放列表中,则将该节点添加到开放列表中, 
                            //    并将该相邻节点的父节点设为当前节点,同时保存该相邻节点的G和F值;
                            adjacentTmp.sessionId = pathSessionId;
                            adjacentTmp.parent = currNode;
                            adjacentTmp.isOpen = true;
                            
                            //H和F值
                            adjacentTmp.computeHeuristic(startPos);

                     //m_WallDistance 是保存三角形各边中点连线的距离,共3个
                            adjacentTmp.f = currNode.f + adjacentTmp.m_WallDistance[Math.abs(i - currNode.m_ArrivalWall)];
                            
                            //放入开放列表并排序
                            openList.put(adjacentTmp);
                            
                            // remember the side this caller is entering from
                            adjacentTmp.setAndGetArrivalWall(currNode.index);
                        } else {
                            // 5. 如果该相邻节点在开放列表中, 
                            //    则判断若经由当前节点到达该相邻节点的G值是否小于原来保存的G值,
                            //    若小于,则将该相邻节点的父节点设为当前节点,并重新设置该相邻节点的G和F值
                            if (adjacentTmp.isOpen) {//已经在openList中
                                if (currNode.f + adjacentTmp.m_WallDistance[Math.abs(i - currNode.m_ArrivalWall)] < adjacentTmp.f) {
                                    adjacentTmp.f = currNode.f;
                                    adjacentTmp.parent = currNode;
                                    
                                    // remember the side this caller is entering from
                                    adjacentTmp.setAndGetArrivalWall(currNode.index);
                                }
                            } else {//已在closeList中
                                adjacentTmp = null;
                                continue;
                            }
                        }
                    }
                }
            }

}

 

由close list取出网格路径

/**
         * 路径经过的网格
         * @return 
         */        
        private function getCellPath():Vector.<Cell> {
            var pth:Vector.<Cell> = new Vector.<Cell>();
            
            var st:Cell = closeList[closeList.length-1];
            pth.push(st);
                        
            while (st.parent != null) {
                pth.push(st.parent);
                st = st.parent;
            }
            return pth;
        }

 

根据网格路径计算路径点

算法前面已经详细说明,以下是代码

 /**
         * 根据经过的三角形返回路径点(下一个拐角点法)
         * @param start 
         * @param end 
         * @return Point数组
         */        
        private function getPath(start:Vector2f, end:Vector2f):Array {
            //经过的三角形
            var cellPath:Vector.<Cell> = getCellPath();
            //没有路径
            if (cellPath == null || cellPath.length == 0) {
                return null;
            }
            
            //保存最终的路径(Point数组)
            var pathArr:Array = new Array();
            
            //开始点
            pathArr.push(start.toPoint());    
            //起点与终点在同一三角形中
            if (cellPath.length == 1) {        
                pathArr.push(end.toPoint());    //结束点
                return pathArr;
            }
            
            //获取路点
            var wayPoint:WayPoint = new WayPoint(cellPath[0], start);
            while (!wayPoint.position.equals(end)) {
                wayPoint = this.getFurthestWayPoint(wayPoint, cellPath, end);
                pathArr.push(wayPoint.position);
            }
            
            return pathArr;
        }
        
        /**
         * 下一个拐点
         * @param wayPoint 当前所在路点
         * @param cellPath 网格路径
         * @param end 终点
         * @return 
         */        
        private function getFurthestWayPoint(wayPoint:WayPoint, cellPath:Vector.<Cell>, end:Vector2f):WayPoint {
            var startPt:Vector2f = wayPoint.position;    //当前所在路径点
            var cell:Cell = wayPoint.cell;
            var lastCell:Cell = cell;
            var startIndex:int = cellPath.indexOf(cell);    //开始路点所在的网格索引
            var outSide:Line2D = cell.sides[cell.m_ArrivalWall];    //路径线在网格中的穿出边
            var lastPtA:Vector2f = outSide.getPointA();
            var lastPtB:Vector2f = outSide.getPointB();
            var lastLineA:Line2D = new Line2D(startPt, lastPtA);
            var lastLineB:Line2D = new Line2D(startPt, lastPtB);
            var testPtA:Vector2f, testPtB:Vector2f;        //要测试的点
            for (var i:int=startIndex+1; i<cellPath.length; i++) {
                cell = cellPath[i];
                outSide = cell.sides[cell.m_ArrivalWall];
                if (i == cellPath.length-1) {
                    testPtA = end;
                    testPtB = end;
                } else {
                    testPtA = outSide.getPointA();
                    testPtB = outSide.getPointB();
                }
                
                if (!lastPtA.equals(testPtA)) {
                    if (lastLineB.classifyPoint(testPtA, EPSILON) == PointClassification.RIGHT_SIDE) {
                        //路点
                        return new WayPoint(lastCell, lastPtB);
                    } else {
                        if (lastLineA.classifyPoint(testPtA, EPSILON) != PointClassification.LEFT_SIDE) {
                            lastPtA = testPtA;
                            lastCell = cell;
                            //重设直线
                            lastLineA.setPointB(lastPtA);
                        }
                    }
                }
                
                if (!lastPtB.equals(testPtB)) {
                    if (lastLineA.classifyPoint(testPtB, EPSILON) == PointClassification.LEFT_SIDE) {
                        //路径点
                        return new WayPoint(lastCell, lastPtA);
                    } else {
                        if (lastLineB.classifyPoint(testPtB, EPSILON) != PointClassification.RIGHT_SIDE) {
                            lastPtB = testPtB;
                            lastCell = cell;
                            //重设直线
                            lastLineB.setPointB(lastPtB);
                        }
                    }
                }
            }
            return new WayPoint(cellPath[cellPath.length-1], end);    //终点
        }

using System.Collections.Generic; using System.Linq; using UnityEngine; using UnityEngine.AI; using TMPro; using static ControlTheSceneXF; public class PathManager : MonoBehaviour { [Header("寻路设置")] public GameObject mainArrowPrefab; // 主箭头预制体 public GameObject secondaryArrowPrefab; // 辅助箭头预制体 public float moveSpeed = 3.5f; // 箭头移动速度 public float arrivalThreshold = 1f; // 到达点的阈值距离 public float pathHeight = 0.5f; // 路径离地面的高度 private bool isUpdate = false; [Header("路径查找设置")] public float secondaryPathRange = 2.0f; // 辅助路径查找范围 public int maxJunctionsPerSegment = 3; // 每段路径最多添加的路口点数量 [Header("寻路标签设置")] public string pathPointTag = "PathPoint"; // 路径点标签 public string junctionPointTag = "JunctionPoint"; // 路口点标签 public LayerMask pathPointLayer; // 路径点Layer public bool useTagInsteadOfLayer = true; // 使用标签而不是Layer [Header("路径可视化")] public bool showPathsInGame = true; // 是否显示路径 public Material mainPathMaterial; // 主路径材质 public Material secondaryPathMaterial; // 辅助路径材质 public float pathWidth = 0.2f; // 路径宽度 [Header("显示调试Gizmos")] public bool showDebugGizmos = true; // 显示调试Gizmos public bool logDebugInfo = true; // 打印调试信息 public float navMeshSampleDistance = 5f; // 导航网格采样距离 // 路径数据 private List<Transform> pathPoints = new List<Transform>(); private List<ArrowController> activeArrows = new List<ArrowController>(); private Dictionary<ArrowController, LineRenderer> arrowPathRenderers = new Dictionary<ArrowController, LineRenderer>(); private Dictionary<Vector3, List<Vector3>> junctionConnections = new Dictionary<Vector3, List<Vector3>>(); // 主路径和辅助路径 private List<Vector3> mainPath = new List<Vector3>(); private Dictionary<Vector3, List<List<Vector3>>> secondaryPaths = new Dictionary<Vector3, List<List<Vector3>>>(); private ArrowController mainArrow; void Update() { // 更新所有箭头 foreach (var arrow in activeArrows.ToArray()) { if (arrow != null) { arrow.UpdateMovement(); // 更新路径渲染 if (showPathsInGame && arrowPathRenderers.ContainsKey(arrow)) { UpdatePathRenderer(arrow, arrowPathRenderers[arrow]); } // 检查主箭头是否到达路径点 if (arrow == mainArrow && mainPath.Count > 0) { int currentIndex = arrow.CurrentPathIndex; if (currentIndex >= 0 && currentIndex < mainPath.Count) { Vector3 pathPoint = mainPath[currentIndex]; float distance = Vector3.Distance(arrow.transform.position, pathPoint); // 当接近路径点时检查辅助路径 if (distance < arrivalThreshold * 1.5f) { // 检查并激活辅助路径 CheckAndActivateSecondaryPaths(pathPoint); } } } } else { if (arrowPathRenderers.ContainsKey(arrow)) { Destroy(arrowPathRenderers[arrow].gameObject); arrowPathRenderers.Remove(arrow); } activeArrows.Remove(arrow); } } } /// <summary> /// 更新路径渲染器,逐步绘制路径 /// </summary> void UpdatePathRenderer(ArrowController arrow, LineRenderer lineRenderer) { if (arrow == null || lineRenderer == null) return; // 获取箭头已经经过的路径点 int pointsToShow = Mathf.Min(arrow.CurrentPathIndex + 1, arrow.PathPoints.Count); if (pointsToShow < 2) return; // 创建临时列表存储要显示的点 List<Vector3> points = new List<Vector3>(); points.AddRange(arrow.PathPoints.GetRange(0, pointsToShow)); // 添加当前箭头位置作为最后一个点 points.Add(arrow.transform.position); // 更新LineRenderer lineRenderer.positionCount = points.Count; lineRenderer.SetPositions(points.ToArray()); } /// <summary> /// 收集所有路径点并按时间排序 /// </summary> void CollectPathPoints() { pathPoints.Clear(); List<Transform> allPathPoints = FindAllPathPoints(); if (allPathPoints.Count == 0) { Debug.LogError("没有找到路径点!"); enabled = false; return; } // 按时间排序路径点 pathPoints = allPathPoints .Where(p => p.GetComponent<GjImagePrefab>() != null) .OrderBy(p => p.GetComponent<GjImagePrefab>().GetTimeInMinutes()) .ToList(); } /// <summary> /// 查找场景中所有路径点 /// </summary> List<Transform> FindAllPathPoints() { List<Transform> points = new List<Transform>(); if (useTagInsteadOfLayer) { // 通过标签查找路径点 GameObject[] taggedObjects = GameObject.FindGameObjectsWithTag(pathPointTag); foreach (GameObject obj in taggedObjects) { points.Add(obj.transform); ValidatePointPosition(obj.transform); } // 通过标签查找路口点并构建连接图 GameObject[] junctionObjects = GameObject.FindGameObjectsWithTag(junctionPointTag); BuildJunctionConnectionGraph(junctionObjects); } else { // 通过Layer查找(略) } return points; } /// <summary> /// 构建路口连接图 /// </summary> void BuildJunctionConnectionGraph(GameObject[] junctions) { junctionConnections.Clear(); // 初始化连接图 foreach (var junction in junctions) { Vector3 pos = junction.transform.position; if (!junctionConnections.ContainsKey(pos)) { junctionConnections.Add(pos, new List<Vector3>()); } } // 添加连接 foreach (var junction in junctions) { JunctionPoint junctionComp = junction.GetComponent<JunctionPoint>(); if (junctionComp != null && junctionComp.connectedPoints != null) { Vector3 startPos = junction.transform.position; foreach (var connectedPoint in junctionComp.connectedPoints) { if (connectedPoint != null) { Vector3 endPos = connectedPoint.position; if (!junctionConnections[startPos].Contains(endPos)) { junctionConnections[startPos].Add(endPos); } // 确保双向连接 if (junctionConnections.ContainsKey(endPos) && !junctionConnections[endPos].Contains(startPos)) { junctionConnections[endPos].Add(startPos); } } } } } } /// <summary> /// 验证路径点是否在导航网格上 /// </summary> void ValidatePointPosition(Transform point) { NavMeshHit hit; if (!NavMesh.SamplePosition(point.position, out hit, navMeshSampleDistance, NavMesh.AllAreas)) { Debug.LogWarning($"路径点 '{point.name}' 不在导航网格上! 位置: {point.position}"); if (NavMesh.FindClosestEdge(point.position, out hit, NavMesh.AllAreas)) { point.position = hit.position; Debug.Log($"调整 '{point.name}' 到最近的导航网格点: {hit.position}"); } } } /// <summary> /// 计算主路径 /// </summary> public void CalculateMainPath() { mainPath.Clear(); secondaryPaths.Clear(); if (pathPoints.Count < 2) { Debug.LogWarning("路径点数不足"); return; } // 1. 计算基础路径 - 直接连接所有PathPoint的最短路径 mainPath = CalculatePathThroughAllPoints(pathPoints); if (mainPath == null || mainPath.Count < 2) { Debug.LogError("主路径计算失败!"); return; } // 2. 在关键路口点预计算辅助路径 PrecalculateSecondaryPaths(); } /// <summary> /// 预计算所有可能的分支路径(核心修改) /// </summary> void PrecalculateSecondaryPaths() { secondaryPaths.Clear(); // 遍历主路径上的每一段(从当前点到下一个点) for (int i = 0; i < mainPath.Count - 1; i++) { Vector3 currentPoint = mainPath[i]; Vector3 nextPoint = mainPath[i + 1]; // 计算当前段的直径范围 Vector3 segmentCenter = (currentPoint + nextPoint) / 2f; float segmentDiameter = Vector3.Distance(currentPoint, nextPoint); // 查找范围内的所有路口点 List<Vector3> nearbyJunctions = FindNearbyJunctions(segmentCenter, segmentDiameter * secondaryPathRange); if (logDebugInfo) Debug.Log($"主路径段 {i}: {currentPoint} -> {nextPoint}, 找到{nearbyJunctions.Count}个附近路口点"); List<List<Vector3>> alternatives = new List<List<Vector3>>(); // 尝试为每个路口点创建替代路径 foreach (var junction in nearbyJunctions) { // 跳过主路径点本身 if (junction == currentPoint || junction == nextPoint) continue; // 计算替代路径:当前点 -> 路口点 -> 下一个点 List<Vector3> altPath = CalculateAlternativePath(currentPoint, junction, nextPoint); if (altPath != null && altPath.Count > 1) { alternatives.Add(altPath); if (logDebugInfo) Debug.Log($"创建替代路径: {currentPoint} -> {junction} -> {nextPoint}, 点数: {altPath.Count}"); // 限制每段路径的最大分支数 if (alternatives.Count >= maxJunctionsPerSegment) break; } } if (alternatives.Count > 0) { // 使用当前点作为键存储替代路径 secondaryPaths[currentPoint] = alternatives; if (logDebugInfo) Debug.Log($"为点 {currentPoint} 添加 {alternatives.Count} 条替代路径"); } } } /// <summary> /// 查找范围内的路口点 /// </summary> List<Vector3> FindNearbyJunctions(Vector3 center, float radius) { List<Vector3> nearby = new List<Vector3>(); foreach (var junction in junctionConnections.Keys) { if (Vector3.Distance(junction, center) <= radius) { nearby.Add(junction); } } return nearby; } /// <summary> /// 计算替代路径(当前点 -> 路口点 -> 目标点) /// </summary> List<Vector3> CalculateAlternativePath(Vector3 start, Vector3 junction, Vector3 end) { // 计算三段路径 List<Vector3> pathToJunction = CalculatePath(start, junction); List<Vector3> pathFromJunctionToEnd = CalculatePath(junction, end); if (pathToJunction == null || pathToJunction.Count == 0 || pathFromJunctionToEnd == null || pathFromJunctionToEnd.Count == 0) { return null; } // 合并路径 List<Vector3> fullPath = new List<Vector3>(); fullPath.AddRange(pathToJunction); fullPath.AddRange(pathFromJunctionToEnd.Skip(1)); // 跳过重复点 return fullPath; } /// <summary> /// 检查并激活辅助路径 /// </summary> void CheckAndActivateSecondaryPaths(Vector3 junctionPoint) { if (!secondaryPaths.ContainsKey(junctionPoint)) { if (logDebugInfo) Debug.Log($"点 {junctionPoint} 没有预计算的辅助路径"); return; } // 获取该点的所有辅助路径 List<List<Vector3>> paths = secondaryPaths[junctionPoint]; if (logDebugInfo) Debug.Log($"在点 {junctionPoint} 找到 {paths.Count} 条辅助路径"); foreach (var path in paths) { if (path == null || path.Count < 2) { if (logDebugInfo) Debug.LogWarning("跳过无效的辅助路径"); continue; } // 检查是否已存在相同路径的箭头 bool pathExists = false; foreach (var arrow1 in activeArrows) { if (arrow1 == null || arrow1.IsMainArrow) continue; // 简单比较路径起点和终点 if (Vector3.Distance(arrow1.StartPosition, path[0]) < 0.1f && Vector3.Distance(arrow1.EndPosition, path[path.Count - 1]) < 0.1f) { pathExists = true; break; } } if (pathExists) { if (logDebugInfo) Debug.Log($"辅助路径已存在: {path[0]} -> {path[path.Count - 1]}"); continue; } // 创建辅助箭头 GameObject arrowObj = Instantiate(secondaryArrowPrefab, path[0], Quaternion.identity); ArrowController arrow = arrowObj.GetComponent<ArrowController>(); if (arrow == null) { arrow = arrowObj.AddComponent<ArrowController>(); } arrow.Initialize(this, path, false); // 设置箭头材质 var renderer = arrowObj.GetComponent<MeshRenderer>(); if (renderer != null && secondaryPathMaterial != null) { renderer.material = secondaryPathMaterial; } activeArrows.Add(arrow); // 创建辅助路径渲染器 if (showPathsInGame) { CreatePathRenderer(arrow, path, secondaryPathMaterial); } if (logDebugInfo) Debug.Log($"创建辅助路径箭头: {path[0]} -> {path[path.Count - 1]}, 点数: {path.Count}"); } } /// <summary> /// 计算经过所有路径点的完整路径 /// </summary> List<Vector3> CalculatePathThroughAllPoints(List<Transform> points) { if (points.Count < 2) return null; List<Vector3> fullPath = new List<Vector3>(); for (int i = 0; i < points.Count - 1; i++) { Vector3 start = points[i].position; Vector3 end = points[i + 1].position; // 计算两点之间的路径 List<Vector3> segment = CalculatePath(start, end); if (segment == null || segment.Count == 0) { Debug.LogWarning($"路径段 {i} 计算失败,在 {points[i].name} 和 {points[i + 1].name} 之间"); return null; } // 如果是第一个段,添加全部点 if (i == 0) { fullPath.AddRange(segment); } else { // 移除重复点(上一段的终点) if (fullPath.Count > 0) fullPath.RemoveAt(fullPath.Count - 1); fullPath.AddRange(segment); } } return fullPath; } /// <summary> /// 计算两点之间的导航路径 /// </summary> List<Vector3> CalculatePath(Vector3 startPos, Vector3 endPos) { NavMeshHit hitStart, hitEnd; if (!NavMesh.SamplePosition(startPos, out hitStart, navMeshSampleDistance, NavMesh.AllAreas) || !NavMesh.SamplePosition(endPos, out hitEnd, navMeshSampleDistance, NavMesh.AllAreas)) { Debug.LogError($"点不在导航网格上!起点: {startPos}, 终点: {endPos}"); return null; } NavMeshPath navPath = new NavMeshPath(); if (!NavMesh.CalculatePath(hitStart.position, hitEnd.position, NavMesh.AllAreas, navPath)) { Debug.LogError($"路径计算失败!起点: {startPos}, 终点: {endPos}"); return null; } // 调整路径高度 List<Vector3> adjustedPath = new List<Vector3>(); foreach (Vector3 point in navPath.corners) { adjustedPath.Add(new Vector3(point.x, point.y + pathHeight, point.z)); } return adjustedPath; } /// <summary> /// 创建主箭头 /// </summary> void CreateMainArrow() { if (mainArrowPrefab == null) { Debug.LogError("未设置主箭头预制体!"); return; } // 清除现有箭头 foreach (var arrow in activeArrows) if (arrow != null) Destroy(arrow.gameObject); activeArrows.Clear(); foreach (var renderer1 in arrowPathRenderers.Values) if (renderer1 != null) Destroy(renderer1.gameObject); arrowPathRenderers.Clear(); if (mainPath == null || mainPath.Count == 0) return; // 创建主箭头 GameObject arrowObj = Instantiate(mainArrowPrefab, mainPath[0], Quaternion.identity); mainArrow = arrowObj.GetComponent<ArrowController>(); if (mainArrow == null) { mainArrow = arrowObj.AddComponent<ArrowController>(); } mainArrow.Initialize(this, mainPath, true); // 设置箭头材质 var renderer = arrowObj.GetComponent<MeshRenderer>(); if (renderer != null && mainPathMaterial != null) { renderer.material = mainPathMaterial; } activeArrows.Add(mainArrow); // 创建主路径渲染器 if (showPathsInGame) { CreatePathRenderer(mainArrow, mainPath, mainPathMaterial); } } /// <summary> /// 创建路径渲染器 /// </summary> void CreatePathRenderer(ArrowController arrow, List<Vector3> path, Material material) { if (path == null || path.Count < 2) return; GameObject pathObj = new GameObject("PathRenderer"); pathObj.transform.SetParent(transform); LineRenderer lineRenderer = pathObj.AddComponent<LineRenderer>(); lineRenderer.material = material; lineRenderer.startWidth = pathWidth; lineRenderer.endWidth = pathWidth; lineRenderer.useWorldSpace = true; // 初始只设置第一个点 lineRenderer.positionCount = 1; lineRenderer.SetPosition(0, path[0]); arrowPathRenderers[arrow] = lineRenderer; } /// <summary> /// 手动重新计算路径 /// </summary> public void RecalculatePaths() { isUpdate = true; ClearAllPaths(); CollectPathPoints(); CalculateMainPath(); CreateMainArrow(); } /// <summary> /// 清除所有路径和箭头 /// </summary> public void ClearAllPaths() { isUpdate = false; foreach (var arrow in activeArrows) if (arrow != null) Destroy(arrow.gameObject); activeArrows.Clear(); pathPoints.Clear(); junctionConnections.Clear(); mainPath.Clear(); secondaryPaths.Clear(); } void OnDrawGizmos() { if (!showDebugGizmos) return; // 绘制路径点 Gizmos.color = Color.cyan; foreach (Transform point in pathPoints) { Gizmos.DrawSphere(point.position, 0.5f); Gizmos.DrawWireSphere(point.position, navMeshSampleDistance); } // 绘制路口点 Gizmos.color = Color.magenta; foreach (var junction in junctionConnections.Keys) { Gizmos.DrawSphere(junction, 0.3f); // 绘制路口连接 if (junctionConnections.ContainsKey(junction)) { foreach (var connectedPoint in junctionConnections[junction]) { Gizmos.DrawLine(junction, connectedPoint); } } } // 绘制主路径 if (mainPath.Count > 1) { Gizmos.color = Color.green; for (int i = 1; i < mainPath.Count; i++) { Gizmos.DrawLine(mainPath[i - 1], mainPath[i]); } } // 绘制辅助路径 Gizmos.color = Color.yellow; foreach (var kvp in secondaryPaths) { foreach (var path in kvp.Value) { for (int i = 1; i < path.Count; i++) { Gizmos.DrawLine(path[i - 1], path[i]); } } } } } using System.Collections.Generic; using TMPro; using UnityEngine; public class ArrowController : MonoBehaviour { private List<Vector3> currentPath; private int currentCornerIndex = 0; private float moveSpeed; private float arrivalThreshold; private bool isMainArrow; private PathManager pathManager; private List<Vector3> pathPoints; // Public properties for path checking public bool IsMainArrow => isMainArrow; public Vector3 StartPosition => pathPoints != null && pathPoints.Count > 0 ? pathPoints[0] : Vector3.zero; public Vector3 EndPosition => pathPoints != null && pathPoints.Count > 0 ? pathPoints[pathPoints.Count - 1] : Vector3.zero; public List<Vector3> PathPoints => pathPoints; public int CurrentPathIndex => currentCornerIndex; public void Initialize(PathManager manager, List<Vector3> path, bool isMain) { pathPoints = new List<Vector3>(path); // Store a copy of the path points pathManager = manager; moveSpeed = manager.moveSpeed; arrivalThreshold = manager.arrivalThreshold; isMainArrow = isMain; SetPath(path); } public void SetPath(List<Vector3> path) { currentPath = path; currentCornerIndex = 0; if (currentPath != null && currentPath.Count > 0) transform.position = currentPath[0]; } public void UpdateMovement() { if (currentPath == null || currentPath.Count == 0) return; // Reached end of path if (currentCornerIndex >= currentPath.Count) { // Secondary arrows disappear when reaching the end if (!isMainArrow) { Destroy(gameObject); } else { // Main arrow loops currentCornerIndex = 0; transform.position = currentPath[0]; } return; } Vector3 targetPoint = currentPath[currentCornerIndex]; transform.position = Vector3.MoveTowards(transform.position, targetPoint, moveSpeed * Time.deltaTime); // Update arrow direction if (transform.position != targetPoint) { Vector3 direction = -(targetPoint - transform.position).normalized; if (direction != Vector3.zero) { transform.rotation = Quaternion.LookRotation(direction); } } // Check if reached current waypoint if (Vector3.Distance(transform.position, targetPoint) <= arrivalThreshold) currentCornerIndex++; } } 这两个脚本你给我修改一下,首先是主路径一定要结合navigation,先看从起点到终点的最短路线(这个不需要划线),然后把这条路线左右一定范围内的路口点和途径点用线条连接起来作为主路径线条(划线要)不论是主路径线条还是辅助路径线条都要结合navigation;另外当路径开始规划的时候,辅助路线不能走除了主路径的当前点和下一个点,辅助路径也不能走主路径已经走过的路口点的一定范围,也就是说,除了去到主路径的下一个路口点的周围外,主路径线条的左右一定范围内是没有辅助路径的,你给我修改一下,完整的脚本代码和中文注释给我
06-07
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值