protected IEnumerable <Progress> ScanAllTiles() { transform = CalculateTransform(); InitializeTileInfo(); // If this is true, just fill the graph with empty tiles if (scanEmptyGraph) { FillWithEmptyTiles(); yield break; } // A walkableClimb higher than walkableHeight can cause issues when generating the navmesh since then it can in some cases // Both be valid for a character to walk under an obstacle and climb up on top of it (and that cannot be handled with navmesh without links) // The editor scripts also enforce this but we enforce it here too just to be sure walkableClimb = Mathf.Min(walkableClimb, walkableHeight); yield return(new Progress(0, "Finding Meshes")); var bounds = transform.Transform(new Bounds(forcedBoundsSize * 0.5f, forcedBoundsSize)); var meshes = CollectMeshes(bounds); var buckets = PutMeshesIntoTileBuckets(meshes); ListPool <RasterizationMesh> .Release(meshes); Queue <Int2> tileQueue = new Queue <Int2>(); // Put all tiles in the queue for (int z = 0; z < tileZCount; z++) { for (int x = 0; x < tileXCount; x++) { tileQueue.Enqueue(new Int2(x, z)); } } #if UNITY_WEBGL && !UNITY_EDITOR // WebGL does not support multithreading so we will do everything synchronously instead BuildTiles(tileQueue, buckets, null, 0); #else // Fire up a bunch of threads to scan the graph in parallel int threadCount = Mathf.Min(tileQueue.Count, Mathf.Max(1, AstarPath.CalculateThreadCount(ThreadCount.AutomaticHighLoad))); var waitEvents = new ManualResetEvent[threadCount]; for (int i = 0; i < waitEvents.Length; i++) { waitEvents[i] = new ManualResetEvent(false); #if NETFX_CORE // Need to make a copy here, otherwise it may refer to some other index when the task actually runs var threadIndex = i; System.Threading.Tasks.Task.Run(() => BuildTiles(tileQueue, buckets, waitEvents[threadIndex], threadIndex)); #else ThreadPool.QueueUserWorkItem(state => BuildTiles(tileQueue, buckets, waitEvents[(int)state], (int)state), i); #endif } // Prioritize responsiveness while playing // but when not playing prioritize throughput // (the Unity progress bar is also pretty slow to update) int timeoutMillis = Application.isPlaying ? 1 : 200; while (!WaitHandle.WaitAll(waitEvents, timeoutMillis)) { int count; lock (tileQueue) count = tileQueue.Count; yield return(new Progress(Mathf.Lerp(0.1f, 0.9f, (tiles.Length - count + 1) / (float)tiles.Length), "Generating Tile " + (tiles.Length - count + 1) + "/" + tiles.Length)); } #endif yield return(new Progress(0.9f, "Assigning Graph Indices")); // Assign graph index to nodes uint graphIndex = (uint)AstarPath.active.data.GetGraphIndex(this); GetNodes(node => node.GraphIndex = graphIndex); #if UNITY_WEBGL && !UNITY_EDITOR // Put all tiles in the queue to be connected for (int i = 0; i < tiles.Length; i++) { tileQueue.Enqueue(new Int2(tiles[i].x, tiles[i].z)); } // Calculate synchronously ConnectTiles(tileQueue, null, true, true); #else // First connect all tiles with an EVEN coordinate sum // This would be the white squares on a chess board. // Then connect all tiles with an ODD coordinate sum (which would be all black squares). // This will prevent the different threads that do all // this in parallel from conflicting with each other. // The directions are also done separately // first they are connected along the X direction and then along the Z direction. // Looping over 0 and then 1 for (int coordinateSum = 0; coordinateSum <= 1; coordinateSum++) { for (int direction = 0; direction <= 1; direction++) { for (int i = 0; i < tiles.Length; i++) { if ((tiles[i].x + tiles[i].z) % 2 == coordinateSum) { tileQueue.Enqueue(new Int2(tiles[i].x, tiles[i].z)); } } int numTilesInQueue = tileQueue.Count; for (int i = 0; i < waitEvents.Length; i++) { waitEvents[i].Reset(); #if NETFX_CORE var waitEvent = waitEvents[i]; System.Threading.Tasks.Task.Run(() => ConnectTiles(tileQueue, waitEvent, direction == 0, direction == 1)); #else ThreadPool.QueueUserWorkItem(state => ConnectTiles(tileQueue, state as ManualResetEvent, direction == 0, direction == 1), waitEvents[i]); #endif } while (!WaitHandle.WaitAll(waitEvents, timeoutMillis)) { int count; lock (tileQueue) { count = tileQueue.Count; } yield return(new Progress(0.95f, "Connecting Tile " + (numTilesInQueue - count) + "/" + numTilesInQueue + " (Phase " + (direction + 1 + 2 * coordinateSum) + " of 4)")); } } } #endif // This may be used by the TileHandlerHelper script to update the tiles // while taking NavmeshCuts into account after the graph has been completely recalculated. if (OnRecalculatedTiles != null) { OnRecalculatedTiles(tiles.Clone() as NavmeshTile[]); } }