画线移除循环往复的点
This commit is contained in:
@ -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];
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
@ -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,
|
||||
|
||||
Reference in New Issue
Block a user