using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using Pathfinding;
using Pathfinding.Drawing;
using UnityEngine.Profiling;
using Pathfinding.Util;
using Pathfinding.Graphs.Navmesh;
using Pathfinding.Graphs.Util;
using Pathfinding.Jobs;
using Pathfinding.Collections;
using Pathfinding.Sync;
using Unity.Jobs;
#if NETFX_CORE
using Thread = Pathfinding.WindowsStore.Thread;
#else
using Thread = System.Threading.Thread;
#endif
[ExecuteInEditMode]
[AddComponentMenu("Pathfinding/AstarPath")]
[DisallowMultipleComponent]
///
/// Core component for the A* Pathfinding System.
/// This class handles all of the pathfinding system, calculates all paths and stores the info.
/// This class is a singleton class, meaning there should only exist at most one active instance of it in the scene.
/// It might be a bit hard to use directly, usually interfacing with the pathfinding system is done through the class.
///
[HelpURL("https://arongranberg.com/astar/documentation/stable/astarpath.html")]
public class AstarPath : VersionedMonoBehaviour {
/// The version number for the A* Pathfinding Project
public static readonly System.Version Version = new System.Version(5, 3, 7);
/// Information about where the package was downloaded
public enum AstarDistribution { WebsiteDownload, AssetStore, PackageManager };
/// Used by the editor to guide the user to the correct place to download updates
public static readonly AstarDistribution Distribution = AstarDistribution.AssetStore;
///
/// Which branch of the A* Pathfinding Project is this release.
/// Used when checking for updates so that users of the development
/// versions can get notifications of development updates.
///
public static readonly string Branch = "master";
/// Holds all graph data
[UnityEngine.Serialization.FormerlySerializedAs("astarData")]
public AstarData data;
///
/// Returns the active AstarPath object in the scene.
/// Note: This is only set if the AstarPath object has been initialized (which happens in Awake).
///
public static AstarPath active;
/// Shortcut to
public NavGraph[] graphs => data.graphs;
bool hasScannedGraphAtStartup = false;
#region InspectorDebug
///
/// Visualize graphs in the scene view (editor only).
/// [Open online documentation to see images]
///
public bool showNavGraphs = true;
///
/// Toggle to show unwalkable nodes.
///
/// Note: Only relevant in the editor
///
/// See:
///
public bool showUnwalkableNodes = true;
///
/// The mode to use for drawing nodes in the sceneview.
///
/// Note: Only relevant in the editor
///
/// See:
///
public GraphDebugMode debugMode;
///
/// Low value to use for certain modes.
/// For example if is set to G, this value will determine when the node will be completely red.
///
/// Note: Only relevant in the editor
///
/// See:
/// See:
///
public float debugFloor = 0;
///
/// High value to use for certain modes.
/// For example if is set to G, this value will determine when the node will be completely green.
///
/// For the penalty debug mode, the nodes will be colored green when they have a penalty less than and red
/// when their penalty is greater or equal to this value and something between red and green otherwise.
///
/// Note: Only relevant in the editor
///
/// See:
/// See:
///
public float debugRoof = 20000;
///
/// If set, the and values will not be automatically recalculated.
///
/// Note: Only relevant in the editor
///
public bool manualDebugFloorRoof = false;
///
/// If enabled, nodes will draw a line to their 'parent'.
/// This will show the search tree for the latest path.
///
/// Note: Only relevant in the editor
///
public bool showSearchTree = false;
///
/// Size of the red cubes shown in place of unwalkable nodes.
///
/// Note: Only relevant in the editor. Does not apply to grid graphs.
/// See:
///
public float unwalkableNodeDebugSize = 0.3F;
///
/// The amount of debugging messages.
/// Use less debugging to improve performance (a bit) or just to get rid of the Console spamming.
/// Use more debugging (heavy) if you want more information about what the pathfinding scripts are doing.
/// The InGame option will display the latest path log using in-game GUI.
///
/// [Open online documentation to see images]
///
public PathLog logPathResults = PathLog.Normal;
#endregion
#region InspectorSettings
///
/// Maximum distance to search for nodes.
/// When searching for the nearest node to a point, this is the limit (in world units) for how far away it is allowed to be.
///
/// This is relevant if you try to request a path to a point that cannot be reached and it thus has to search for
/// the closest node to that point which can be reached (which might be far away). If it cannot find a node within this distance
/// then the path will fail.
///
/// [Open online documentation to see images]
///
/// See:
///
public float maxNearestNodeDistance = 100;
///
/// Max Nearest Node Distance Squared.
/// See:
///
public float maxNearestNodeDistanceSqr => maxNearestNodeDistance*maxNearestNodeDistance;
///
/// If true, all graphs will be scanned when the game starts, during OnEnable.
/// If you disable this, you will have to call yourself to enable pathfinding.
/// Alternatively you could load a saved graph from a file.
///
/// If a startup cache has been generated (see save-load-graphs) (view in online documentation for working links), it always takes priority, and the graphs will be loaded from the cache instead of scanned.
///
/// This can be useful to disable if you want to scan your graphs asynchronously, or if you have a procedural world which has not been created yet
/// at the start of the game.
///
/// See:
/// See:
///
public bool scanOnStartup = true;
///
/// Do a full GetNearest search for all graphs.
/// Additional searches will normally only be done on the graph which in the first fast search seemed to have the closest node.
/// With this setting on, additional searches will be done on all graphs since the first check is not always completely accurate.
/// More technically: GetNearestForce on all graphs will be called if true, otherwise only on the one graph which's GetNearest search returned the best node.
/// Usually faster when disabled, but higher quality searches when enabled.
/// Note: For the PointGraph this setting doesn't matter much as it has only one search mode.
///
[System.Obsolete("This setting has been removed. It is now always true", true)]
public bool fullGetNearestSearch = false;
///
/// Prioritize graphs.
/// Graphs will be prioritized based on their order in the inspector.
/// The first graph which has a node closer than will be chosen instead of searching all graphs.
///
/// Deprecated: This setting has been removed. It was always a bit of a hack. Use NNConstraint.graphMask if you want to choose which graphs are searched.
///
[System.Obsolete("This setting has been removed. It was always a bit of a hack. Use NNConstraint.graphMask if you want to choose which graphs are searched.", true)]
public bool prioritizeGraphs = false;
///
/// Distance limit for .
/// See:
///
/// Deprecated: This setting has been removed. It was always a bit of a hack. Use NNConstraint.graphMask if you want to choose which graphs are searched.
///
[System.Obsolete("This setting has been removed. It was always a bit of a hack. Use NNConstraint.graphMask if you want to choose which graphs are searched.", true)]
public float prioritizeGraphsLimit = 1F;
///
/// Reference to the color settings for this AstarPath object.
/// Color settings include for example which color the nodes should be in, in the sceneview.
///
public AstarColor colorSettings;
///
/// Stored tag names.
/// See: AstarPath.FindTagNames
/// See: AstarPath.GetTagNames
///
[SerializeField]
protected string[] tagNames = null;
///
/// The distance function to use as a heuristic.
/// The heuristic, often referred to as just 'H' is the estimated cost from a node to the target.
/// Different heuristics affect how the path picks which one to follow from multiple possible with the same length
/// See: for more details and descriptions of the different modes.
/// See: Wikipedia: Admissible heuristic
/// See: Wikipedia: A* search algorithm
/// See: Wikipedia: Dijkstra's Algorithm
///
/// Warning: Reducing the heuristic scale below 1, or disabling the heuristic, can significantly increase the cpu cost for pathfinding, especially for large graphs.
///
public Heuristic heuristic = Heuristic.Euclidean;
///
/// The scale of the heuristic.
/// If a value lower than 1 is used, the pathfinder will search more nodes (slower).
/// If 0 is used, the pathfinding algorithm will be reduced to dijkstra's algorithm. This is equivalent to setting to None.
/// If a value larger than 1 is used the pathfinding will (usually) be faster because it expands fewer nodes, but the paths may no longer be the optimal (i.e the shortest possible paths).
///
/// Usually you should leave this to the default value of 1.
///
/// Warning: Reducing the heuristic scale below 1, or disabling the heuristic, can significantly increase the cpu cost for pathfinding, especially for large graphs.
///
/// See: Wikipedia: Admissible heuristic
/// See: Wikipedia: A* search algorithm
/// See: Wikipedia: Dijkstra's Algorithm
///
public float heuristicScale = 1F;
///
/// Number of pathfinding threads to use.
/// Multithreading puts pathfinding in another thread, this is great for performance on 2+ core computers since the framerate will barely be affected by the pathfinding at all.
/// - None indicates that the pathfinding is run in the Unity thread as a coroutine
/// - Automatic will try to adjust the number of threads to the number of cores and memory on the computer.
/// Less than 512mb of memory or a single core computer will make it revert to using no multithreading.
///
/// It is recommended that you use one of the "Auto" settings that are available.
/// The reason is that even if your computer might be beefy and have 8 cores.
/// Other computers might only be quad core or dual core in which case they will not benefit from more than
/// 1 or 3 threads respectively (you usually want to leave one core for the unity thread).
/// If you use more threads than the number of cores on the computer it is mostly just wasting memory, it will not run any faster.
/// The extra memory usage is not trivially small. Each thread needs to keep a small amount of data for each node in all the graphs.
/// It is not the full graph data but it is proportional to the number of nodes.
/// The automatic settings will inspect the machine it is running on and use that to determine the number of threads so that no memory is wasted.
///
/// The exception is if you only have one (or maybe two characters) active at time. Then you should probably just go with one thread always since it is very unlikely
/// that you will need the extra throughput given by more threads. Keep in mind that more threads primarily increases throughput by calculating different paths on different
/// threads, it will not calculate individual paths any faster.
///
/// Warning: If you are modifying the pathfinding core scripts or if you are directly modifying graph data without using any of the
/// safe wrappers (like , multithreading can cause strange errors and cause pathfinding to stop working if you are not careful.
///
/// Note: WebGL does not support threads at all (since javascript is single-threaded) so no threads will be used on that platform.
///
/// Note: This setting only applies to pathfinding. Graph updates use the Unity Job System, which uses a different thread pool.
///
/// See: CalculateThreadCount
///
public ThreadCount threadCount = ThreadCount.One;
///
/// Max number of milliseconds to spend on pathfinding during each frame.
/// At least 500 nodes will be searched each frame (if there are that many to search).
/// When using multithreading this value is irrelevant.
///
public float maxFrameTime = 1F;
///
/// Throttle graph updates and batch them to improve performance.
/// If toggled, graph updates will batched and executed less often (specified by .
///
/// This can have a positive impact on pathfinding throughput since the pathfinding threads do not need
/// to be stopped as often, and it reduces the overhead per graph update.
/// All graph updates are still applied, they are just batched together so that more of them are
/// applied at the same time.
///
/// Do not use this if you want minimal latency between a graph update being requested
/// and it being applied.
///
/// This only applies to graph updates requested using the method. Not those requested
/// using .
///
/// If you want to apply graph updates immediately at some point, you can call .
///
/// See: graph-updates (view in online documentation for working links)
///
public bool batchGraphUpdates = false;
///
/// Minimum number of seconds between each batch of graph updates.
/// If is true, this defines the minimum number of seconds between each batch of graph updates.
///
/// This can have a positive impact on pathfinding throughput since the pathfinding threads do not need
/// to be stopped as often, and it reduces the overhead per graph update.
/// All graph updates are still applied however, they are just batched together so that more of them are
/// applied at the same time.
///
/// Do not use this if you want minimal latency between a graph update being requested
/// and it being applied.
///
/// This only applies to graph updates requested using the method. Not those requested
/// using .
///
/// See: graph-updates (view in online documentation for working links)
///
public float graphUpdateBatchingInterval = 0.2F;
#endregion
#region DebugVariables
#if ProfileAstar
///
/// How many paths has been computed this run. From application start.
/// Debugging variable
///
public static int PathsCompleted = 0;
public static System.Int64 TotalSearchedNodes = 0;
public static System.Int64 TotalSearchTime = 0;
#endif
/// The time it took for the last call to to complete
public float lastScanTime { get; private set; }
///
/// The path to debug using gizmos.
/// This is the path handler used to calculate the last path.
/// It is used in the editor to draw debug information using gizmos.
///
[System.NonSerialized]
internal PathHandler debugPathData;
/// The path ID to debug using gizmos
[System.NonSerialized]
internal ushort debugPathID;
///
/// Debug string from the last completed path.
/// Will be updated if == PathLog.InGame
///
string inGameDebugPath;
#endregion
#region StatusVariables
///
/// True while any graphs are being scanned.
///
/// This is primarily relevant when scanning graph asynchronously.
///
/// Note: Not to be confused with graph updates.
///
/// Note: This will be false during and during the .LatePostScan event.
///
/// See: IsAnyGraphUpdateQueued
/// See: IsAnyGraphUpdateInProgress
///
[field: System.NonSerialized]
public bool isScanning { get; private set; }
///
/// Number of parallel pathfinders.
/// Returns the number of concurrent processes which can calculate paths at once.
/// When using multithreading, this will be the number of threads, if not using multithreading it is always 1 (since only 1 coroutine is used).
/// See: IsUsingMultithreading
///
public int NumParallelThreads => pathProcessor.NumThreads;
///
/// Returns whether or not multithreading is used.
/// \exception System.Exception Is thrown when it could not be decided if multithreading was used or not.
/// This should not happen if pathfinding is set up correctly.
/// Note: This uses info about if threads are running right now, it does not use info from the settings on the A* object.
///
public bool IsUsingMultithreading => pathProcessor.IsUsingMultithreading;
///
/// Returns if any graph updates are waiting to be applied.
/// Note: This is false while the updates are being performed.
/// Note: This does *not* includes other types of work items such as navmesh cutting or anything added by .
///
public bool IsAnyGraphUpdateQueued => graphUpdates.IsAnyGraphUpdateQueued;
///
/// Returns if any graph updates are being calculated right now.
/// Note: This does *not* includes other types of work items such as navmesh cutting or anything added by .
///
/// See: IsAnyWorkItemInProgress
///
public bool IsAnyGraphUpdateInProgress => graphUpdates.IsAnyGraphUpdateInProgress;
///
/// Returns if any work items are in progress right now.
/// Note: This includes pretty much all types of graph updates.
/// Such as normal graph updates, navmesh cutting and anything added by .
///
public bool IsAnyWorkItemInProgress => workItems.workItemsInProgress;
///
/// Returns if this code is currently being exectuted inside a work item.
/// Note: This includes pretty much all types of graph updates.
/// Such as normal graph updates, navmesh cutting and anything added by .
///
/// In contrast to this is only true when work item code is being executed, it is not
/// true in-between the updates to a work item that takes several frames to complete.
///
internal bool IsInsideWorkItem => workItems.workItemsInProgressRightNow;
#endregion
#region Callbacks
///
/// Called on Awake before anything else is done.
/// This is called at the start of the Awake call, right after has been set, but this is the only thing that has been done.
/// 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)
///
/// // Create a new AstarPath object on Start and apply some default settings
/// public void Start () {
/// AstarPath.OnAwakeSettings += ApplySettings;
/// AstarPath astar = gameObject.AddComponent();
/// }
///
/// public void ApplySettings () {
/// // Unregister from the delegate
/// AstarPath.OnAwakeSettings -= ApplySettings;
/// // For example threadCount should not be changed after the Awake call
/// // so here's the only place to set it if you create the component during runtime
/// AstarPath.active.threadCount = ThreadCount.One;
/// }
///
///
public static System.Action OnAwakeSettings;
/// Called for each graph before they are scanned. In most cases it is recommended to create a custom class which inherits from Pathfinding.GraphModifier instead.
public static OnGraphDelegate OnGraphPreScan;
/// Called for each graph after they have been scanned. All other graphs might not have been scanned yet. In most cases it is recommended to create a custom class which inherits from Pathfinding.GraphModifier instead.
public static OnGraphDelegate OnGraphPostScan;
/// 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 before starting the scanning. In most cases it is recommended to create a custom class which inherits from Pathfinding.GraphModifier instead.
public static OnScanDelegate OnPreScan;
/// Called after scanning. This is called before applying links, flood-filling the graphs and other post processing. In most cases it is recommended to create a custom class which inherits from Pathfinding.GraphModifier instead.
public static OnScanDelegate OnPostScan;
/// Called after scanning has completed fully. This is called as the last thing in the Scan function. In most cases it is recommended to create a custom class which inherits from Pathfinding.GraphModifier instead.
public static OnScanDelegate OnLatePostScan;
/// Called when any graphs are updated. Register to for example recalculate the path whenever a graph changes. In most cases it is recommended to create a custom class which inherits from Pathfinding.GraphModifier instead.
public static OnScanDelegate OnGraphsUpdated;
///
/// Called when 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;
///
/// Called right after callbacks on paths have been called.
///
/// A path's callback function runs on the main thread when the path has been calculated.
/// This is done in batches for all paths that have finished their calculation since the last frame.
/// This event will trigger right after a batch of callbacks have been called.
///
/// If you do not want to use individual path callbacks, you can use this instead to poll all pending paths
/// and see which ones have completed. This is better than doing it in e.g. the Update loop, because
/// here you will have a guarantee that all calculated paths are still valid.
/// Immediately after this callback has finished, other things may invalidate calculated paths, like for example
/// graph updates.
///
/// This is used by the ECS integration to update all entities' pending paths, without having to store
/// a callback for each agent, and also to avoid the ECS synchronization overhead that having individual
/// callbacks would entail.
///
public static System.Action OnPathsCalculated;
#endregion
#region MemoryStructures
/// Processes graph updates
readonly GraphUpdateProcessor graphUpdates;
/// Holds a hierarchical graph to speed up some queries like if there is a path between two nodes
internal readonly HierarchicalGraph hierarchicalGraph;
/// Holds all active off-mesh links
public readonly OffMeshLinks offMeshLinks;
///
/// Handles navmesh cuts.
/// See:
///
public NavmeshUpdates navmeshUpdates = new NavmeshUpdates();
/// Processes work items
readonly WorkItemProcessor workItems;
/// Holds all paths waiting to be calculated and calculates them
readonly PathProcessor pathProcessor;
/// Holds global node data that cannot be stored in individual graphs
internal GlobalNodeStorage nodeStorage;
///
/// Global read-write lock for graph data.
///
/// Graph data is always consistent from the main-thread's perspective, but if you are using jobs to read from graph data, you may need this.
///
/// A write lock is held automatically...
/// - During graph updates. During async graph updates, the lock is only held once per frame while the graph update is actually running, not for the whole duration.
/// - During work items. Async work items work similarly to graph updates, the lock is only held once per frame while the work item is actually running.
/// - When events run.
/// - When graph related callbacks, such as , run.
/// - During the last step of a graph's scanning process. See .
///
/// To use e.g. AstarPath.active.GetNearest from an ECS job, you'll need to acquire a read lock first, and make sure the lock is only released when the job is finished.
///
///
/// var readLock = AstarPath.active.LockGraphDataForReading();
/// var handle = new MyJob {
/// // ...
/// }.Schedule(readLock.dependency);
/// readLock.UnlockAfter(handle);
///
///
/// See:
///
RWLock graphDataLock = new RWLock();
bool graphUpdateRoutineRunning = false;
/// Makes sure QueueGraphUpdates will not queue multiple graph update orders
bool graphUpdatesWorkItemAdded = false;
///
/// Time the last graph update was done.
/// Used to group together frequent graph updates to batches
///
float lastGraphUpdate = -9999F;
/// Held if any work items are currently queued
PathProcessor.GraphUpdateLock workItemLock;
/// Holds all completed paths waiting to be returned to where they were requested
internal readonly PathReturnQueue pathReturnQueue;
///
/// Holds settings for heuristic optimization.
/// See: heuristic-opt (view in online documentation for working links)
///
public EuclideanEmbedding euclideanEmbedding = new EuclideanEmbedding();
///
/// If an async scan is running, this will be set to the coroutine.
///
/// This primarily used to be able to force the async scan to complete immediately,
/// if the AstarPath component should happen to be destroyed while an async scan is running.
///
IEnumerator