示例#1
0
	/** Forces thread safe callbacks to be called.
	 * This method should only be called from inside another thread safe callback to for example instantly run some graph updates.
	 * \throws System.InvalidOperationException if any threads are detected to be active and running
	 */
	public static void ForceCallThreadSafeCallbacks () {
		if (!threadSafeUpdateState) {
			throw new System.InvalidOperationException ("You should only call this function from a thread safe callback. That does not seem to be the case for this call.");
		}
		
		if (OnThreadSafeCallback != null) {
			OnVoidDelegate tmp = OnThreadSafeCallback;
			OnThreadSafeCallback = null;
			tmp ();
		}
	}
示例#2
0
	/** Updates the graphs based on the #graphUpdateQueue
	 * \see UpdateGraphs
	 */
	private void DoUpdateGraphs () {
		isRegisteredForUpdate = false;
		isUpdatingGraphs = true;
		lastGraphUpdate = Time.time;
		
		if (OnGraphsWillBeUpdated2 != null) {
			OnVoidDelegate callbacks = OnGraphsWillBeUpdated2;
			OnGraphsWillBeUpdated2 = null;
			callbacks ();
		}
		
		if (OnGraphsWillBeUpdated != null) {
			OnVoidDelegate callbacks = OnGraphsWillBeUpdated;
			OnGraphsWillBeUpdated = null;
			callbacks ();
		}
		GraphModifier.TriggerEvent (GraphModifier.EventType.PreUpdate);
		
		//If any GUOs requires a flood fill, then issue it, otherwise we can skip it to save processing power
		bool anyRequiresFloodFill = false;
		
		if (graphUpdateQueue != null) {
			while (graphUpdateQueue.Count > 0) {
				GraphUpdateObject ob = graphUpdateQueue.Dequeue ();
				
				if (ob.requiresFloodFill) anyRequiresFloodFill = true;
				
				foreach (IUpdatableGraph g in astarData.GetUpdateableGraphs ()) {
					NavGraph gr = g as NavGraph;
					if (ob.nnConstraint == null || ob.nnConstraint.SuitableGraph (active.astarData.GetGraphIndex (gr),gr)) {
						
						g.UpdateArea (ob);
					}
				}
				
			}
		}
		
		isUpdatingGraphs = false;
		
		//If any GUOs required flood filling and if we are not scanning graphs at the moment (in that case a FloodFill will be done later)
		if (anyRequiresFloodFill && !isScanning) {
			FloodFill ();
		}
		
		//this callback should not be called if scanning
		//Notify scripts that the graph has been updated
		if (OnGraphsUpdated != null && !isScanning) {
			OnGraphsUpdated (this);
		}
		GraphModifier.TriggerEvent (GraphModifier.EventType.PostUpdate);
	}
示例#3
0
	/** Main pathfinding function. This function will calculate the paths in the pathfinding queue
	 * \see CalculatePaths
	 */
	private static IEnumerator CalculatePaths (System.Object _threadInfo) {
		
		
		//Increment the counter for how many threads are calculating
		System.Threading.Interlocked.Increment (ref numActiveThreads);
		
		PathThreadInfo threadInfo;
		try {
			threadInfo = (PathThreadInfo)_threadInfo;
		} catch (System.Exception e) {
			Debug.LogError ("Arguments to pathfinding threads must be of type ThreadStartInfo\n"+e);
			throw new System.ArgumentException ("Argument must be of type ThreadStartInfo",e);
		}
		
		int numPaths = 0;
		
		//Initialize memory for this thread
		NodeRunData runData = threadInfo.runData;
		
		
		//Max number of ticks before yielding/sleeping
		long maxTicks = (long)(active.maxFrameTime*10000);
		long targetTick = System.DateTime.UtcNow.Ticks + maxTicks;
		
		threadSafeUpdateState = true;
		
		while (true) {
			
			//The path we are currently calculating
			Path p = null;
			
			AstarProfiler.StartProfile ("Path Queue");
			
			//Try to get the next path to be calculated
			while (true) {
				//Cancel function (and thus the thread) if no more paths should be accepted.
				//This is done when the A* object is about to be destroyed
				if (!active.acceptNewPaths) {
					System.Threading.Interlocked.Decrement (ref numActiveThreads);
					yield break;
				}
				
				if (pathQueue.Count > 0) {
					p = pathQueue.Dequeue ();
				}
				
				//System.Threading.Interlocked.Increment(ref threadsIdle);
				
				//Last thread alive
				//Call callbacks if any are requested
				OnVoidDelegate tmp = OnSafeCallback;
				OnSafeCallback = null;
				if (tmp != null) tmp();
				
				TryCallThreadSafeCallbacks ();
				//The threadSafeUpdateState is still enabled since this is coroutine mode
				//It would be reset in TryCallThreadSafeCallbacks
				threadSafeUpdateState = true;
				
				if (p == null) {
					AstarProfiler.EndProfile ();
					yield return 0;
					AstarProfiler.StartProfile ("Path Queue");
				}
				
				//If we have a path, start calculating it
				if (p != null) break;
			}
			
			AstarProfiler.EndProfile ();
			
			AstarProfiler.StartProfile ("Path Calc");
			
			//Max number of ticks we are allowed to continue working in one run
			//One tick is 1/10000 of a millisecond
			maxTicks = (long)(active.maxFrameTime*10000);
			
			threadSafeUpdateState = false;
			
			p.PrepareBase (runData);
			
			//Now processing the path
			//Will advance to Processing
			p.AdvanceState (PathState.Processing);
			
			//Call some callbacks
			if (OnPathPreSearch != null) {
				OnPathPreSearch (p);
			}
			
			numPaths++;
			
			//Tick for when the path started, used for calculating how long time the calculation took
			long startTicks = System.DateTime.UtcNow.Ticks;
			long totalTicks = 0;
			
			AstarProfiler.StartFastProfile(8);
			
			AstarProfiler.StartFastProfile(0);
			//Prepare the path
			AstarProfiler.StartProfile ("Path Prepare");
			p.Prepare ();
			AstarProfiler.EndProfile ();
			AstarProfiler.EndFastProfile (0);
			
			if (!p.IsDone()) {
				
				//For debug uses, we set the last computed path to p, so we can view debug info on it in the editor (scene view).
				active.debugPath = p;
				
				//Initialize the path, now ready to begin search
				AstarProfiler.StartProfile ("Path Initialize");
				p.Initialize ();
				AstarProfiler.EndProfile ();
				
				//The error can turn up in the Init function
				while (!p.IsDone ()) {
					//Do some work on the path calculation.
					//The function will return when it has taken too much time
					//or when it has finished calculation
					AstarProfiler.StartFastProfile(2);
					
					AstarProfiler.StartProfile ("Path Calc Step");
					p.CalculateStep (targetTick);
					AstarProfiler.EndFastProfile(2);
					p.searchIterations++;
					
					AstarProfiler.EndProfile ();
					
					//If the path has finished calculation, we can break here directly instead of sleeping
					if (p.IsDone ()) break;
					
					AstarProfiler.EndFastProfile(8);
					totalTicks += System.DateTime.UtcNow.Ticks-startTicks;
					//Yield/sleep so other threads can work
						
					AstarProfiler.EndProfile ();
					yield return 0;
					AstarProfiler.StartProfile ("Path Calc");
					
					startTicks = System.DateTime.UtcNow.Ticks;
					AstarProfiler.StartFastProfile(8);
					
					//Cancel function (and thus the thread) if no more paths should be accepted.
					//This is done when the A* object is about to be destroyed
					//The path is returned and then this function will be terminated (see similar IF statement higher up in the function)
					if (!active.acceptNewPaths) {
						p.Error ();
					}
					
					targetTick = System.DateTime.UtcNow.Ticks + maxTicks;
				}
				
				totalTicks += System.DateTime.UtcNow.Ticks-startTicks;
				p.duration = totalTicks*0.0001F;
				
			}
			
			//Log path results
			AstarProfiler.StartProfile ("Log Path Results");
			active.LogPathResults (p);
			AstarProfiler.EndProfile ();
			
			AstarProfiler.EndFastProfile(8);
			
			AstarProfiler.StartFastProfile(13);
			if (OnPathPostSearch != null) {
				OnPathPostSearch (p);
			}
			AstarProfiler.EndFastProfile(13);
			
			//Push the path onto the return stack
			//It will be detected by the main Unity thread and returned as fast as possible (the next late update)
			pathReturnStack.Push (p);
			
			p.AdvanceState (PathState.ReturnQueue);
			
			AstarProfiler.EndProfile ();
			
			threadSafeUpdateState = true;
			
			//Wait a bit if we have calculated a lot of paths
			if (System.DateTime.UtcNow.Ticks > targetTick) {
				yield return 0;
				targetTick = System.DateTime.UtcNow.Ticks + maxTicks;
				numPaths = 0;
			}
		}
		
		//Debug.LogError ("Error : This part should never be reached");
		//System.Threading.Interlocked.Decrement (ref numActiveThreads);
	}
示例#4
0
	/* Checks if the OnThreadSafeCallback callback needs to be (and can) be called and if so, does it.
	 * Unpauses pathfinding threads after that.
	 * Thread safe callbacks can only be called when no pathfinding threads are running at the momment.
	 * Should only be called from the main unity thread.
	 * \see FlushThreadSafeCallbacks
	 * \see Update
	 */
	private static void TryCallThreadSafeCallbacks () {
		if (threadSafeUpdateState) {
			if (OnThreadSafeCallback != null) {
				OnVoidDelegate tmp = OnThreadSafeCallback;
				OnThreadSafeCallback = null;
				tmp ();
			}
			threadSafeUpdateFlag.Set();
			threadSafeUpdateState = false;
		}
	}
示例#5
0
		public AstarWorkItem (OnVoidDelegate init, System.Func<bool, bool> update) {
			this.init = init;
			this.update = update;
		}
示例#6
0
	/** Will send a callback when it is safe to update nodes. This is defined as between the path searches.
	  * This callback will only be sent once and is nulled directly after the callback has been sent
	  * \warning Note that these callbacks are not thread safe when using multithreading, DO NOT call any part of the Unity API from these callbacks except for Debug.Log
	  * \see RegisterThreadSafeNodeUpdate
	  */
	public static void RegisterSafeNodeUpdate (OnVoidDelegate callback) {
		if (callback == null) {
			return;
		}
		
		if (isCalculatingPaths) {
			OnSafeNodeUpdate += callback;
		} else {
			callback ();
		}
	}
示例#7
0
	public static void RegisterSafeUpdate (OnVoidDelegate callback, bool threadSafe) {
		RegisterSafeUpdate ( callback );
	}
示例#8
0
	/** Returns the next free path ID. If the next free path ID overflows 65535, a cleanup operation is queued
	  * \see Pathfinding.CleanupPath65K */
	public ushort GetNextPathID ()
	{
		if (nextFreePathID == 0) {
			nextFreePathID++;
			
			//Queue a cleanup operation to zero all path IDs
			//StartPath (new CleanupPath65K ());
			Debug.Log ("65K cleanup");
			
			//ushort toBeReturned = nextFreePathID;
			
			if (On65KOverflow != null) {
				OnVoidDelegate tmp = On65KOverflow;
				On65KOverflow = null;
				tmp ();
			}
			
			//return nextFreePathID++;
		}
		return nextFreePathID++;
	}
示例#9
0
	/** Updates the graphs based on the #graphUpdateQueue
	 * \see UpdateGraphs
	 */
	private void DoUpdateGraphs () {
		
		isUpdatingGraphs = true;
		lastGraphUpdate = Time.realtimeSinceStartup;
		
		if (OnGraphsWillBeUpdated2 != null) {
			OnVoidDelegate callbacks = OnGraphsWillBeUpdated2;
			OnGraphsWillBeUpdated2 = null;
			callbacks ();
		}
		
		if (OnGraphsWillBeUpdated != null) {
			OnVoidDelegate callbacks = OnGraphsWillBeUpdated;
			OnGraphsWillBeUpdated = null;
			callbacks ();
		}
		
		//If any GUOs requires a flood fill, then issue it, otherwise we can skip it to save processing power
		bool anyRequiresFloodFill = false;
		
		if (graphUpdateQueue != null) {
			while (graphUpdateQueue.Count > 0) {
				GraphUpdateObject ob = graphUpdateQueue.Dequeue ();
				
				if (ob.requiresFloodFill) anyRequiresFloodFill = true;
				
				foreach (IUpdatableGraph g in astarData.GetUpdateableGraphs ()) {
						
						g.UpdateArea (ob);
				}
				
			}
		}
		isUpdatingGraphs = false;
		
		if (anyRequiresFloodFill) {
			FloodFill ();
		}
		
		if (OnGraphsUpdated != null) {
			OnGraphsUpdated (this);
		}
		
		//Debug.Log ("Updating Graphs... "+((Time.realtimeSinceStartup-startUpdate)*1000).ToString ("0.00"));
		//resetEvent.Set ();
		//resetFlag = true;
	}
示例#10
0
	public void RegisterCanUpdateGraphs (OnVoidDelegate callback, OnVoidDelegate callback2 = null) {
		
		OnGraphsWillBeUpdated += callback;
		
		if (callback2 != null) {
			OnGraphsWillBeUpdated2 += callback2;
		}
		
		if (limitGraphUpdates && Time.realtimeSinceStartup-lastGraphUpdate < maxGraphUpdateFreq) {
			if (!graphUpdateRoutineRunning) {
				StartCoroutine (DelayedGraphUpdate ());
			}
		} else {
			if (useMultithreading) {
				lock (lockObject) {
					DoUpdateGraphs ();
				}
			} else if (!isRegisteredForUpdate) {
				//Only add a callback for the first item
				isRegisteredForUpdate = true;
				OnGraphUpdate += DoUpdateGraphs;
			}
		}
	}
示例#11
0
	/** Returns the next free path ID. If the next free path ID overflows 65535, a cleanup operation is queued
	  * \see Pathfinding::CleanupPath65K */
	public int GetNextPathID () {
		if (nextFreePathID > 65535) {
			nextFreePathID = 1;
			
			//Queue a cleanup operation to zero all path IDs
			StartPath (new CleanupPath65K ());
			
			int toBeReturned = nextFreePathID++;
			
			if (On65KOverflow != null) {
				OnVoidDelegate tmp = On65KOverflow;
				On65KOverflow = null;
				tmp ();
			}
			
			return toBeReturned;
		}
		return nextFreePathID++;
	}
示例#12
0
	/** Main pathfinding function. This coroutine will calculate the paths in the pathfinding queue.
	 * \see CalculatePathsThreaded ()
	 */
	public static IEnumerator CalculatePaths () {
		isCalculatingPaths = true;
		
		float ptf = 0F;
		
		int framesWithNoPaths = 0;
		
		//The path currently being computed
		Path p = pathQueueStart;
		
		if (missedPathQueueIncrement) {
			
			missedPathQueueIncrement = false;
			
			if (pathQueueStart.next != null) {
				pathQueueStart = pathQueueStart.next;
				p = pathQueueStart;
			} else {
				Debug.LogError ("Error : No path was added to the queue, but the pathfinding was started");
				yield break;
			}
		}	
			
		while (p != null) {
			framesWithNoPaths = 0;
			
			//Wait for a bit if we have calculated a lot of paths
			if (ptf >= active.maxFrameTime) {
				//for (int i=0;i<(active.pathExecutionDelay < 1 ? 1 : active.pathExecutionDelay);i++) {
					yield return 0;
				//}
				ptf = 0F;
			}
			
			bool skip = false;
			if (p.processed) {
				skip = true;
				p.error = true;
				p.errorLog += "Calling pathfinding with an already processed path";
				Debug.LogError (p.errorLog);
			}
			
			if (!skip) {
				
				//Note: These Profiler calls will not get included if the AstarProfiler.cs script does not have the #define DEBUG enabled
				//[Conditional] attributes FTW!
				AstarProfiler.StartFastProfile (8);
				
				if (OnPathPreSearch != null) {
					OnPathPreSearch (p);
				}
				
				if (OnGraphUpdate != null) {
					OnGraphUpdate (p);
				}
				
				if (OnSafeNodeUpdate != null) {
					OnSafeNodeUpdate ();
					OnSafeNodeUpdate = null;
				}
				
				AstarProfiler.EndFastProfile (8);
					
				/*currentPath++;
				if (currentPath >= active.pathQueueLength) {
					currentPath = 0;
				}*/
				
				//Path p = pathQueue[currentPath]; 
				//Path p = pathQueueStarts;
				
				AstarProfiler.StartFastProfile (0);
				
				p.Prepare ();
				
				AstarProfiler.EndFastProfile (0);
				
				if (!p.error) {
					
					if (OnPathPreSearch != null) {
						OnPathPreSearch (p);
					}
				
					//ptf += 0.9F;
					
					//For debug uses, we set the last computed path to p, so we can view debug info on it in the editor (scene view).
					active.debugPath = p;
					
					AstarProfiler.StartFastProfile (1);
					
					p.Initialize ();
					
					AstarProfiler.EndFastProfile (1);
					
					ptf += p.duration+0.1F;
					
					//The error can turn up in the Init function
					if (!p.IsDone ()) {
						
						AstarProfiler.StartFastProfile (2);
						
						ptf += p.CalculateStep (active.maxFrameTime-ptf);
						p.searchIterations++;
						
						AstarProfiler.EndFastProfile (2);
						
						while (!p.IsDone ()) {
							yield return 0;
							
							AstarProfiler.StartFastProfile (2);
							
							//Reset the counter for this frame since we have called yield at least once and has now only computed 1 path this frame
							ptf = p.CalculateStep (active.maxFrameTime);
							p.searchIterations++;
							
							AstarProfiler.EndFastProfile (2);
						}
					}
					
					AstarProfiler.StartFastProfile (13);
					
					if (OnPathPostSearch != null) {
						OnPathPostSearch (p);
					}
					
					AstarProfiler.EndFastProfile (13);
					//1000F));//.ToString ("0.0") +"ms");	  //Return Time: "+((Time.realtimeSinceStartup-p.realStartTime)*1000).ToString ("0.0")+"ms");
					
				} else {
					//0.1 is added to prevent infinite loop when the duration is to short to measure
					ptf += p.duration+0.05F;
				}
				
				
				AstarProfiler.StartFastProfile (9);
					
				PathsCompleted++;
				
				if (p.pathID > 2) {
					TotalSearchedNodes += p.searchedNodes;
					TotalSearchTime += (System.Int64)System.Math.Round (p.duration*10000);
				}
				
				if (OnPathPostSearch != null) {
					OnPathPostSearch (p);
				}
				
				p.processed = true;
				
				p.ReturnPath ();
				
				
				//Add stuff to the log
				active.LogPathResults (p);
			}
			
			AstarProfiler.EndFastProfile (9);
			
			while (pathQueueStart.next == null) {
				ptf = 0;
				
				framesWithNoPaths++;
				
				if (OnSafeNodeUpdate != null) {
					OnSafeNodeUpdate ();
					OnSafeNodeUpdate = null;
				}
				
				if (OnGraphUpdate != null) {
					OnGraphUpdate (p);
				}
				
				if (framesWithNoPaths > 5000) {
					missedPathQueueIncrement = true;
					isCalculatingPaths = false;
					yield break;
				}
			
				yield return 0;
				
			}
			
			Path tmp = pathQueueStart;
			
			pathQueueStart = pathQueueStart.next;
			p = pathQueueStart;
			
			//Remove the reference to prevent possible memory leaks
			//If for example the first path computed was stored somewhere, it would through the linked list contain references to all comming paths to be computed, and thus the nodes those paths searched. Possibly other paths too through the "activePath"/"script" variable on each node. That adds up
			tmp.next = null;
		}
		
		Debug.LogError ("Error : This part should never be reached");
		isCalculatingPaths = false;
	}
示例#13
0
	/** Will send a callback when it is safe to update nodes. This is defined as between the path searches.
	  * This callback will only be sent once and is nulled directly after the callback has been sent. This callback is also threadsafe, and because of that, using it often might affect performance when called often and multithreading is enabled due to locking and synchronisation.
	  * \see RegisterSafeNodeUpdate
	  */
	public static void RegisterThreadSafeNodeUpdate (OnVoidDelegate callback) {
		if (callback == null) {
			return;
		}
		
		if (isCalculatingPaths) {
			if (active.useMultithreading) {
				lock (lockObject) {
					callback ();
				}
			} else {
				OnSafeNodeUpdate += callback;
			}
		} else {
			callback ();
		}
	}
示例#14
0
	/** Clears up variables and other stuff, destroys graphs.
	 * Note that when destroying an AstarPath object, all static variables such as callbacks will be cleared.
	 */
	public void OnDestroy () {
		
		if (logPathResults == PathLog.Heavy)
			Debug.Log ("+++ AstarPath Component Destroyed - Cleaning Up Pathfinding Data +++");
		
		if ( active != this ) return;
		
		
		//Don't accept any more path calls to this AstarPath instance.
		//This will cause all eventual multithreading threads to exit
		pathQueue.TerminateReceivers();

		BlockUntilPathQueueBlocked();
		FlushWorkItems ();

		if (logPathResults == PathLog.Heavy)
			Debug.Log ("Processing Eventual Work Items");
		
		// Process work items until done 
		// Nope, don't do this
		//PerformBlockingActions (true);
		
		//Resume graph update thread, will cause it to terminate
		graphUpdateAsyncEvent.Set();
		
		//Try to join pathfinding threads
		if (threads != null) {
			for (int i=0;i<threads.Length;i++) {
#if UNITY_WEBPLAYER
				if (!threads[i].Join(200)) {
					Debug.LogError ("Could not terminate pathfinding thread["+i+"] in 200ms." +
						"Not good.\nUnity webplayer does not support Thread.Abort\nHoping that it will be terminated by Unity WebPlayer");
				}
#else
				if (!threads[i].Join (50)) {
					Debug.LogError ("Could not terminate pathfinding thread["+i+"] in 50ms, trying Thread.Abort");
					threads[i].Abort ();
				}
#endif
			}
		}
		
		if (logPathResults == PathLog.Heavy)
			Debug.Log ("Returning Paths");
		
		
		//Return all paths
		ReturnPaths (false);
		//Just in case someone happened to request a path in ReturnPath() (even though they should get canceled)
		pathReturnStack.PopAll ();
		
		if (logPathResults == PathLog.Heavy)
			Debug.Log ("Destroying Graphs");

		
		//Clean graphs up
		astarData.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
		floodStack = null;
		graphUpdateQueue = null;
		
		//Clear all callbacks
		OnDrawGizmosCallback	= null;
		OnAwakeSettings			= null;
		OnGraphPreScan			= null;
		OnGraphPostScan			= null;
		OnPathPreSearch			= null;
		OnPathPostSearch		= null;
		OnPreScan				= null;
		OnPostScan				= null;
		OnLatePostScan			= null;
		On65KOverflow			= null;
		OnGraphsUpdated			= null;
		OnSafeCallback			= null;
		OnThreadSafeCallback	= null;
		
		threads = null;
		threadInfos = null;
		
		PathsCompleted = 0;
		
		active = null;
		
	}
示例#15
0
	/** Clears up variables and other stuff, destroys graphs.
	 * Note that when destroying an AstarPath object, all static variables such as callbacks will be cleared.
	 */
	public void OnDestroy () {
		
		if (logPathResults == PathLog.Heavy)
			Debug.Log ("+++ AstarPath Component Destroyed - Cleaning Up Pathfinding Data +++");
		
		
		//Don't accept any more path calls to this AstarPath instance.
		//This will cause all eventual multithreading threads to exit
		TrickAbortThreads ();
		
		
		//Try to join pathfinding threads
		if (threads != null) {
			for (int i=0;i<threads.Length;i++) {
#if UNITY_WEBPLAYER
				if (!threads[i].Join(200)) {
					Debug.LogError ("Could not terminate pathfinding thread["+i+"] in 200ms." +
						"Not good.\nUnity webplayer does not support Thread.Abort\nHoping that it will be terminated by Unity WebPlayer");
				}
#else
				if (!threads[i].Join (50)) {
					Debug.LogError ("Could not terminate pathfinding thread["+i+"] in 50ms, trying Thread.Abort");
					threads[i].Abort ();
				}
#endif
			}
		}
		
		if (logPathResults == PathLog.Heavy)
			Debug.Log ("Destroying Graphs");

		
		//Clean graphs up
		if (astarData.graphs != null) {
			for (int i=0;i<astarData.graphs.Length;i++) {
				astarData.graphs[i].OnDestroy ();
			}
		}
		astarData.graphs = null;
		
		if (logPathResults == PathLog.Heavy)
			Debug.Log ("Returning Paths");
		
		
		//Return all paths with errors
		/*Path p = pathReturnStack.PopAll ();
		while (p != null) {
			p.Error ();
			p.LogError ("Canceled because AstarPath object was destroyed\n");
			p.AdvanceState (PathState.Returned);
			p.ReturnPath ();
			Path tmp = p;
			p = p.next;
			tmp.next = null;
		}*/
		ReturnPaths (false);
		//Just in case someone happened to request a path in ReturnPath() (even though they should get canceled)
		pathReturnStack.PopAll ();
		
		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
		floodStack = null;
		graphUpdateQueue = null;
		
		//Clear all callbacks
		OnDrawGizmosCallback	= null;
		OnAwakeSettings			= null;
		OnGraphPreScan			= null;
		OnGraphPostScan			= null;
		OnPathPreSearch			= null;
		OnPathPostSearch		= null;
		OnPreScan				= null;
		OnPostScan				= null;
		OnLatePostScan			= null;
		On65KOverflow			= null;
		OnGraphsUpdated			= null;
		OnSafeCallback			= null;
		OnThreadSafeCallback	= null;
		
		pathQueue.Clear ();
		threads = null;
		threadInfos = null;
		numActiveThreads = 0;
		ResetQueueStates ();
		
		PathsCompleted = 0;
		
		active = null;
		
	}
示例#16
0
	/** Will send a callback when it is safe to update nodes. This is defined as between the path searches.
	  * This callback will only be sent once and is nulled directly after the callback has been sent.
	  * When using more threads than one, calling this often might decrease pathfinding performance due to a lot of idling in the threads.
	  * Not performance as in it will use much CPU power,
	  * but performance as in the number of paths per second will probably go down (though your framerate might actually increase a tiny bit)
	  * 
	  * You should only call this function from the main unity thread (i.e normal game code).
	  * 
	  * \warning Note that if you do not set \a threadSafe to true, the callback might not be called from the Unity thread,
	  * DO NOT call any part of the Unity API from those callbacks except for Debug.Log
	  * 
	  * \code
Node node = AstarPath.active.GetNearest (transform.position).node;
AstarPath.RegisterSafeUpdate (delegate () {
	node.walkable = false;
}, false);
\endcode

\code
Node node = AstarPath.active.GetNearest (transform.position).node;
AstarPath.RegisterSafeUpdate (delegate () {
	node.position = (Int3)transform.position;
}, true);
\endcode
	  * Note that the second example uses transform in the callback, and must thus be threadSafe.
	  */
	public static void RegisterSafeUpdate (OnVoidDelegate callback, bool threadSafe) {
		if (callback == null || !Application.isPlaying) {
			return;
		}
		
		if (active.pathQueue.AllReceiversBlocked) {
			// We need to lock here since we cannot be sure that this is the Unity Thread
			// and therefore we cannot be sure that some other thread will not unblock the queue while we are processing the callback
			active.pathQueue.Lock();
			try {
				//Check again
				if (active.pathQueue.AllReceiversBlocked) {
					callback ();
					return;
				}
			} finally {
				active.pathQueue.Unlock();
			}
		}
		
		lock (safeUpdateLock) {			
			if (threadSafe)
				OnThreadSafeCallback += callback;
			else
				OnSafeCallback += callback;
		}
		//Block path queue so that the above callbacks may be called
		active.pathQueue.Block();
		
	}
示例#17
0
	/** Will send a callback when it is safe to update nodes. This is defined as between the path searches.
	  * This callback will only be sent once and is nulled directly after the callback has been sent.
	  * When using more threads than one, calling this often might decrease pathfinding performance due to a lot of idling in the threads.
	  * Not performance as in it will use much CPU power,
	  * but performance as in the number of paths per second will probably go down (though your framerate might actually increase a tiny bit)
	  * 
	  * You should only call this function from the main unity thread (i.e normal game code).
	  * 
	  * \warning Note that if you do not set \a threadSafe to true, the callback might not be called from the Unity thread,
	  * DO NOT call any part of the Unity API from those callbacks except for Debug.Log
	  * 
	  * \code
Node node = AstarPath.active.GetNearest (transform.position).node;
AstarPath.RegisterSafeUpdate (delegate () {
	node.walkable = false;
}, false);
\endcode

\code
Node node = AstarPath.active.GetNearest (transform.position).node;
AstarPath.RegisterSafeUpdate (delegate () {
	node.position = (Int3)transform.position;
}, true);
\endcode
	  * Note that the second example uses transform in the callback, and must thus be threadSafe.
	  */
	public static void RegisterSafeUpdate (OnVoidDelegate callback, bool threadSafe) {
		if (callback == null || !Application.isPlaying) {
			return;
		}
		
		//If it already is safe to call any callbacks. call them.
		if (threadSafeUpdateState) {
			callback ();
			return;
		}
		
		if (IsUsingMultithreading) {
			int max = 0;
			//Try to aquire all locks, this will not block
			for (int i=0;i<threadInfos.Length;i++) {
				if (Monitor.TryEnter (threadInfos[i].Lock))
					max = i;
				else
					break;
			}
			
			//We could aquire all locks
			if (max == threadInfos.Length-1) {
				//Temporarily set threadSafeUpdateState to true to tell error checking code that it is safe to update graphs
				threadSafeUpdateState = true;
				callback ();
				threadSafeUpdateState = false;
			}
			
			//Release all locks we managed to aquire
			for (int i=0;i<=max;i++)
				Monitor.Exit (threadInfos[i].Lock);
			
			//If we could not aquire all locks, put it in a queue to be called as soon as possible
			if (max != threadInfos.Length-1) {
				//To speed up things, the path queue flag is reset and it is flagged that it should not be set until callbacks have been updated
				//This will trick the threads to think there is nothing to process and go to sleep (thereby allowing us to update graphs)
				doSetQueueState = false;
				pathQueueFlag.Reset();
				
				lock (safeUpdateLock) {
					
					if (threadSafe)
						OnThreadSafeCallback += callback;
					else
						OnSafeCallback += callback;
					
					//SetSafeUpdateState (true);
					safeUpdateFlag.Set();
				}
			}
		} else {
			
			if (threadSafeUpdateState) {
				callback();
			} else {
				lock (safeUpdateLock) {
					if (threadSafe)
						OnThreadSafeCallback += callback;
					else
						OnSafeCallback += callback;
				}
			}
		}
	}
示例#18
0
		public AstarWorkItem (System.Func<bool, bool> update) {
			init = null;
			this.update = update;
		}
示例#19
0
	private static void LockThread (System.Object _astar) {
		AstarPath astar = (AstarPath)_astar;
		
		while (astar.acceptNewPaths) {
			safeUpdateFlag.WaitOne ();
			
			PathThreadInfo[] infos = threadInfos;
			if (infos == null) { Debug.LogError ("Path Thread Infos are null"); return; }
			
			//Claim all locks
			for (int i=0;i<infos.Length;i++)
				Monitor.Enter (infos[i].Lock);
			
			lock (safeUpdateLock) {
				safeUpdateFlag.Reset ();
				OnVoidDelegate tmp = OnSafeCallback;
				OnSafeCallback = null;
				if (tmp != null) tmp();
				
				if (OnThreadSafeCallback != null) {
					threadSafeUpdateFlag.Reset ();
				} else {
					threadSafeUpdateFlag.Set();
				}
			}
			threadSafeUpdateState = true;
			
			//Wait until threadsafe updates have been called
			threadSafeUpdateFlag.WaitOne();
			
			//We can set the pathQueueFlag now since we have updated all graphs
			doSetQueueState = true;
			pathQueueFlag.Set();
			
			//Release all locks
			for (int i=0;i<infos.Length;i++)
				Monitor.Exit (infos[i].Lock);
			
		}
	}
示例#20
0
	private void PerformBlockingActions (bool force = false, bool unblockOnComplete = true) {
		if (pathQueue.AllReceiversBlocked) {
			// Return all paths before starting blocking actions (these might change the graph and make returned paths invalid (at least the nodes))
			ReturnPaths (false);
			
			//This must be called before since otherwise ProcessWorkItems might start pathfinding again
			//if no work items are left to be processed resulting in thread safe callbacks never being called
			if (OnThreadSafeCallback != null) {
				OnVoidDelegate tmp = OnThreadSafeCallback;
				OnThreadSafeCallback = null;
				tmp ();
			}
			
			if (ProcessWorkItems (force) == 2) {
				//At this stage there are no more work items, restart pathfinding threads
				workItemsQueued = false;
				if (unblockOnComplete) {
					pathQueue.Unblock();
				}
			}
		}
		
	}
示例#21
0
	/** Main pathfinding function (multithreaded). This function will calculate the paths in the pathfinding queue when multithreading is enabled.
	 * \see CalculatePaths
	 * \astarpro 
	 */
	public static IEnumerator CalculateAdv (System.Object _threadInfo) {
		
		
		//Increment the counter for how many threads are calculating
		System.Threading.Interlocked.Increment (ref numActiveThreads);
		
		PathThreadInfo threadInfo;
		try {
			threadInfo = (PathThreadInfo)_threadInfo;
		} catch (System.Exception e) {
			Debug.LogError ("Arguments to pathfinding threads must be of type ThreadStartInfo\n"+e);
			throw new System.ArgumentException ("Argument must be of type ThreadStartInfo",e);
		}
		
		int numPaths = 0;
		
		//Initialize memory for this thread
		NodeRunData runData = threadInfo.runData;
		
		//Create a binary heap
		runData.open = new BinaryHeapM (InitialBinaryHeapSize);
		
#if !SingleCoreOptimize
		if (runData.nodes == null)
			throw new System.NullReferenceException ("NodeRuns must be assigned to the threadInfo.runData.nodes field before threads are started\nthreadInfo is an argument to the thread functions");
#endif
		
		//Max number of ticks before yielding/sleeping
		long maxTicks = (long)(active.maxFrameTime*10000);
		long targetTick = System.DateTime.Now.Ticks + maxTicks;
		
		threadSafeUpdateState = true;
		
		while (true) {
			
			//The path we are currently calculating
			Path p = null;
			
			while (true) {
				//Cancel function (and thus the thread) if no more paths should be accepted.
				//This is done when the A* object is about to be destroyed
				if (!active.acceptNewPaths) {
					System.Threading.Interlocked.Decrement (ref numActiveThreads);
					yield break;
				}
				
				if (pathQueue.Count > 0) {
					p = pathQueue.Dequeue ();
				}
				
				//System.Threading.Interlocked.Increment(ref threadsIdle);
				
				//Last thread alive
				//Call callbacks if any are requested
				OnVoidDelegate tmp = OnSafeCallback;
				OnSafeCallback = null;
				if (tmp != null) tmp();
				
				TryCallThreadSafeCallbacks ();
				//The threadSafeUpdateState is still enabled since this is coroutine mode
				threadSafeUpdateState = true;
				
				if (p == null)
					yield return 0;
				
				//System.Threading.Interlocked.Decrement(ref threadsIdle);
				
				if (p != null) break;
				
			}
			
			//Max number of ticks we are allowed to continue working in one run
			//One tick is 1/10000 of a millisecond
			maxTicks = (long)(active.maxFrameTime*10000);
			
			threadSafeUpdateState = false;
			
			if (p.processed) {
				p.error = true;
				p.errorLog += "Requesting to process an already processed path\n";
				Debug.LogError (p.errorLog);
			} else {
				//Path IDs have overflowed 65K, cleanup is needed
				if (runData.pathID > p.pathID) {
					runData.ClearPathIDs ();
				}
				
				//Assign relevant path data to the runData
				runData.path = p;
				runData.pathID = p.pathID;
				p.runData = runData;
				
				//Call some callbacks
				if (OnPathPreSearch != null) {
					OnPathPreSearch (p);
				}
				
				numPaths++;
				
				//Tick for when the path started, used for calculating how long time the calculation took
				long startTicks = System.DateTime.Now.Ticks;
				long totalTicks = 0;
				
				AstarProfiler.StartFastProfile(8);
				
				AstarProfiler.StartFastProfile(0);
				//Prepare the path
				p.Prepare ();
				
				AstarProfiler.EndFastProfile (0);
				
				if (!p.error) {
					
					//For debug uses, we set the last computed path to p, so we can view debug info on it in the editor (scene view).
					active.debugPath = p;
					
					//Initialize the path, now ready to begin search
					p.Initialize ();
					
					//The error can turn up in the Init function
					while (!p.IsDone ()) {
						//Do some work on the path calculation.
						//The function will return when it has taken too much time
						//or when it has finished calculation
						AstarProfiler.StartFastProfile(2);
						p.CalculateStep (targetTick);
						AstarProfiler.EndFastProfile(2);
						p.searchIterations++;
						
						//If the path has finished calculation, we can break here directly instead of sleeping
						if (p.IsDone ()) break;
						
						AstarProfiler.EndFastProfile(8);
						totalTicks += System.DateTime.Now.Ticks-startTicks;
						//Yield/sleep so other threads can work
						yield return 0;
						startTicks = System.DateTime.Now.Ticks;
						AstarProfiler.StartFastProfile(8);
						
						//Cancel function (and thus the thread) if no more paths should be accepted.
						//This is done when the A* object is about to be destroyed
						//The path is returned and then this function will be terminated (see similar IF statement higher up in the function)
						if (!active.acceptNewPaths) {
							p.error = true;
						}
						
						targetTick = System.DateTime.Now.Ticks + maxTicks;
					}
					
					totalTicks += System.DateTime.Now.Ticks-startTicks;
					p.duration = totalTicks*0.0001F;
					
					//Log path results
					active.LogPathResults (p);
					
#if ProfileAstar
					Interlocked.Increment (ref PathsCompleted);
					Interlocked.Add (ref TotalSearchedNodes, p.searchedNodes);
#endif
				}
				
				AstarProfiler.EndFastProfile(8);
				
				AstarProfiler.StartFastProfile(13);
				if (OnPathPostSearch != null) {
					OnPathPostSearch (p);
				}
				AstarProfiler.EndFastProfile(13);
				//Push the path onto the return stack
				//It will be detected by the main Unity thread and returned as fast as possible (the next late update)
				pathReturnStack.Push (p);
				
				//Flag the path as processed
				//Serves as a check to make sure a user does not try to calculate the same path instance multiple times
				p.processed = true;
			}
			
			threadSafeUpdateState = true;
			
			//Wait a bit if we have calculated a lot of paths
			if (System.DateTime.Now.Ticks > targetTick) {
				yield return 0;
				targetTick = System.DateTime.Now.Ticks + maxTicks;
				numPaths = 0;
			}
		}
		
		//Debug.LogError ("Error : This part should never be reached");
		//System.Threading.Interlocked.Decrement (ref numActiveThreads);
	}