diff options
| author | chai <215380520@qq.com> | 2024-05-23 10:08:29 +0800 | 
|---|---|---|
| committer | chai <215380520@qq.com> | 2024-05-23 10:08:29 +0800 | 
| commit | 8722a9920c1f6119bf6e769cba270e63097f8e25 (patch) | |
| tree | 2eaf9865de7fb1404546de4a4296553d8f68cc3b /Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/Pathfinding/PathHandler.cs | |
| parent | 3ba4020b69e5971bb0df7ee08b31d10ea4d01937 (diff) | |
+ astar project
Diffstat (limited to 'Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/Pathfinding/PathHandler.cs')
| -rw-r--r-- | Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/Pathfinding/PathHandler.cs | 249 | 
1 files changed, 249 insertions, 0 deletions
diff --git a/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/Pathfinding/PathHandler.cs b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/Pathfinding/PathHandler.cs new file mode 100644 index 0000000..debafc1 --- /dev/null +++ b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Core/Pathfinding/PathHandler.cs @@ -0,0 +1,249 @@ +using System.Collections.Generic; +using Unity.Collections; +using UnityEngine; + +namespace Pathfinding { +	using Pathfinding.Util; + +	/// <summary> +	/// Stores temporary node data for a single pathfinding request. +	/// Every node has one PathNode per thread used. +	/// It stores e.g G score, H score and other temporary variables needed +	/// for path calculation, but which are not part of the graph structure. +	/// +	/// See: Pathfinding.PathHandler +	/// See: https://en.wikipedia.org/wiki/A*_search_algorithm +	/// </summary> +	public struct PathNode { +		/// <summary>The path request (in this thread, if multithreading is used) which last used this node</summary> +		public ushort pathID; + +		/// <summary> +		/// Index of the node in the binary heap. +		/// The open list in the A* algorithm is backed by a binary heap. +		/// To support fast 'decrease key' operations, the index of the node +		/// is saved here. +		/// </summary> +		public ushort heapIndex; + +		/// <summary>Bitpacked variable which stores several fields</summary> +		private uint flags; + +		public static readonly PathNode Default = new PathNode { pathID = 0, heapIndex = BinaryHeap.NotInHeap, flags = 0 }; + +		/// <summary>Parent index uses the first 26 bits</summary> +		private const uint ParentIndexMask = (1U << 26) - 1U; + +		private const int FractionAlongEdgeOffset = 26; +		private const uint FractionAlongEdgeMask = ((1U << 30) - 1U) & ~ParentIndexMask; +		public const int FractionAlongEdgeQuantization = 1 << (30 - 26); + +		public static uint ReverseFractionAlongEdge(uint v) => (FractionAlongEdgeQuantization - 1) - v; + +		public static uint QuantizeFractionAlongEdge (float v) { +			v *= FractionAlongEdgeQuantization - 1; +			v += 0.5f; +			return Unity.Mathematics.math.clamp((uint)v, 0, FractionAlongEdgeQuantization - 1); +		} + +		public static float UnQuantizeFractionAlongEdge (uint v) { +			return (float)v * (1.0f / (FractionAlongEdgeQuantization - 1)); +		} + +		/// <summary>Flag 1 is at bit 30</summary> +		private const int Flag1Offset = 30; +		private const uint Flag1Mask = 1U << Flag1Offset; + +		/// <summary>Flag 2 is at bit 31</summary> +		private const int Flag2Offset = 31; +		private const uint Flag2Mask = 1U << Flag2Offset; + +		public uint fractionAlongEdge { +			get => (flags & FractionAlongEdgeMask) >> FractionAlongEdgeOffset; +			set => flags = (flags & ~FractionAlongEdgeMask) | ((value << FractionAlongEdgeOffset) & FractionAlongEdgeMask); +		} + +		public uint parentIndex { +			get => flags & ParentIndexMask; +			set => flags = (flags & ~ParentIndexMask) | value; +		} + +		/// <summary> +		/// Use as temporary flag during pathfinding. +		/// Path types can use this during pathfinding to mark +		/// nodes. When done, this flag should be reverted to its default state (false) to +		/// avoid messing up other pathfinding requests. +		/// </summary> +		public bool flag1 { +			get => (flags & Flag1Mask) != 0; +			set => flags = (flags & ~Flag1Mask) | (value ? Flag1Mask : 0U); +		} + +		/// <summary> +		/// Use as temporary flag during pathfinding. +		/// Path types can use this during pathfinding to mark +		/// nodes. When done, this flag should be reverted to its default state (false) to +		/// avoid messing up other pathfinding requests. +		/// </summary> +		public bool flag2 { +			get => (flags & Flag2Mask) != 0; +			set => flags = (flags & ~Flag2Mask) | (value ? Flag2Mask : 0U); +		} +	} + +	public enum TemporaryNodeType { +		Start, +		End, +		Ignore, +	} + +	public struct TemporaryNode { +		public uint associatedNode; +		public Int3 position; +		public int targetIndex; +		public TemporaryNodeType type; +	} + +	/// <summary>Handles thread specific path data.</summary> +	public class PathHandler { +		/// <summary> +		/// Current PathID. +		/// See: <see cref="PathID"/> +		/// </summary> +		private ushort pathID; + +		public readonly int threadID; +		public readonly int totalThreadCount; +		internal readonly GlobalNodeStorage nodeStorage; +		public int numTemporaryNodes { get; private set; } + +		/// <summary> +		/// All path nodes with an index greater or equal to this are temporary nodes that only exist for the duration of a single path. +		/// +		/// This is a copy of NodeStorage.nextNodeIndex. This is used to avoid having to access the NodeStorage while pathfinding as it's an extra indirection. +		/// </summary> +		public uint temporaryNodeStartIndex { get; private set; } +		UnsafeSpan<TemporaryNode> temporaryNodes; + +		/// <summary> +		/// Reference to the per-node data for this thread. +		/// +		/// Note: Only guaranteed to point to a valid allocation while the path is being calculated. +		/// </summary> +		public UnsafeSpan<PathNode> pathNodes; +#if UNITY_EDITOR +		UnsafeSpan<GlobalNodeStorage.DebugPathNode> debugPathNodes; +#endif + +		/// <summary> +		/// Binary heap to keep track of nodes on the "Open list". +		/// See: https://en.wikipedia.org/wiki/A*_search_algorithm +		/// </summary> +		public BinaryHeap heap = new BinaryHeap(128); + +		/// <summary>ID for the path currently being calculated or last path that was calculated</summary> +		public ushort PathID { get { return pathID; } } + +		/// <summary> +		/// StringBuilder that paths can use to build debug strings. +		/// Better for performance and memory usage to use a single StringBuilder instead of each path creating its own +		/// </summary> +		public readonly System.Text.StringBuilder DebugStringBuilder = new System.Text.StringBuilder(); + +		internal PathHandler (GlobalNodeStorage nodeStorage, int threadID, int totalThreadCount) { +			this.threadID = threadID; +			this.totalThreadCount = totalThreadCount; +			this.nodeStorage = nodeStorage; +			temporaryNodes = new UnsafeSpan<TemporaryNode>(Allocator.Persistent, GlobalNodeStorage.MaxTemporaryNodes); +		} + +		public void InitializeForPath (Path p) { +			var lastPathId = pathID; +			pathID = p.pathID; +			numTemporaryNodes = 0; +			temporaryNodeStartIndex = nodeStorage.nextNodeIndex; +			// Get the path nodes for this thread (may have been resized since last we calculated a path) +			pathNodes = nodeStorage.pathfindingThreadData[threadID].pathNodes; + +#if UNITY_EDITOR +			var astar = AstarPath.active; +			var shouldLog = astar.showGraphs && (astar.debugMode == GraphDebugMode.F || astar.debugMode == GraphDebugMode.H || astar.debugMode == GraphDebugMode.G || astar.showSearchTree); +			debugPathNodes = shouldLog ? nodeStorage.pathfindingThreadData[threadID].debugPathNodes : default; +#endif + +			// Path IDs have overflowed 65K, cleanup is needed to avoid bugs where we think +			// we have already visited a node when we haven't. +			// Since pathIDs are handed out sequentially, we can check if the new path id +			// is smaller than the last one. +			if (pathID < lastPathId) { +				ClearPathIDs(); +			} +		} + +		/// <summary> +		/// Returns the PathNode corresponding to the specified node. +		/// The PathNode is specific to this PathHandler since multiple PathHandlers +		/// are used at the same time if multithreading is enabled. +		/// </summary> +		public ref PathNode GetPathNode (GraphNode node, uint variant = 0) { +			return ref pathNodes[node.NodeIndex + variant]; +		} + +		public bool IsTemporaryNode(uint pathNodeIndex) => pathNodeIndex >= temporaryNodeStartIndex; + +		public uint AddTemporaryNode (TemporaryNode node) { +			if (numTemporaryNodes >= GlobalNodeStorage.MaxTemporaryNodes) { +				// It would be nice if we could dynamically re-allocate the temporaryNodes array, and the pathNodes array. But this class allows handing out references to path nodes and temporary nodes, +				// and we cannot guarantee that those references will not be used after this function is called (which may lead to memory corruption). +				// So instead we just have a hard limit, which can be increased by enabling the ASTAR_MORE_MULTI_TARGET_PATH_TARGETS define. +				throw new System.InvalidOperationException("Cannot create more than " + GlobalNodeStorage.MaxTemporaryNodes + " temporary nodes. You can enable ASTAR_MORE_MULTI_TARGET_PATH_TARGETS in the A* Inspector optimizations tab to increase this limit."); +			} + +			var index = temporaryNodeStartIndex + (uint)numTemporaryNodes; +			temporaryNodes[numTemporaryNodes] = node; +			pathNodes[index] = PathNode.Default; +			numTemporaryNodes++; +			return index; +		} + +		public GraphNode GetNode(uint nodeIndex) => nodeStorage.GetNode(nodeIndex); + +		public ref TemporaryNode GetTemporaryNode (uint nodeIndex) { +			if (nodeIndex < temporaryNodeStartIndex || nodeIndex >= temporaryNodeStartIndex + numTemporaryNodes) +				throw new System.ArgumentOutOfRangeException(); +			return ref temporaryNodes[(int)(nodeIndex - temporaryNodeStartIndex)]; +		} + +		[System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] +		public void LogVisitedNode (uint pathNodeIndex, uint h, uint g) { +#if UNITY_EDITOR +			if (debugPathNodes.Length > 0 && !IsTemporaryNode(pathNodeIndex)) { +				var parent = pathNodes[pathNodeIndex].parentIndex; +				debugPathNodes[pathNodeIndex] = new GlobalNodeStorage.DebugPathNode { +					h = h, +					g = g, +					parentIndex = parent >= temporaryNodeStartIndex ? 0 : parent, +					pathID = pathID, +					fractionAlongEdge = (byte)pathNodes[pathNodeIndex].fractionAlongEdge, +				}; +			} +#endif +		} + +		/// <summary> +		/// Set all nodes' pathIDs to 0. +		/// See: Pathfinding.PathNode.pathID +		/// </summary> +		public void ClearPathIDs () { +			for (int i = 0; i < pathNodes.Length; i++) { +				pathNodes[i].pathID = 0; +			} +		} + +		public void Dispose () { +			heap.Dispose(); +			temporaryNodes.Free(Allocator.Persistent); +			pathNodes = default; +		} +	} +}  | 
