画线移除循环往复的点

This commit is contained in:
jwp
2025-06-19 07:49:48 +08:00
parent b9b2472ba3
commit dc91a58fc5
2 changed files with 143 additions and 94 deletions

View File

@ -5,47 +5,48 @@ using UnityEngine;
public class PathLineAnimator : MonoBehaviour
{
private static IEnumerator enumerator;
public static void Play(
MonoBehaviour caller,
Transform origin,
Transform mover,
LineRenderer lineRenderer,
List<MapGraph.Node> unsortedNodes,
float moveSpeed = 2f)
{
if(enumerator!=null)
caller.StopCoroutine(enumerator);
List<MapGraph.Node> sortedNodes = SortNodesByGraphCost(origin.position,unsortedNodes);
List<Vector3> sortedPositions = sortedNodes.Select(n => n.position).ToList();
enumerator= DrawLineCoroutine(mover, lineRenderer, sortedPositions, moveSpeed);
caller.StartCoroutine(enumerator);
}
public static void Play(
MonoBehaviour caller,
Transform origin,
Transform mover,
LineRenderer lineRenderer,
List<Vector3> unsortedNodes,
float moveSpeed = 2f)
{
if(enumerator!=null)
caller.StopCoroutine(enumerator);
enumerator= DrawLineCoroutine(mover, lineRenderer, unsortedNodes, moveSpeed);
caller.StartCoroutine(enumerator);
}
private static IEnumerator enumerator;
public static void Play(
MonoBehaviour caller,
Transform origin,
Transform mover,
LineRenderer lineRenderer,
List<MapGraph.Node> unsortedNodes,
float moveSpeed = 2f)
{
if (enumerator != null)
caller.StopCoroutine(enumerator);
List<MapGraph.Node> sortedNodes = SortNodesByGraphCost(origin.position, unsortedNodes);
List<Vector3> sortedPositions = sortedNodes.Select(n => n.position).ToList();
enumerator = DrawLineCoroutine(mover, lineRenderer, sortedPositions, moveSpeed);
caller.StartCoroutine(enumerator);
}
public static void Play(
MonoBehaviour caller,
Transform origin,
Transform mover,
LineRenderer lineRenderer,
List<Vector3> unsortedNodes,
float moveSpeed = 2f)
{
if (enumerator != null)
caller.StopCoroutine(enumerator);
enumerator = DrawLineCoroutine(mover, lineRenderer, unsortedNodes, moveSpeed);
caller.StartCoroutine(enumerator);
}
public static List<MapGraph.Node> SortNodesByGraphCost(Vector3 origin, List<MapGraph.Node> nodes)
public static List<MapGraph.Node> SortNodesByGraphCost(Vector3 origin, List<MapGraph.Node> nodes)
{
if (nodes == null || nodes.Count == 0) return new List<MapGraph.Node>();
if (nodes == null || nodes.Count == 0)
return new List<MapGraph.Node>();
List<MapGraph.Node> sorted = new List<MapGraph.Node>();
HashSet<MapGraph.Node> unvisited = new HashSet<MapGraph.Node>(nodes);
// ✅ 从 origin.position 找最近的节点作为起点
// 从原点出发,找出最近的可达节点作为起点
MapGraph.Node current = unvisited
.OrderBy(n => Vector3.Distance(origin, n.position))
.First();
@ -60,7 +61,8 @@ public static List<MapGraph.Node> SortNodesByGraphCost(Vector3 origin, List<MapG
foreach (var candidate in unvisited)
{
var path = PathFinder.FindPath(current, candidate);
if (path == null) continue;
if (path == null || path.Count < 2)
continue;
float totalCost = 0f;
for (int i = 0; i < path.Count - 1; i++)
@ -68,31 +70,35 @@ public static List<MapGraph.Node> SortNodesByGraphCost(Vector3 origin, List<MapG
var edge = path[i].edges.Find(e => e.target == path[i + 1]);
if (edge != null)
totalCost += edge.cost;
else
{
totalCost = float.MaxValue;
break; // 不连通路径
}
}
candidateCosts[candidate] = totalCost;
if (totalCost < float.MaxValue)
candidateCosts[candidate] = totalCost;
}
if (candidateCosts.Count == 0)
{
Debug.LogError("排序中断:无可达点");
Debug.LogWarning("图中存在不可达的节点,停止排序。");
break;
}
// 找出最小权重
// 选择最小总代价的节点
float minCost = candidateCosts.Values.Min();
// 找出具有最小权重的所有点
var sameCostNodes = candidateCosts
.Where(kv => Mathf.Approximately(kv.Value, minCost))
.Select(kv => kv.Key)
.ToList();
// ✅ 只保留其中一个(第一个),其余全部舍弃
MapGraph.Node selected = sameCostNodes.First();
sorted.Add(selected);
unvisited.Remove(selected);
// 移除同样代价的其他节点(也算访问过了)
for (int i = 1; i < sameCostNodes.Count; i++)
{
unvisited.Remove(sameCostNodes[i]);
@ -104,55 +110,76 @@ public static List<MapGraph.Node> SortNodesByGraphCost(Vector3 origin, List<MapG
return sorted;
}
private static IEnumerator DrawLineCoroutine(
Transform mover,
LineRenderer lineRenderer,
List<Vector3> points,
float moveSpeed)
{
int currentIndex = 0;
// 初始:第一段开始
lineRenderer.positionCount = 2;
lineRenderer.SetPosition(0, points[0]);
lineRenderer.SetPosition(1, points[0]);
mover.position = points[0];
while (currentIndex < points.Count - 1)
{
Vector3 start = points[currentIndex];
Vector3 end = points[currentIndex + 1];
float t = 0f;
float segmentLength = Vector3.Distance(start, end);
private static IEnumerator DrawLineCoroutine(
Transform mover,
LineRenderer lineRenderer,
List<Vector3> rawPoints,
float moveSpeed)
{
if (rawPoints == null || rawPoints.Count < 2)
yield break;
while (t < 1f)
{
t += Time.deltaTime * moveSpeed / segmentLength;
Vector3 currentPos = Vector3.Lerp(start, end, t);
mover.position = currentPos;
// ✅ 折返清洗逻辑A → B → A => 保留 B → A
List<Vector3> points = new List<Vector3>();
foreach (var point in rawPoints)
{
int count = points.Count;
if (count >= 2 && Vector3.Distance(point, points[count - 2]) < 0.01f)
{
// 移除中间的点A → B → A => 移除 B
points.RemoveAt(count - 2);
}
points.Add(point);
}
// 前段保持不动,当前段动态更新
for (int i = 0; i <= currentIndex; i++)
{
lineRenderer.SetPosition(i, points[i]);
}
if (points.Count < 2)
yield break;
lineRenderer.SetPosition(currentIndex + 1, currentPos);
// ✅ 动画绘制逻辑
int currentIndex = 0;
mover.position = points[0];
yield return null;
}
lineRenderer.positionCount = 2;
lineRenderer.SetPosition(0, points[0]);
lineRenderer.SetPosition(1, points[0]);
currentIndex++;
while (currentIndex < points.Count - 1)
{
Vector3 start = points[currentIndex];
Vector3 end = points[currentIndex + 1];
float t = 0f;
float segmentLength = Vector3.Distance(start, end);
if (currentIndex < points.Count - 1)
{
// 增加一个点用于下一段
lineRenderer.positionCount++;
}
}
while (t < 1f)
{
t += Time.deltaTime * moveSpeed / Mathf.Max(segmentLength, 0.001f);
Vector3 currentPos = Vector3.Lerp(start, end, t);
mover.position = currentPos;
// 最后一段固定终点
lineRenderer.SetPosition(lineRenderer.positionCount - 1, points.Last());
mover.position = points.Last();
}
}
for (int i = 0; i <= currentIndex; i++)
{
lineRenderer.SetPosition(i, points[i]);
}
lineRenderer.SetPosition(currentIndex + 1, currentPos);
yield return null;
}
currentIndex++;
if (currentIndex < points.Count - 1)
{
lineRenderer.positionCount++;
}
}
lineRenderer.SetPosition(lineRenderer.positionCount - 1, points[^1]);
mover.position = points[^1];
}
}

View File

@ -10,41 +10,63 @@ public class PathFinder
private static List<MapGraph.Node> FindPathInternal(MapGraph.Node start, MapGraph.Node goal, HashSet<MapGraph.Node> temporarilyAllowed)
{
// 优先队列:按 fScore 排序
var openSet = new PriorityQueue<MapGraph.Node>();
var cameFrom = new Dictionary<MapGraph.Node, MapGraph.Node>();
var gScore = new Dictionary<MapGraph.Node, float>();
var fScore = new Dictionary<MapGraph.Node, float>();
// 用于判断是否已在 openSet 中,避免重复入队
var openSetHash = new HashSet<MapGraph.Node>();
openSet.Enqueue(start, 0);
gScore[start] = 0;
fScore[start] = Heuristic(start, goal);
// 回溯路径的字典
var cameFrom = new Dictionary<MapGraph.Node, MapGraph.Node>();
// 起点到每个节点的实际代价
var gScore = new Dictionary<MapGraph.Node, float> { [start] = 0 };
// fScore = g + h估计总代价
var fScore = new Dictionary<MapGraph.Node, float> { [start] = Heuristic(start, goal) };
openSet.Enqueue(start, fScore[start]);
openSetHash.Add(start);
while (openSet.Count > 0)
{
var current = openSet.Dequeue();
openSetHash.Remove(current);
// 如果找到目标,回溯路径
if (current == goal)
return ReconstructPath(cameFrom, current);
foreach (var edge in current.edges)
{
// 若目标节点不可达,且不在临时允许列表中,跳过
if (!edge.target.isReachable && !temporarilyAllowed.Contains(edge.target))
continue;
float tentativeG = gScore[current] + edge.cost;
// 如果没访问过目标节点,或找到更短的路径
if (!gScore.ContainsKey(edge.target) || tentativeG < gScore[edge.target])
{
cameFrom[edge.target] = current;
gScore[edge.target] = tentativeG;
fScore[edge.target] = tentativeG + Heuristic(edge.target, goal);
openSet.Enqueue(edge.target, fScore[edge.target]);
float estimatedF = tentativeG + Heuristic(edge.target, goal);
fScore[edge.target] = estimatedF;
// 若未加入 openSet才加入队列
if (!openSetHash.Contains(edge.target))
{
openSet.Enqueue(edge.target, estimatedF);
openSetHash.Add(edge.target);
}
}
}
}
return null;
// 找不到路径,返回空列表(比返回 null 更安全)
return new List<MapGraph.Node>();
}
public static List<MapGraph.Node> FindPathThroughNodes(
List<MapGraph.Node> mustPassNodes,
List<MapGraph.Node> mustAvoidNodes,