/// 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 <see cref="Pathfinding.Seeker"/> class.
/// If true, all graphs will be scanned during Awake.
/// If you disable this, you will have to call <see cref="Scan"/> 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 to load that instead of scanning the graphs.
///
/// This can be useful to enable 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 cref="Scan"/>
/// See: <see cref="ScanAsync"/>
/// </summary>
publicboolscanOnStartup=true;
/// <summary>
/// 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.
/// </summary>
publicboolfullGetNearestSearch=false;
/// <summary>
/// Prioritize graphs.
/// Graphs will be prioritized based on their order in the inspector.
/// The first graph which has a node closer than <see cref="prioritizeGraphsLimit"/> will be chosen instead of searching all graphs.
///
/// Deprecated: This setting is discouraged, and it will be removed in a future update.
/// </summary>
[System.Obsolete("This setting is discouraged, and it will be removed in a future update")]
publicboolprioritizeGraphs=false;
/// <summary>
/// Distance limit for <see cref="prioritizeGraphs"/>.
/// See: <see cref="prioritizeGraphs"/>
///
/// Deprecated: This setting is discouraged, and it will be removed in a future update.
/// </summary>
[System.Obsolete("This setting is discouraged, and it will be removed in a future update")]
publicfloatprioritizeGraphsLimit=1F;
/// <summary>
/// Reference to the color settings for this AstarPath object.
/// Color settings include for example which color the nodes should be in, in the sceneview.
/// </summary>
publicAstarColorcolorSettings;
/// <summary>
/// Stored tag names.
/// See: AstarPath.FindTagNames
/// See: AstarPath.GetTagNames
/// </summary>
[SerializeField]
protectedstring[]tagNames=null;
/// <summary>
/// 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: <see cref="Pathfinding.Heuristic"/> for more details and descriptions of the different modes.
/// 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 <see cref="heuristic"/> 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.
/// 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.
///
/// Note that if you are modifying the pathfinding core scripts or if you are directly modifying graph data without using any of the
/// safe wrappers (like <see cref="AddWorkItem)"/> multithreading can cause strange errors and pathfinding stopping to work if you are not careful.
/// For basic usage (not modding the pathfinding core) it should be safe.
///
/// Note: WebGL does not support threads at all (since javascript is single-threaded) so no threads will be used on that platform.
///
/// See: CalculateThreadCount
/// </summary>
publicThreadCountthreadCount=ThreadCount.One;
/// <summary>
/// Max number of milliseconds to spend each frame for pathfinding.
/// At least 500 nodes will be searched each frame (if there are that many to search).
/// When using multithreading this value is irrelevant.
/// </summary>
publicfloatmaxFrameTime=1F;
/// <summary>
/// Throttle graph updates and batch them to improve performance.
/// If toggled, graph updates will batched and executed less often (specified by <see cref="graphUpdateBatchingInterval)"/>.
///
/// 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.
///
/// However 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 <see cref="UpdateGraphs"/> method. Not those requested
/// using <see cref="RegisterSafeUpdate"/> or <see cref="AddWorkItem"/>.
///
/// If you want to apply graph updates immediately at some point, you can call <see cref="FlushGraphUpdates"/>.
///
/// See: graph-updates (view in online documentation for working links)
/// </summary>
publicboolbatchGraphUpdates=false;
/// <summary>
/// Minimum number of seconds between each batch of graph updates.
/// If <see cref="batchGraphUpdates"/> 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 <see cref="UpdateGraphs"/> method. Not those requested
/// using <see cref="RegisterSafeUpdate"/> or <see cref="AddWorkItem"/>.
///
/// See: graph-updates (view in online documentation for working links)
/// </summary>
publicfloatgraphUpdateBatchingInterval=0.2F;
/// <summary>
/// Batch graph updates.
/// Deprecated: This field has been renamed to <see cref="batchGraphUpdates"/>.
/// </summary>
[System.Obsolete("This field has been renamed to 'batchGraphUpdates'")]
/// 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 <see cref="RegisterSafeUpdate"/> or <see cref="AddWorkItem"/>.
/// 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 cref="RegisterSafeUpdate"/> or <see cref="AddWorkItem"/>.
/// This is called at the start of the Awake call, right after <see cref="active"/> 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)
/// <code>
/// // Create a new AstarPath object on Start and apply some default settings
/// <summary>Called for each graph before they are scanned</summary>
publicstaticOnGraphDelegateOnGraphPreScan;
/// <summary>Called for each graph after they have been scanned. All other graphs might not have been scanned yet.</summary>
publicstaticOnGraphDelegateOnGraphPostScan;
/// <summary>Called for each path before searching. Be careful when using multithreading since this will be called from a different thread.</summary>
publicstaticOnPathDelegateOnPathPreSearch;
/// <summary>Called for each path after searching. Be careful when using multithreading since this will be called from a different thread.</summary>
publicstaticOnPathDelegateOnPathPostSearch;
/// <summary>Called before starting the scanning</summary>
publicstaticOnScanDelegateOnPreScan;
/// <summary>Called after scanning. This is called before applying links, flood-filling the graphs and other post processing.</summary>
publicstaticOnScanDelegateOnPostScan;
/// <summary>Called after scanning has completed fully. This is called as the last thing in the Scan function.</summary>
publicstaticOnScanDelegateOnLatePostScan;
/// <summary>Called when any graphs are updated. Register to for example recalculate the path whenever a graph changes.</summary>
publicstaticOnScanDelegateOnGraphsUpdated;
/// <summary>
/// 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.
/// </summary>
publicstaticSystem.ActionOn65KOverflow;
/// <summary>Deprecated:</summary>
[System.ObsoleteAttribute]
publicSystem.ActionOnGraphsWillBeUpdated;
/// <summary>Deprecated:</summary>
[System.ObsoleteAttribute]
publicSystem.ActionOnGraphsWillBeUpdated2;
#endregion
#regionMemoryStructures
/// <summary>Processes graph updates</summary>
readonlyGraphUpdateProcessorgraphUpdates;
/// <summary>Holds a hierarchical graph to speed up some queries like if there is a path between two nodes</summary>
// Forward graphUpdates.OnGraphsUpdated to AstarPath.OnGraphsUpdated
graphUpdates.OnGraphsUpdated+=()=>{
if(OnGraphsUpdated!=null){
OnGraphsUpdated(this);
}
};
}
/// <summary>
/// Returns tag names.
/// Makes sure that the tag names array is not null and of length 32.
/// If it is null or not of length 32, it creates a new array and fills it with 0,1,2,3,4 etc...
/// See: AstarPath.FindTagNames
/// </summary>
publicstring[]GetTagNames(){
if(tagNames==null||tagNames.Length!=32){
tagNames=newstring[32];
for(inti=0;i<tagNames.Length;i++){
tagNames[i]=""+i;
}
tagNames[0]="Basic Ground";
}
returntagNames;
}
/// <summary>
/// Used outside of play mode to initialize the AstarPath object even if it has not been selected in the inspector yet.
/// This will set the <see cref="active"/> property and deserialize all graphs.
///
/// This is useful if you want to do changes to the graphs in the editor outside of play mode, but cannot be sure that the graphs have been deserialized yet.
/// var node = AstarPath.active.GetNearest(transform.position).node;
/// node.Walkable = false;
/// }));
/// </code>
///
/// <code>
/// AstarPath.active.AddWorkItem(() => {
/// // Safe to update graphs here
/// var node = AstarPath.active.GetNearest(transform.position).node;
/// node.position = (Int3)transform.position;
/// });
/// </code>
///
/// See: <see cref="FlushWorkItems"/>
/// </summary>
publicvoidAddWorkItem(AstarWorkItemitem){
workItems.AddWorkItem(item);
// Make sure pathfinding is stopped and work items are processed
if(!workItemLock.Held){
workItemLock=PausePathfindingSoon();
}
#ifUNITY_EDITOR
// If not playing, execute instantly
if(!Application.isPlaying){
FlushWorkItems();
}
#endif
}
#regionGraphUpdateMethods
/// <summary>
/// Will apply queued graph updates as soon as possible, regardless of <see cref="batchGraphUpdates"/>.
/// Calling this multiple times will not create multiple callbacks.
/// This function is useful if you are limiting graph updates, but you want a specific graph update to be applied as soon as possible regardless of the time limit.
/// Note that this does not block until the updates are done, it merely bypasses the <see cref="batchGraphUpdates"/> time limit.
///
/// See: <see cref="FlushGraphUpdates"/>
/// </summary>
publicvoidQueueGraphUpdates(){
if(!graphUpdatesWorkItemAdded){
graphUpdatesWorkItemAdded=true;
varworkItem=graphUpdates.GetWorkItem();
// Add a new work item which first
// sets the graphUpdatesWorkItemAdded flag to false
// and then processes the graph updates
AddWorkItem(newAstarWorkItem(()=>{
graphUpdatesWorkItemAdded=false;
lastGraphUpdate=Time.realtimeSinceStartup;
workItem.init();
},workItem.update));
}
}
/// <summary>
/// Waits a moment with updating graphs.
/// If batchGraphUpdates is set, we want to keep some space between them to let pathfinding threads running and then calculate all queued calls at once
Debug.LogWarning("Another A* component is already in the scene. More than one A* component cannot be active at the same time. Disabling this one.",this);
}
enabled=false;
return;
}
// Very important to set this. Ensures the singleton pattern holds
thrownewSystem.Exception("Singleton pattern broken. Make sure you only have one AstarPath object in the scene");
}
if(data==null){
thrownewSystem.NullReferenceException("data is null... A* not set up correctly?");
}
if(data.graphs==null){
data.graphs=newNavGraph[0];
data.UpdateShortcuts();
}
}
/// <summary>\cond internal</summary>
/// <summary>
/// Internal method to make sure <see cref="active"/> is set to this object and that <see cref="data"/> is not null.
/// Also calls OnEnable for the <see cref="colorSettings"/> and initializes data.userConnections if it wasn't initialized before
///
/// Warning: This is mostly for use internally by the system.
/// </summary>
publicvoidConfigureReferencesInternal(){
active=this;
data=data??newAstarData();
colorSettings=colorSettings??newAstarColor();
colorSettings.PushToStatic(this);
}
/// <summary>\endcond</summary>
/// <summary>
/// Initializes the AstarData class.
/// Searches for graph types, calls Awake on <see cref="data"/> and on all graphs
///
/// See: AstarData.FindGraphTypes
/// </summary>
voidInitializeAstarData(){
data.FindGraphTypes();
data.Awake();
data.UpdateShortcuts();
}
/// <summary>Cleans up meshes to avoid memory leaks</summary>
voidOnDisable(){
gizmos.ClearCache();
}
/// <summary>
/// Clears up variables and other stuff, destroys graphs.
/// Note that when destroying an AstarPath object, all static variables such as callbacks will be cleared.
/// </summary>
voidOnDestroy(){
// This class uses the [ExecuteInEditMode] attribute
// So OnDestroy is called even when not playing
// Don't do anything when not in play mode
if(!Application.isPlaying)return;
if(logPathResults==PathLog.Heavy)
Debug.Log("+++ AstarPath Component Destroyed - Cleaning Up Pathfinding Data +++");
if(active!=this)return;
// Block until the pathfinding threads have
// completed their current path calculation
PausePathfinding();
navmeshUpdates.OnDisable();
euclideanEmbedding.dirty=false;
FlushWorkItems();
// Don't accept any more path calls to this AstarPath instance.
// This will cause all pathfinding threads to exit (if any exist)
pathProcessor.queue.TerminateReceivers();
if(logPathResults==PathLog.Heavy)
Debug.Log("Processing Possible Work Items");
// Stop the graph update thread (if it is running)
graphUpdates.DisableMultithreading();
// Try to join pathfinding threads
pathProcessor.JoinThreads();
if(logPathResults==PathLog.Heavy)
Debug.Log("Returning Paths");
// Return all paths
pathReturnQueue.ReturnPaths(false);
if(logPathResults==PathLog.Heavy)
Debug.Log("Destroying Graphs");
// Clean up graph data
// Data may be null if this object was never enabled because another A* instance existed.
if(data!=null)data.OnDestroy();
if(logPathResults==PathLog.Heavy)
Debug.Log("Cleaning up variables");
// Clear variables up, static variables are good to clean up, otherwise the next scene might get weird data
// Clear all callbacks
OnAwakeSettings=null;
OnGraphPreScan=null;
OnGraphPostScan=null;
OnPathPreSearch=null;
OnPathPostSearch=null;
OnPreScan=null;
OnPostScan=null;
OnLatePostScan=null;
On65KOverflow=null;
OnGraphsUpdated=null;
active=null;
}
#regionScanMethods
/// <summary>
/// Floodfills starting from the specified node.
///
/// Deprecated: Deprecated: Not meaningful anymore. The HierarchicalGraph takes care of things automatically behind the scenes
/// </summary>
[System.Obsolete("Not meaningful anymore. The HierarchicalGraph takes care of things automatically behind the scenes")]
publicvoidFloodFill(GraphNodeseed){
}
/// <summary>
/// Floodfills starting from 'seed' using the specified area.
///
/// Deprecated: Not meaningful anymore. The HierarchicalGraph takes care of things automatically behind the scenes
/// </summary>
[System.Obsolete("Not meaningful anymore. The HierarchicalGraph takes care of things automatically behind the scenes")]
publicvoidFloodFill(GraphNodeseed,uintarea){
}
/// <summary>
/// Floodfills all graphs and updates areas for every node.
/// The different colored areas that you see in the scene view when looking at graphs
/// are called just 'areas', this method calculates which nodes are in what areas.
/// See: Pathfinding.Node.area
///
/// Deprecated: Avoid using. This will force a full recalculation of the connected components. In most cases the HierarchicalGraph class takes care of things automatically behind the scenes now.
/// </summary>
[ContextMenu("Flood Fill Graphs")]
[System.Obsolete("Avoid using. This will force a full recalculation of the connected components. In most cases the HierarchicalGraph class takes care of things automatically behind the scenes now.")]
publicvoidFloodFill(){
hierarchicalGraph.RecalculateAll();
workItems.OnFloodFill();
}
/// <summary>
/// Returns a new global node index.
/// Warning: This method should not be called directly. It is used by the GraphNode constructor.
/// </summary>
internalintGetNewNodeIndex(){
returnpathProcessor.GetNewNodeIndex();
}
/// <summary>
/// Initializes temporary path data for a node.
/// Warning: This method should not be called directly. It is used by the GraphNode constructor.
/// </summary>
internalvoidInitializeNode(GraphNodenode){
pathProcessor.InitializeNode(node);
}
/// <summary>
/// Internal method to destroy a given node.
/// This is to be called after the node has been disconnected from the graph so that it cannot be reached from any other nodes.
/// It should only be called during graph updates, that is when the pathfinding threads are either not running or paused.
///
/// Warning: This method should not be called by user code. It is used internally by the system.
/// </summary>
internalvoidDestroyNode(GraphNodenode){
pathProcessor.DestroyNode(node);
}
/// <summary>
/// Blocks until all pathfinding threads are paused and blocked.
/// Deprecated: Use <see cref="PausePathfinding"/> instead. Make sure to call Release on the returned lock.
/// </summary>
[System.Obsolete("Use PausePathfinding instead. Make sure to call Release on the returned lock.", true)]
publicvoidBlockUntilPathQueueBlocked(){
}
/// <summary>
/// Blocks until all pathfinding threads are paused and blocked.
///
/// <code>
/// var graphLock = AstarPath.active.PausePathfinding();
/// // Here we can modify the graphs safely. For example by adding a new node to a point graph
/// var node = AstarPath.active.data.pointGraph.AddNode((Int3) new Vector3(3, 1, 4));
///
/// // Allow pathfinding to resume
/// graphLock.Release();
/// </code>
///
/// Returns: A lock object. You need to call <see cref="Pathfinding.PathProcessor.GraphUpdateLock.Release"/> on that object to allow pathfinding to resume.
/// Note: In most cases this should not be called from user code. Use the <see cref="AddWorkItem"/> method instead.
/// Adds the path to a queue so that it will be calculated as soon as possible.
/// The callback specified when constructing the path will be called when the path has been calculated.
/// Usually you should use the Seeker component instead of calling this function directly.
/// </summary>
/// <param name="path">The path that should be enqueued.</param>
/// <param name="pushToFront">If true, the path will be pushed to the front of the queue, bypassing all waiting paths and making it the next path to be calculated.
/// This can be useful if you have a path which you want to prioritize over all others. Be careful to not overuse it though.
/// If too many paths are put in the front of the queue often, this can lead to normal paths having to wait a very long time before being calculated.</param>