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;另外当路径开始规划的时候,辅助路线不能走除了主路径的当前点和下一个点,辅助路径也不能走主路径已经走过的路口点的一定范围,也就是说,除了去到主路径的下一个路口点的周围外,主路径线条的左右一定范围内是没有辅助路径的,你给我修改一下,完整的脚本代码和中文注释给我