public IEnumerable <Progress> ScanAsync(NavGraph[] graphsToScan = null) { if (graphsToScan == null) { graphsToScan = graphs; } if (graphsToScan == null) { yield break; } if (isScanning) { throw new System.InvalidOperationException("Another async scan is already running"); } isScanning = true; VerifyIntegrity(); var graphUpdateLock = PausePathfinding(); // Make sure all paths that are in the queue to be returned // are returned immediately // Some modifiers (e.g the funnel modifier) rely on // the nodes being valid when the path is returned pathReturnQueue.ReturnPaths(false); if (!Application.isPlaying) { data.FindGraphTypes(); GraphModifier.FindAllModifiers(); } int startFrame = Time.frameCount; yield return(new Progress(0.05F, "Pre processing graphs")); if (Time.frameCount != startFrame) { throw new System.Exception("Async scanning can only be done in the pro version of the A* Pathfinding Project"); } if (OnPreScan != null) { OnPreScan(this); } GraphModifier.TriggerEvent(GraphModifier.EventType.PreScan); data.LockGraphStructure(); var watch = System.Diagnostics.Stopwatch.StartNew(); // Destroy previous nodes for (int i = 0; i < graphsToScan.Length; i++) { if (graphsToScan[i] != null) { ((IGraphInternals)graphsToScan[i]).DestroyAllNodes(); } } // Loop through all graphs and scan them one by one for (int i = 0; i < graphsToScan.Length; i++) { // Skip null graphs if (graphsToScan[i] == null) { continue; } // Just used for progress information // This graph will advance the progress bar from minp to maxp float minp = Mathf.Lerp(0.1F, 0.8F, (float)(i) / (graphsToScan.Length)); float maxp = Mathf.Lerp(0.1F, 0.8F, (float)(i + 0.95F) / (graphsToScan.Length)); var progressDescriptionPrefix = "Scanning graph " + (i + 1) + " of " + graphsToScan.Length + " - "; // Like a foreach loop but it gets a little complicated because of the exception // handling (it is not possible to yield inside try-except clause). var coroutine = ScanGraph(graphsToScan[i]).GetEnumerator(); while (true) { try { if (!coroutine.MoveNext()) { break; } } catch { isScanning = false; data.UnlockGraphStructure(); graphUpdateLock.Release(); throw; } yield return(coroutine.Current.MapTo(minp, maxp, progressDescriptionPrefix)); } } data.UnlockGraphStructure(); yield return(new Progress(0.8F, "Post processing graphs")); if (OnPostScan != null) { OnPostScan(this); } GraphModifier.TriggerEvent(GraphModifier.EventType.PostScan); FlushWorkItems(); yield return(new Progress(0.9F, "Computing areas")); hierarchicalGraph.RecalculateIfNecessary(); yield return(new Progress(0.95F, "Late post processing")); // Signal that we have stopped scanning here // Note that no yields can happen after this point // since then other parts of the system can start to interfere isScanning = false; if (OnLatePostScan != null) { OnLatePostScan(this); } GraphModifier.TriggerEvent(GraphModifier.EventType.LatePostScan); euclideanEmbedding.dirty = true; euclideanEmbedding.RecalculatePivots(); // Perform any blocking actions FlushWorkItems(); // Resume pathfinding threads graphUpdateLock.Release(); watch.Stop(); lastScanTime = (float)watch.Elapsed.TotalSeconds; System.GC.Collect(); if (logPathResults != PathLog.None && logPathResults != PathLog.OnlyErrors) { Debug.Log("Scanning - Process took " + (lastScanTime * 1000).ToString("0") + " ms to complete"); } }