using UnityEngine; namespace PF { /** What data to draw the graph debugging with */ public enum GraphDebugMode { Areas, G, H, F, Penalty, Connections, Tags } /** How path results are logged by the system */ public enum PathLog { /** Does not log anything. This is recommended for release since logging path results has a performance overhead. */ None, /** Logs basic info about the paths */ Normal, /** Includes additional info */ Heavy, /** Same as heavy, but displays the info in-game using GUI */ InGame, /** Same as normal, but logs only paths which returned an error */ OnlyErrors } /** How to estimate the cost from to the destination. * * The heuristic is the estimated cost from the current node to the target. * The different heuristics have roughly the same performance except not using any heuristic at all (#None) * which is usually significantly slower. * * In the image below you can see a comparison of the different heuristic options for an 8-connected grid and * for a 4-connected grid. * Note that all paths within the green area will all have the same length. The only difference between the heuristics * is which of those paths of the same length that will be chosen. * Note that while the Diagonal Manhattan and Manhattan options seem to behave very differently on an 8-connected grid * they only do it in this case because of very small rounding errors. Usually they behave almost identically on 8-connected grids. * * \shadowimage{heuristic.png} * * Generally for a 4-connected grid graph the Manhattan option should be used as it is the true distance on a 4-connected grid. * For an 8-connected grid graph the Diagonal Manhattan option is the mathematically most correct option, however the Euclidean option * is often preferred, especially if you are simplifying the path afterwards using modifiers. * * For any graph that is not grid based the Euclidean option is the best one to use. * * \see Wikipedia: A* search_algorithm */ public enum Heuristic { /** Manhattan distance. \see https://en.wikipedia.org/wiki/Taxicab_geometry */ Manhattan, /** Manhattan distance, but allowing diagonal movement as well. * \note This option is currently hard coded for the XZ plane. It will be equivalent to Manhattan distance if you try to use it in the XY plane (i.e for a 2D game). */ DiagonalManhattan, /** Ordinary distance. \see https://en.wikipedia.org/wiki/Euclidean_distance */ Euclidean, /** Use no heuristic at all. * This reduces the pathfinding algorithm to Dijkstra's algorithm. * This is usually significantly slower compared to using a heuristic, which is why the A* algorithm is usually preferred over Dijkstra's algorithm. * You may have to use this if you have a very non-standard graph. For example a world with a wraparound playfield (think Civilization or Asteroids) and you have custom links * with a zero cost from one end of the map to the other end. Usually the A* algorithm wouldn't find the wraparound links because it wouldn't think to look in that direction. * \see https://en.wikipedia.org/wiki/Dijkstra%27s_algorithm */ None } public static class PathFindHelper { #if !SERVER public static AstarPath GetConfig() { return AstarPath.active; } #else public static AStarConfig GetConfig() { return AStarConfig.Instance; } #endif /** * Returns the nearest node to a position using the specified NNConstraint. * Searches through all graphs for their nearest nodes to the specified position and picks the closest one.\n * Using the NNConstraint.None constraint. * * \snippet MiscSnippets.cs AstarPath.GetNearest1 * * \see Pathfinding.NNConstraint */ public static NNInfo GetNearest(Vector3 position) { return GetNearest(position, NNConstraint.None); } /** * Returns the nearest node to a position using the specified NNConstraint. * Searches through all graphs for their nearest nodes to the specified position and picks the closest one. * The NNConstraint can be used to specify constraints on which nodes can be chosen such as only picking walkable nodes. * * \snippet MiscSnippets.cs AstarPath.GetNearest2 * * \snippet MiscSnippets.cs AstarPath.GetNearest3 * * \see Pathfinding.NNConstraint */ public static NNInfo GetNearest(Vector3 position, NNConstraint constraint) { return GetNearest(position, constraint, null); } /** * Returns the nearest node to a position using the specified NNConstraint. * Searches through all graphs for their nearest nodes to the specified position and picks the closest one. * The NNConstraint can be used to specify constraints on which nodes can be chosen such as only picking walkable nodes. * \see Pathfinding.NNConstraint */ public static NNInfo GetNearest(Vector3 position, NNConstraint constraint, GraphNode hint) { // Cache property lookup var localGraphs = GetConfig().graphs; float minDist = float.PositiveInfinity; NNInfoInternal nearestNode = new NNInfoInternal(); int nearestGraph = -1; if (localGraphs != null) { for (int i = 0; i < localGraphs.Length; i++) { NavGraph graph = localGraphs[i]; // Check if this graph should be searched if (graph == null || !constraint.SuitableGraph(i, graph)) { continue; } NNInfoInternal nnInfo; if (GetConfig().fullGetNearestSearch) { // Slower nearest node search // this will try to find a node which is suitable according to the constraint nnInfo = graph.GetNearestForce(position, constraint); } else { // Fast nearest node search // just find a node close to the position without using the constraint that much // (unless that comes essentially 'for free') nnInfo = graph.GetNearest(position, constraint); } GraphNode node = nnInfo.node; // No node found in this graph if (node == null) { continue; } // Distance to the closest point on the node from the requested position float dist = ((Vector3) nnInfo.clampedPosition - position).magnitude; if (GetConfig().prioritizeGraphs && dist < GetConfig().prioritizeGraphsLimit) { // The node is close enough, choose this graph and discard all others minDist = dist; nearestNode = nnInfo; nearestGraph = i; break; } else { // Choose the best node found so far if (dist < minDist) { minDist = dist; nearestNode = nnInfo; nearestGraph = i; } } } } // No matches found if (nearestGraph == -1) { return new NNInfo(); } // Check if a constrained node has already been set if (nearestNode.constrainedNode != null) { nearestNode.node = nearestNode.constrainedNode; nearestNode.clampedPosition = nearestNode.constClampedPosition; } if (!GetConfig().fullGetNearestSearch && nearestNode.node != null && !constraint.Suitable(nearestNode.node)) { // Otherwise, perform a check to force the graphs to check for a suitable node NNInfoInternal nnInfo = localGraphs[nearestGraph].GetNearestForce(position, constraint); if (nnInfo.node != null) { nearestNode = nnInfo; } } if (!constraint.Suitable(nearestNode.node) || (constraint.constrainDistance && (nearestNode.clampedPosition - position).sqrMagnitude > GetConfig().maxNearestNodeDistanceSqr)) { return new NNInfo(); } // Convert to NNInfo which doesn't have all the internal fields return new NNInfo(nearestNode); } public static PathHandler debugPathData { get { return GetConfig().debugPathData; } set { GetConfig().debugPathData = value; } } public static ushort debugPathID { get { return GetConfig().debugPathID; } set { GetConfig().debugPathID = value; } } public static PathProcessor pathProcessor { get { return GetConfig().pathProcessor; } set { GetConfig().pathProcessor = value; } } public static bool IsUsingMultithreading { get { //return GetConfig().pathProcessor.IsUsingMultithreading; return false; } } public static NavGraph[] graphs { get { return GetConfig().graphs; } } public static Heuristic heuristic { get { return GetConfig().heuristic; } set { GetConfig().heuristic = value; } } public static float heuristicScale { get { return GetConfig().heuristicScale; } set { GetConfig().heuristicScale = value; } } public static EuclideanEmbedding euclideanEmbedding { get { return GetConfig().euclideanEmbedding; } set { GetConfig().euclideanEmbedding = value; } } public static PathLog logPathResults { get { return GetConfig().logPathResults; } set { GetConfig().logPathResults = value; } } /** Returns a new global node index. * \warning This method should not be called directly. It is used by the GraphNode constructor. */ public static int GetNewNodeIndex () { return pathProcessor.GetNewNodeIndex(); } /** Initializes temporary path data for a node. * \warning This method should not be called directly. It is used by the GraphNode constructor. */ public static void InitializeNode (GraphNode node) { pathProcessor.InitializeNode(node); } /** @name Callbacks */ /* Callbacks to pathfinding events. * These allow you to hook in to the pathfinding process.\n * Callbacks can be used like this: * \snippet MiscSnippets.cs AstarPath.Callbacks */ /** @{ */ /** Called on Awake before anything else is done. * This is called at the start of the Awake call, right after #active has been set, but this is the only thing that has been done.\n * Use this when you want to set up default settings for an AstarPath component created during runtime since some settings can only be changed in Awake * (such as multithreading related stuff) * \snippet MiscSnippets.cs AstarPath.OnAwakeSettings */ public static System.Action OnAwakeSettings; /** Called for each path before searching. Be careful when using multithreading since this will be called from a different thread. */ public static OnPathDelegate OnPathPreSearch; /** Called for each path after searching. Be careful when using multithreading since this will be called from a different thread. */ public static OnPathDelegate OnPathPostSearch; /** * Called when \a pathID overflows 65536 and resets back to zero. * \note This callback will be cleared every time it is called, so if you want to register to it repeatedly, register to it directly on receiving the callback as well. */ public static System.Action On65KOverflow; /** The next unused Path ID. * Incremented for every call to GetNextPathID */ private static ushort nextFreePathID = 1; /** Returns the next free path ID */ public static ushort GetNextPathID () { if (nextFreePathID == 0) { nextFreePathID++; if (On65KOverflow != null) { System.Action tmp = On65KOverflow; On65KOverflow = null; tmp(); } } return nextFreePathID++; } public static void Close() { OnAwakeSettings = null; OnPathPreSearch = null; OnPathPostSearch = null; On65KOverflow = null; } } }