using System; using System.Collections.Generic; using Unity.Collections; using Unity.Jobs; using UnityEngine; namespace DefaultNamespace.Navigation { public struct FindPathJob : IJob { private readonly Vector2 from; private readonly int to; [ReadOnly] private NativeList points; [ReadOnly] private NativeMultiHashMap connectionsDictionary; public NativeList Path; public FindPathJob( Vector2 from, int to, [ReadOnly] NativeList points, [ReadOnly] NativeMultiHashMap connectionsDictionary) { this.from = from; this.to = to; this.points = points; this.connectionsDictionary = connectionsDictionary; Path = new NativeList(Allocator.Persistent); } public void Execute() { var from = this.from; var toPos = points[to].Pos; var closestStartPoint = points.FindMinIndex(p => (p.Pos - from).sqrMagnitude); var closestEndPoint = points.FindMinIndex(p => (p.Pos - toPos).sqrMagnitude); if (closestStartPoint < 0 || closestEndPoint < 0) { return; } var parents = new Dictionary(); var gScore = new NativeArray(points.Length, Allocator.Temp); var fScore = new NativeArray(points.Length, Allocator.Temp); var cons = new NativeArray(points.Length, Allocator.Temp); try { var closed = new HashSet(); var open = new HashSet(); gScore.Fill(float.PositiveInfinity); fScore.Fill(float.PositiveInfinity); gScore[closestStartPoint] = 0; fScore[closestStartPoint] = CalculateHeuristic(closestStartPoint, closestEndPoint); open.Add(closestStartPoint); while (open.Count > 0) { var fScoreLocal = fScore; var current = open.MinValue(i => fScoreLocal[i]); var qPoint = points[current]; open.Remove(current); if (current == closestEndPoint) { ConstructPath(parents, current, cons); break; } foreach (var connection in connectionsDictionary.GetEnumerator(qPoint.FaltIndex)) { if (closed.Contains(connection.Destination)) { continue; } var gScoreVal = gScore[current] + (connection.Type == PathNodeConnectionType.Jump ? connection.Distance * connection.Distance * connection.Distance : 0); var fScoreValue = gScoreVal + CalculateHeuristic(connection.Destination, closestEndPoint); if (!open.Contains(connection.Destination)) // Discover a new node { open.Add(connection.Destination); } else if (gScoreVal >= gScore[connection.Destination]) { continue; // This is not a better path. } parents[connection.Destination] = current; gScore[connection.Destination] = gScoreVal; fScore[connection.Destination] = fScoreValue; cons[connection.Destination] = connection; open.Add(connection.Destination); } closed.Add(current); } } catch (Exception e) { Debug.LogException(e); } finally { gScore.Dispose(); fScore.Dispose(); cons.Dispose(); } } private void ConstructPath(IDictionary parents, int end, NativeArray cons) { Path.Add(new PathNode { ConnectionType = cons[end].Type, pos = points[end].Pos }); while (parents.TryGetValue(end, out var parent)) { Path.Add(new PathNode { ConnectionType = cons[parent].Type, pos = points[parent].Pos }); end = parent; } var distanceFromStart = Vector2.Distance(Path[Path.Length - 1].pos, from); if (distanceFromStart > 0.05f) { Path.Add(new PathNode { pos = @from, ConnectionType = PathNodeConnectionType.Start }); } Path.Reverse(); } private float CalculateHeuristic(int from, int to) { return (points[from].Index2D - points[to].Index2D).sqrMagnitude; } } }