Esempio n. 1
0
        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[]);
            }
        }