/// 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 when the game starts, during OnEnable.
/// If you disable this, you will have to call <see cref="AstarPath.active.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, 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 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>
[System.Obsolete("This setting has been removed. It is now always true", true)]
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 has been removed. It was always a bit of a hack. Use NNConstraint.graphMask if you want to choose which graphs are searched.
/// </summary>
[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)]
publicboolprioritizeGraphs=false;
/// <summary>
/// Distance limit for <see cref="prioritizeGraphs"/>.
/// See: <see cref="prioritizeGraphs"/>
///
/// 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.
/// </summary>
[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)]
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.
/// Warning: Reducing the heuristic scale below 1, or disabling the heuristic, can significantly increase the cpu cost for pathfinding, especially for large graphs.
/// </summary>
publicHeuristicheuristic=Heuristic.Euclidean;
/// <summary>
/// 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 <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.
///
/// Warning: Reducing the heuristic scale below 1, or disabling the heuristic, can significantly increase the cpu cost for pathfinding, especially for large graphs.
/// 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 <see cref="AddWorkItem)"/>, 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
/// </summary>
publicThreadCountthreadCount=ThreadCount.One;
/// <summary>
/// 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.
/// </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, 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="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="AddWorkItem"/>.
///
/// See: graph-updates (view in online documentation for working links)
/// </summary>
publicfloatgraphUpdateBatchingInterval=0.2F;
#endregion
#regionDebugVariables
#ifProfileAstar
/// <summary>
/// How many paths has been computed this run. From application start.
/// Debugging variable
/// </summary>
publicstaticintPathsCompleted=0;
publicstaticSystem.Int64TotalSearchedNodes=0;
publicstaticSystem.Int64TotalSearchTime=0;
#endif
/// <summary>The time it took for the last call to <see cref="Scan"/> to complete</summary>
publicfloatlastScanTime{get;privateset;}
/// <summary>
/// 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.
/// </summary>
[System.NonSerialized]
internalPathHandlerdebugPathData;
/// <summary>The path ID to debug using gizmos</summary>
[System.NonSerialized]
internalushortdebugPathID;
/// <summary>
/// Debug string from the last completed path.
/// Will be updated if <see cref="logPathResults"/> == PathLog.InGame
/// </summary>
stringinGameDebugPath;
#endregion
#regionStatusVariables
/// <summary>
/// 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 <see cref="OnLatePostScan"/> and during the <see cref="GraphModifier.EventType"/>.LatePostScan event.
///
/// See: IsAnyGraphUpdateQueued
/// See: IsAnyGraphUpdateInProgress
/// </summary>
[field: System.NonSerialized]
publicboolisScanning{get;privateset;}
/// <summary>
/// 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).
/// 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. In most cases it is recommended to create a custom class which inherits from Pathfinding.GraphModifier instead.</summary>
publicstaticOnGraphDelegateOnGraphPreScan;
/// <summary>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.</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. In most cases it is recommended to create a custom class which inherits from Pathfinding.GraphModifier instead.</summary>
publicstaticOnScanDelegateOnPreScan;
/// <summary>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.</summary>
publicstaticOnScanDelegateOnPostScan;
/// <summary>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.</summary>
publicstaticOnScanDelegateOnLatePostScan;
/// <summary>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.</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>
/// 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.
/// </summary>
publicstaticSystem.ActionOnPathsCalculated;
#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>
/// <summary>Holds all paths waiting to be calculated and calculates them</summary>
readonlyPathProcessorpathProcessor;
/// <summary>Holds global node data that cannot be stored in individual graphs</summary>
internalGlobalNodeStoragenodeStorage;
/// <summary>
/// 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 <see cref="GraphModifier"/> events run.
/// - When graph related callbacks, such as <see cref="OnGraphsUpdated"/>, run.
/// - During the last step of a graph's scanning process. See <see cref="ScanningStage"/>.
///
/// 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.
///
/// <code>
/// var readLock = AstarPath.active.LockGraphDataForReading();
/// var handle = new MyJob {
/// // ...
/// }.Schedule(readLock.dependency);
/// readLock.UnlockAfter(handle);
/// </code>
///
/// See: <see cref="LockGraphDataForReading"/>
/// </summary>
RWLockgraphDataLock=newRWLock();
boolgraphUpdateRoutineRunning=false;
/// <summary>Makes sure QueueGraphUpdates will not queue multiple graph update orders</summary>
boolgraphUpdatesWorkItemAdded=false;
/// <summary>
/// Time the last graph update was done.
/// Used to group together frequent graph updates to batches
/// </summary>
floatlastGraphUpdate=-9999F;
/// <summary>Held if any work items are currently queued</summary>
PathProcessor.GraphUpdateLockworkItemLock;
/// <summary>Holds all completed paths waiting to be returned to where they were requested</summary>
internalreadonlyPathReturnQueuepathReturnQueue;
/// <summary>
/// Holds settings for heuristic optimization.
/// See: heuristic-opt (view in online documentation for working links)
// Forward graphUpdates.OnGraphsUpdated to AstarPath.OnGraphsUpdated
workItems.OnGraphsUpdated+=()=>{
if(OnGraphsUpdated!=null){
try{
OnGraphsUpdated(this);
}catch(System.Exceptione){
Debug.LogException(e);
}
}
};
pathProcessor.OnPathPreSearch+=path=>{
vartmp=OnPathPreSearch;
if(tmp!=null)tmp(path);
};
pathProcessor.OnPathPostSearch+=path=>{
LogPathResults(path);
vartmp=OnPathPostSearch;
if(tmp!=null)tmp(path);
};
// Sent every time the path queue is unblocked
pathProcessor.OnQueueUnblocked+=()=>{
if(euclideanEmbedding.dirty){
euclideanEmbedding.RecalculateCosts();
}
};
}
/// <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.
// 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(context=>{
graphUpdatesWorkItemAdded=false;
lastGraphUpdate=Time.realtimeSinceStartup;
workItem.initWithContext(context);
},workItem.updateWithContext));
}
}
/// <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
if(active!=this)thrownewSystem.Exception("This AstarPath component is not initialized in a scene. Are you trying to add work items to a prefab or a disabled AstarPath component?");
using(PausePathfinding()){
PerformBlockingActions(true);
}
}
}
/// <summary>
/// Calculates number of threads to use.
/// If count is not Automatic, simply returns count casted to an int.
/// Returns: An int specifying how many threads to use, 0 means a coroutine should be used for pathfinding instead of a separate thread.
///
/// If count is set to Automatic it will return a value based on the number of processors and memory for the current system.
/// If memory is <= 512MB or logical cores are <= 1, it will return 0. If memory is <= 1024 it will clamp threads to max 2.
/// Otherwise it will return the number of logical cores clamped to 6.
///
/// When running on WebGL this method always returns 0
// Discard all queued graph updates. Graph updates that are already in progress will still be allowed to finish,
// as they may be allocating unmanaged data which we don't know how to safely deallocate.
graphUpdates.DiscardQueued();
// TODO: Add unit test that verifies that work items that are added will always complete
// Ensure work items complete before disabling this component.
// This is important because work items may allocate temporary unmanaged memory, so we cannot just forget about them.
FlushWorkItems();
if(logPathResults==PathLog.Heavy)
Debug.Log("Processing Possible Work Items");
// Try to join pathfinding threads
pathProcessor.StopThreads();
if(logPathResults==PathLog.Heavy)
Debug.Log("Returning Paths");
// Return all paths
pathReturnQueue.ReturnPaths(false);
graphLock.Release();
euclideanEmbedding.OnDisable();
}
/// <summary>
/// Called after this component is enabled.
///
/// Unless the component has already been activated in Awake, this method should:
/// - Ensure the singleton holds (setting <see cref="active"/> to this).
/// - Make sure all subsystems that were disabled in OnDisable are again enabled.
/// - This includes starting pathfinding threads.
/// </summary>
voidOnEnable(){
// If the component gets re-enabled during runtime.
// Note that the first time the component loads, then Awake will run first
// and will already have set the #active field.
// In the editor, OnDisable -> OnEnable will be called when an undo or redo event happens (both in and outside of play mode).
if(active!=null){
if(active!=this&&Application.isPlaying){
if(this.enabled){
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
active=this;
// Disable GUILayout to gain some performance, it is not used in the OnGUI call
useGUILayout=false;
if(OnAwakeSettings!=null){
OnAwakeSettings();
}
hierarchicalGraph.OnEnable();
// To make sure all graph modifiers have been enabled before scan (to avoid script execution order issues)
GraphModifier.FindAllModifiers();
RelevantGraphSurface.FindAllGraphSurfaces();
InitializeColors();
navmeshUpdates.OnEnable();
// This will load the graph settings, or whole initialized graphs from the cache, if one has been supplied.
data.OnEnable();
// Flush work items, possibly added when loading the graph data
FlushWorkItems();
euclideanEmbedding.dirty=true;
InitializePathProcessor();
// This class uses the [ExecuteInEditMode] attribute
// So OnEnable is called even when not playing
// Don't scan the graphs unless we are in play mode
if(Application.isPlaying){
// Scan the graphs if #scanOnStartup is enabled, and we have not loaded a graph cache already.
// We only do this the first time the AstarPath component is enabled.
/// - The component is explicitly disabled in play mode or editor mode.
/// - When the component is about to be destroyed
/// - Including when the game stops
/// - When an undo/redo event takes place (Unity will first disable the component and then enable it again).
///
/// During edit and play mode this method should:
/// - Destroy all node data (but not the graphs themselves)
/// - Dispose all unmanaged data
/// - Shutdown pathfinding threads if they are running (any pending path requests are left in the queue)
/// </summary>
voidOnDisable(){
redrawScope.Dispose();
if(active==this){
if(asyncScanTask!=null){
Debug.LogWarning("An async scan was running when the AstarPath component was disabled. Blocking until the async scan is complete.",this);
BlockUntilAsyncScanComplete();
}
// Ensure there are no jobs running that might read or write graph data
graphDataLock.WriteSync().Unlock();
ShutdownPathfindingThreads();
// We need to call dispose data here because in the editor the OnDestroy
// method is not called but OnDisable is. It is vital that graph data
// is destroyed even in the editor (e.g. when going from edit mode to play mode)
// because a lot of data is stored as NativeArrays which need to be disposed.
// There is also another case where this is important. When the unity
// editor is configured to stop play mode after recompiling scripts
// it seems to not call OnDestroy (or at least not reliably across all versions of Unity).
// So we need to ensure we dispose of all the data during OnDisable.
data.DestroyAllNodes();
data.DisposeUnmanagedData();
hierarchicalGraph.OnDisable();
nodeStorage.OnDisable();
offMeshLinks.OnDisable();
active=null;
}
}
/// <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(){
if(logPathResults==PathLog.Heavy)
Debug.Log("AstarPath Component Destroyed - Cleaning Up Pathfinding Data");
// active has already been set to null during OnDisable.
// We temporarily make this object the active one just during the destruction.
varprevActive=active;
active=this;
ShutdownPathfindingThreads();
pathProcessor.Dispose();
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();
active=prevActive;
if(logPathResults==PathLog.Heavy)
Debug.Log("Cleaning up variables");
// Clear all static variables, otherwise the next scene might get weird data
if(active==this){
// 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>
/// Allocate a bunch of nodes at once.
/// This is faster than allocating each individual node separately and it can be done in a separate thread by using jobs.
///
/// <code>
/// var nodes = new PointNode[128];
/// var job = AstarPath.active.AllocateNodes(nodes, 128, () => new PointNode(), 1);
///
/// job.Complete();
/// </code>
///
/// See: <see cref="InitializeNode"/>
/// </summary>
/// <param name="result">Node array to fill</param>
/// <param name="count">How many nodes to allocate</param>
/// <param name="createNode">Delegate which creates a node. () => new T(). Note that new T(AstarPath.active) should *not* be used as that will cause the node to be initialized twice.</param>
/// <param name="variantsPerNode">How many variants of the node to allocate. Should be the same as \reflink{GraphNode.PathNodeVariants} for this node type.</param>
thrownewSystem.Exception("Trying to initialize a node when it is not safe to initialize any nodes. Must be done during a graph update. See http://arongranberg.com/astar/docs/graph-updates.html#direct");
thrownewSystem.Exception("Trying to initialize a node when it is not safe to initialize any nodes. Must be done during a graph update. See http://arongranberg.com/astar/docs/graph-updates.html#direct");
}
nodeStorage.InitializeNode(node);
}
internalvoidInitializeNodes(GraphNode[]nodes){
if(!pathProcessor.queue.allReceiversBlocked){
thrownewSystem.Exception("Trying to initialize a node when it is not safe to initialize any nodes. Must be done during a graph update. See http://arongranberg.com/astar/docs/graph-updates.html#direct");
/// 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.
/// Calling this method will recalculate all specified graphs (or all graphs if the graphsToScan parameter is null) from scratch.
/// This method is pretty slow (depending on graph type and graph complexity of course), so it is advisable to use
/// smaller graph updates whenever possible.
///
/// <code>
/// // Recalculate all graphs
/// AstarPath.active.Scan();
///
/// // Recalculate only the first grid graph
/// var graphToScan = AstarPath.active.data.gridGraph;
/// AstarPath.active.Scan(graphToScan);
///
/// // Recalculate only the first and third graphs
/// var graphsToScan = new [] { AstarPath.active.data.graphs[0], AstarPath.active.data.graphs[2] };
/// AstarPath.active.Scan(graphsToScan);
/// </code>
///
/// See: graph-updates (view in online documentation for working links)
/// See: ScanAsync
/// </summary>
/// <param name="graphsToScan">The graphs to scan. If this parameter is null then all graphs will be scanned</param>
publicvoidScan(NavGraph[]graphsToScan=null){
varprevStage=(ScanningStage)(-1);
if(asyncScanTask!=null){
Debug.LogWarning("An async scan was already running when a new scan was requested. Blocking until it is complete. You can check if a scan is currently in progress using the AstarPath.active.isScanning property.",this);
BlockUntilAsyncScanComplete();
}
Profiler.BeginSample("Scan");
Profiler.BeginSample("Init");
foreach(varpinScanInternal(graphsToScan,false)){
if(prevStage!=p.stage){
Profiler.EndSample();
Profiler.BeginSample(p.stage.ToString());
#if!NETFX_CORE&&UNITY_EDITOR
// Log progress to the console
System.Console.WriteLine(p.stage);
#endif
prevStage=p.stage;
}
}
Profiler.EndSample();
Profiler.EndSample();
}
/// <summary>
/// Scans a particular graph asynchronously. This is a IEnumerable, you can loop through it to get the progress
///
/// You can scan graphs asyncronously by yielding when you iterate through the returned IEnumerable.
/// Note that this does not guarantee a good framerate, but it will allow you
/// to at least show a progress bar while scanning.
///
/// <code>
/// IEnumerator Start () {
/// foreach (Progress progress in AstarPath.active.ScanAsync()) {
/// Note: If the graphs are already scanned, doing an async scan will temporarily cause increased memory usage, since two copies of the graphs will be kept in memory during the async scan.
/// This may not be desirable on some platforms. A non-async scan will not cause this temporary increased memory usage.
///
/// See: Scan
/// </summary>
/// <param name="graphsToScan">The graphs to scan. If this parameter is null then all graphs will be scanned</param>
Debug.LogWarning("An async scan was already running when a new async scan was requested. Blocking until the previous one is complete. You can check if a scan is currently in progress using the AstarPath.active.isScanning property.",this);
thrownewSystem.Exception("Critical error. Path Queue is empty but the path state is '"+path.PipelineState+"'");
}
// Calculate some paths
active.pathProcessor.TickNonMultithreaded();
active.PerformBlockingActions(true);
}
}
}
active.pathReturnQueue.ReturnPaths(false);
waitForPathDepth--;
}
/// <summary>
/// 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.
///
/// <code>
/// // There must be an AstarPath instance in the scene
/// if (AstarPath.active == null) return;
///
/// // We can calculate multiple paths asynchronously
/// for (int i = 0; i < 10; i++) {
/// var path = ABPath.Construct(transform.position, transform.position+transform.forward*i*10, OnPathComplete);
///
/// // Calculate the path by using the AstarPath component directly
/// AstarPath.StartPath(path);
/// }
/// </code>
/// </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>
/// <param name="assumeInPlayMode">Typically path.BlockUntilCalculated will be called when not in play mode. However, the play mode check will not work if
/// you call this from a separate thread, or a job. In that case you can set this to true to skip the check.</param>
/// True if the point is on a walkable part of the navmesh, as seen from above.
///
/// A point is considered on the navmesh if it is above or below a walkable navmesh surface, at any distance,
/// and if it is not above/below a closer unwalkable node.
///
/// Note: This means that, for example, in multi-story building a point will be considered on the navmesh if any walkable floor is below or above the point.
/// If you want more complex behavior then you can use the GetNearest method together with the appropriate <see cref="NNConstraint.distanceMetric"/> settings for your use case.
///
/// This uses the graph's natural up direction to determine which way is up.
/// Therefore, it will also work on rotated graphs, as well as graphs in 2D mode.
///
/// This method works for all graph types.
/// However, for <see cref="PointGraph"/>s, this will never return true unless you pass in the exact coordinate of a node, since point nodes do not have a surface.
///
/// Note: For spherical navmeshes (or other weird shapes), this method will not work as expected, as there's no well defined "up" direction.
///
/// [Open online documentation to see images]
///
/// See: <see cref="NavGraph.IsPointOnNavmesh"/> to check if a point is on the navmesh of a specific graph.
/// </summary>
/// <param name="position">The point to check</param>
publicboolIsPointOnNavmesh(Vector3position){
// We use the None constraint, instead of Walkable, to avoid ignoring unwalkable nodes that are closer to the point.
/// // Constrain the search to walkable nodes only
/// constraint.constrainWalkability = true;
/// constraint.walkable = true;
///
/// // Constrain the search to only nodes with tag 3 or tag 5
/// // The 'tags' field is a bitmask
/// constraint.constrainTags = true;
/// constraint.tags = (1 << 3) | (1 << 5);
///
/// var info = AstarPath.active.GetNearest(transform.position, constraint);
/// var node = info.node;
/// var closestPoint = info.position;
/// </code>
///
/// See: <see cref="NNConstraint"/>
/// </summary>
/// <param name="position">The point to find nodes close to</param>
/// <param name="constraint">The constraint which determines which graphs and nodes are acceptable to search on. May be null, in which case all nodes will be considered acceptable.</param>
/// True if there is an obstacle between start and end on the navmesh.
///
/// This is a simple api to check if there is an obstacle between two points.
/// If you need more detailed information, you can use <see cref="GridGraph.Linecast"/> or <see cref="NavmeshBase.Linecast"/> (for navmesh/recast graphs).
/// Those overloads can also return which nodes the line passed through, and allow you use custom node filtering.
///
/// <code>
/// var start = transform.position;
/// var end = start + Vector3.forward * 10;
/// if (AstarPath.active.Linecast(start, end)) {
/// Debug.DrawLine(start, end, Color.red);
/// } else {
/// Debug.DrawLine(start, end, Color.green);
/// }
/// </code>
///
/// Note: Only grid, recast and navmesh graphs support linecasts. The closest raycastable graph to the start point will be used for the linecast.
/// Note: Linecasts cannot pass through off-mesh links.
///
/// See: <see cref="NavmeshBase.Linecast"/>
/// See: <see cref="GridGraph.Linecast"/>
/// See: <see cref="IRaycastableGraph"/>
/// See: linecasting (view in online documentation for working links), for more details about linecasting
/// True if there is an obstacle between start and end on the navmesh.
///
/// This is a simple api to check if there is an obstacle between two points.
/// If you need more detailed information, you can use <see cref="GridGraph.Linecast"/> or <see cref="NavmeshBase.Linecast"/> (for navmesh/recast graphs).
/// Those overloads can also return which nodes the line passed through, and allow you use custom node filtering.
///
/// <code>
/// var start = transform.position;
/// var end = start + Vector3.forward * 10;
/// if (AstarPath.active.Linecast(start, end, out var hit)) {
/// Captures a snapshot of a part of the graphs, to allow restoring it later.
///
/// This is useful if you want to do a graph update, but you want to be able to restore the graph to the previous state.
///
/// The snapshot will capture enough information to restore the graphs, assuming the world only changed within the given bounding box.
/// This means the captured region may be larger than the bounding box.
///
/// <b>Limitations:</b>
/// - Currently, the <see cref="GridGraph"/> and <see cref="LayerGridGraph"/> supports snapshots. Other graph types do not support it.
/// - The graph must not change its dimensions or other core parameters between the time the snapshot is taken and the time it is restored.
/// - Custom node connections may not be preserved. Unless they are added as off-mesh links using e.g. a <see cref="NodeLink2"/> component.
/// - The snapshot must not be captured during a work item, graph update or when the graphs are being scanned, as the graphs may not be in a consistent state during those times.
///
/// See: <see cref="GraphUpdateUtilities.UpdateGraphsNoBlock"/>, which uses this method internally.
/// See: <see cref="NavGraph.Snapshot"/>
///
/// Note: You must dispose the returned snapshot when you are done with it, to avoid leaking memory.