/** Prepares the path. Searches for start and end nodes and does some simple checking if a path is at all possible */ public override void Prepare() { AstarProfiler.StartProfile("Get Nearest"); //Initialize the NNConstraint nnConstraint.tags = enabledTags; NNInfo startNNInfo = AstarPath.active.GetNearest(startPoint, nnConstraint, startHint); //Tell the NNConstraint which node was found as the start node if it is a PathNNConstraint and not a normal NNConstraint var pathNNConstraint = nnConstraint as PathNNConstraint; if (pathNNConstraint != null) { pathNNConstraint.SetStart(startNNInfo.node); } startPoint = startNNInfo.clampedPosition; startIntPoint = (Int3)startPoint; startNode = startNNInfo.node; //If it is declared that this path type has an end point //Some path types might want to use most of the ABPath code, but will not have an explicit end point at this stage if (hasEndPoint) { NNInfo endNNInfo = AstarPath.active.GetNearest(endPoint, nnConstraint, endHint); endPoint = endNNInfo.clampedPosition; // Note, other methods assume hTarget is (Int3)endPoint hTarget = (Int3)endPoint; endNode = endNNInfo.node; hTargetNode = endNode; } AstarProfiler.EndProfile(); #if ASTARDEBUG if (startNode != null) { Debug.DrawLine((Vector3)startNode.position, startPoint, Color.blue); } if (endNode != null) { Debug.DrawLine((Vector3)endNode.position, endPoint, Color.blue); } #endif if (startNode == null && (hasEndPoint && endNode == null)) { Error(); LogError("Couldn't find close nodes to the start point or the end point"); return; } if (startNode == null) { Error(); LogError("Couldn't find a close node to the start point"); return; } if (endNode == null && hasEndPoint) { Error(); LogError("Couldn't find a close node to the end point"); return; } if (!startNode.Walkable) { #if ASTARDEBUG Debug.DrawRay(startPoint, Vector3.up, Color.red); Debug.DrawLine(startPoint, (Vector3)startNode.position, Color.red); #endif Error(); LogError("The node closest to the start point is not walkable"); return; } if (hasEndPoint && !endNode.Walkable) { Error(); LogError("The node closest to the end point is not walkable"); return; } if (hasEndPoint && startNode.Area != endNode.Area) { Error(); LogError("There is no valid path to the target (start area: " + startNode.Area + ", target area: " + endNode.Area + ")"); return; } }
public override void Apply(Path p) { List <GraphNode> path = p.path; List <Vector3> vectorPath = p.vectorPath; if (path == null || path.Count == 0 || vectorPath == null || vectorPath.Count != path.Count) { return; } List <Vector3> funnelPath = ListPool <Vector3> .Claim(); // Claim temporary lists and try to find lists with a high capacity List <Vector3> left = ListPool <Vector3> .Claim(path.Count + 1); List <Vector3> right = ListPool <Vector3> .Claim(path.Count + 1); AstarProfiler.StartProfile("Construct Funnel"); // Add start point left.Add(vectorPath[0]); right.Add(vectorPath[0]); // Loop through all nodes in the path (except the last one) for (int i = 0; i < path.Count - 1; i++) { // Get the portal between path[i] and path[i+1] and add it to the left and right lists bool portalWasAdded = path[i].GetPortal(path[i + 1], left, right, false); if (!portalWasAdded) { // Fallback, just use the positions of the nodes left.Add((Vector3)path[i].position); right.Add((Vector3)path[i].position); left.Add((Vector3)path[i + 1].position); right.Add((Vector3)path[i + 1].position); } } // Add end point left.Add(vectorPath[vectorPath.Count - 1]); right.Add(vectorPath[vectorPath.Count - 1]); if (!RunFunnel(left, right, funnelPath)) { // If funnel algorithm failed, degrade to simple line funnelPath.Add(vectorPath[0]); funnelPath.Add(vectorPath[vectorPath.Count - 1]); } #if BNICKSON_UPDATED if (0f != obstaclePadding) { PadFunnelPath(ref funnelPath, ref path, p); } #endif // Release lists back to the pool ListPool <Vector3> .Release(p.vectorPath); p.vectorPath = funnelPath; ListPool <Vector3> .Release(left); ListPool <Vector3> .Release(right); }
/** Main pathfinding method. * This method will calculate the paths in the pathfinding queue. * * \see CalculatePathsThreaded * \see StartPath */ IEnumerator CalculatePaths(PathHandler pathHandler) { // Max number of ticks before yielding/sleeping long maxTicks = (long)(astar.maxFrameTime * 10000); long targetTick = System.DateTime.UtcNow.Ticks + maxTicks; while (true) { // The path we are currently calculating Path p = null; AstarProfiler.StartProfile("Path Queue"); // Try to get the next path to be calculated bool blockedBefore = false; while (p == null) { try { p = queue.PopNoBlock(blockedBefore); blockedBefore |= p == null; } catch (ThreadControlQueue.QueueTerminationException) { yield break; } if (p == null) { AstarProfiler.EndProfile(); yield return(null); AstarProfiler.StartProfile("Path Queue"); } } AstarProfiler.EndProfile(); AstarProfiler.StartProfile("Path Calc"); IPathInternals ip = (IPathInternals)p; // Max number of ticks we are allowed to continue working in one run // One tick is 1/10000 of a millisecond maxTicks = (long)(astar.maxFrameTime * 10000); ip.PrepareBase(pathHandler); // Now processing the path // Will advance to Processing ip.AdvanceState(PathState.Processing); // Call some callbacks // It needs to be stored in a local variable to avoid race conditions var tmpOnPathPreSearch = OnPathPreSearch; if (tmpOnPathPreSearch != null) { tmpOnPathPreSearch(p); } // 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"); ip.Prepare(); AstarProfiler.EndProfile("Path Prepare"); AstarProfiler.EndFastProfile(0); // Check if the Prepare call caused the path to complete // If this happens the path usually failed 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). astar.debugPathData = ip.PathHandler; astar.debugPathID = p.pathID; // Initialize the path, now ready to begin search AstarProfiler.StartProfile("Path Initialize"); ip.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"); ip.CalculateStep(targetTick); AstarProfiler.EndFastProfile(2); AstarProfiler.EndProfile(); // If the path has finished calculation, we can break here directly instead of sleeping // Improves latency if (p.IsDone()) { break; } AstarProfiler.EndFastProfile(8); totalTicks += System.DateTime.UtcNow.Ticks - startTicks; // Yield/sleep so other threads can work AstarProfiler.EndProfile(); yield return(null); 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 (queue.IsTerminating) { p.FailWithError("AstarPath object destroyed"); } targetTick = System.DateTime.UtcNow.Ticks + maxTicks; } totalTicks += System.DateTime.UtcNow.Ticks - startTicks; p.duration = totalTicks * 0.0001F; #if ProfileAstar System.Threading.Interlocked.Increment(ref AstarPath.PathsCompleted); #endif } // Cleans up node tagging and other things ip.Cleanup(); AstarProfiler.EndFastProfile(8); // Call the immediate callback // It needs to be stored in a local variable to avoid race conditions var tmpImmediateCallback = p.immediateCallback; if (tmpImmediateCallback != null) { tmpImmediateCallback(p); } AstarProfiler.StartFastProfile(13); // It needs to be stored in a local variable to avoid race conditions var tmpOnPathPostSearch = OnPathPostSearch; if (tmpOnPathPostSearch != null) { tmpOnPathPostSearch(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) returnQueue.Enqueue(p); ip.AdvanceState(PathState.ReturnQueue); AstarProfiler.EndProfile(); // Wait a bit if we have calculated a lot of paths if (System.DateTime.UtcNow.Ticks > targetTick) { yield return(null); targetTick = System.DateTime.UtcNow.Ticks + maxTicks; } } }
public override void CalculateStep(long targetTick) { int counter = 0; //Continue to search while there hasn't ocurred an error and the end hasn't been found while (CompleteState == PathCompleteState.NotCalculated) { searchedNodes++; //--- Here's the important stuff //Close the current node, if the current node satisfies the ending condition, the path is finished if (endingCondition.TargetFound(currentR)) { CompleteState = PathCompleteState.Complete; break; } if (!currentR.flag1) { //Add Node to allNodes allNodes.Add(currentR.node); currentR.flag1 = true; } #if ASTARDEBUG Debug.DrawRay((Vector3)currentR.node.position, Vector3.up * 5, Color.cyan); #endif //--- Here the important stuff ends AstarProfiler.StartFastProfile(4); //Debug.DrawRay ((Vector3)currentR.node.Position, Vector3.up*2,Color.red); //Loop through all walkable neighbours of the node and add them to the open list. currentR.node.Open(this, currentR, pathHandler); AstarProfiler.EndFastProfile(4); //any nodes left to search? if (pathHandler.HeapEmpty()) { CompleteState = PathCompleteState.Complete; break; } //Select the node with the lowest F score and remove it from the open list AstarProfiler.StartFastProfile(7); currentR = pathHandler.PopNode(); AstarProfiler.EndFastProfile(7); //Check for time every 500 nodes, roughly every 0.5 ms usually if (counter > 500) { //Have we exceded the maxFrameTime, if so we should wait one frame before continuing the search since we don't want the game to lag if (System.DateTime.UtcNow.Ticks >= targetTick) { //Return instead of yield'ing, a separate function handles the yield (CalculatePaths) return; } counter = 0; if (searchedNodes > 1000000) { throw new System.Exception("Probable infinite loop. Over 1,000,000 nodes searched"); } } counter++; } }
protected NavmeshTile BuildTileMesh(Voxelize vox, int x, int z, int threadIndex = 0) { AstarProfiler.StartProfile("Build Tile"); AstarProfiler.StartProfile("Init"); vox.borderSize = TileBorderSizeInVoxels; vox.forcedBounds = CalculateTileBoundsWithBorder(x, z); vox.width = tileSizeX + vox.borderSize * 2; vox.depth = tileSizeZ + vox.borderSize * 2; if (!useTiles && relevantGraphSurfaceMode == RelevantGraphSurfaceMode.OnlyForCompletelyInsideTile) { // This best reflects what the user would actually want vox.relevantGraphSurfaceMode = RelevantGraphSurfaceMode.RequireForAll; } else { vox.relevantGraphSurfaceMode = relevantGraphSurfaceMode; } vox.minRegionSize = Mathf.RoundToInt(minRegionSize / (cellSize * cellSize)); AstarProfiler.EndProfile("Init"); // Init voxelizer vox.Init(); vox.VoxelizeInput(transform, CalculateTileBoundsWithBorder(x, z)); AstarProfiler.StartProfile("Filter Ledges"); vox.FilterLedges(vox.voxelWalkableHeight, vox.voxelWalkableClimb, vox.cellSize, vox.cellHeight); AstarProfiler.EndProfile("Filter Ledges"); AstarProfiler.StartProfile("Filter Low Height Spans"); vox.FilterLowHeightSpans(vox.voxelWalkableHeight, vox.cellSize, vox.cellHeight); AstarProfiler.EndProfile("Filter Low Height Spans"); vox.BuildCompactField(); vox.BuildVoxelConnections(); vox.ErodeWalkableArea(CharacterRadiusInVoxels); vox.BuildDistanceField(); vox.BuildRegions(); var cset = new VoxelContourSet(); vox.BuildContours(contourMaxError, 1, cset, Voxelize.RC_CONTOUR_TESS_WALL_EDGES | Voxelize.RC_CONTOUR_TESS_TILE_EDGES); VoxelMesh mesh; vox.BuildPolyMesh(cset, 3, out mesh); AstarProfiler.StartProfile("Build Nodes"); // Position the vertices correctly in graph space (all tiles are laid out on the xz plane with the (0,0) tile at the origin) for (int i = 0; i < mesh.verts.Length; i++) { mesh.verts[i] *= Int3.Precision; } vox.transformVoxel2Graph.Transform(mesh.verts); NavmeshTile tile = CreateTile(vox, mesh, x, z, threadIndex); AstarProfiler.EndProfile("Build Nodes"); AstarProfiler.EndProfile("Build Tile"); return(tile); }
/** Main pathfinding method (multithreaded). * This method will calculate the paths in the pathfinding queue when multithreading is enabled. * * \see CalculatePaths * \see StartPath * * \astarpro */ void CalculatePathsThreaded(PathHandler pathHandler) { #if !ASTAR_FAST_BUT_NO_EXCEPTIONS try { #endif // Max number of ticks we are allowed to continue working in one run. // One tick is 1/10000 of a millisecond. // We need to check once in a while if the thread should be stopped. long maxTicks = (long)(10 * 10000); long targetTick = System.DateTime.UtcNow.Ticks + maxTicks; while (true) { // The path we are currently calculating Path path = queue.Pop(); // Access the internal implementation methods IPathInternals ipath = (IPathInternals)path; AstarProfiler.StartFastProfile(0); ipath.PrepareBase(pathHandler); // Now processing the path // Will advance to Processing ipath.AdvanceState(PathState.Processing); // Call some callbacks if (OnPathPreSearch != null) { OnPathPreSearch(path); } // Tick for when the path started, used for calculating how long time the calculation took long startTicks = System.DateTime.UtcNow.Ticks; // Prepare the path ipath.Prepare(); AstarProfiler.EndFastProfile(0); if (!path.IsDone()) { // For visualization purposes, we set the last computed path to p, so we can view debug info on it in the editor (scene view). astar.debugPathData = ipath.PathHandler; astar.debugPathID = path.pathID; AstarProfiler.StartFastProfile(1); // Initialize the path, now ready to begin search ipath.Initialize(); AstarProfiler.EndFastProfile(1); // Loop while the path has not been fully calculated while (!path.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); ipath.CalculateStep(targetTick); AstarProfiler.EndFastProfile(2); targetTick = System.DateTime.UtcNow.Ticks + maxTicks; // 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 if (queue.IsTerminating) { path.FailWithError("AstarPath object destroyed"); } } path.duration = (System.DateTime.UtcNow.Ticks - startTicks) * 0.0001F; #if ProfileAstar System.Threading.Interlocked.Increment(ref AstarPath.PathsCompleted); System.Threading.Interlocked.Add(ref AstarPath.TotalSearchTime, System.DateTime.UtcNow.Ticks - startTicks); #endif } // Cleans up node tagging and other things ipath.Cleanup(); AstarProfiler.StartFastProfile(9); if (path.immediateCallback != null) { path.immediateCallback(path); } if (OnPathPostSearch != null) { OnPathPostSearch(path); } // 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 hopefully) returnQueue.Enqueue(path); // Will advance to ReturnQueue ipath.AdvanceState(PathState.ReturnQueue); AstarProfiler.EndFastProfile(9); } #if !ASTAR_FAST_BUT_NO_EXCEPTIONS } catch (System.Exception e) { #if !NETFX_CORE if (e is ThreadAbortException || e is ThreadControlQueue.QueueTerminationException) #else if (e is ThreadControlQueue.QueueTerminationException) #endif { if (astar.logPathResults == PathLog.Heavy) { Debug.LogWarning("Shutting down pathfinding thread #" + pathHandler.threadID); } return; } Debug.LogException(e); Debug.LogError("Unhandled exception during pathfinding. Terminating."); // Unhandled exception, kill pathfinding queue.TerminateReceivers(); } #endif Debug.LogError("Error : This part should never be reached."); queue.ReceiverTerminated(); }
/** Main pathfinding method (multithreaded). * This method will calculate the paths in the pathfinding queue when multithreading is enabled. * * \see CalculatePaths * \see StartPath * * \astarpro */ void CalculatePathsThreaded(PathThreadInfo threadInfo) { #if !ASTAR_FAST_BUT_NO_EXCEPTIONS try { #endif //Initialize memory for this thread PathHandler runData = threadInfo.runData; 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"); } //Max number of ticks before yielding/sleeping long maxTicks = (long)(astar.maxFrameTime * 10000); long targetTick = System.DateTime.UtcNow.Ticks + maxTicks; while (true) { // The path we are currently calculating Path p = queue.Pop(); // Access the internal implementation methods IPathInternals ip = (IPathInternals)p; //Max number of ticks we are allowed to continue working in one run //One tick is 1/10000 of a millisecond maxTicks = (long)(astar.maxFrameTime * 10000); AstarProfiler.StartFastProfile(0); ip.PrepareBase(runData); //Now processing the path //Will advance to Processing ip.AdvanceState(PathState.Processing); //Call some callbacks if (OnPathPreSearch != null) { OnPathPreSearch(p); } //Tick for when the path started, used for calculating how long time the calculation took long startTicks = System.DateTime.UtcNow.Ticks; long totalTicks = 0; //Prepare the path ip.Prepare(); 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). astar.debugPathData = ip.PathHandler; astar.debugPathID = p.pathID; AstarProfiler.StartFastProfile(1); //Initialize the path, now ready to begin search ip.Initialize(); AstarProfiler.EndFastProfile(1); //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); ip.CalculateStep(targetTick); AstarProfiler.EndFastProfile(2); // If the path has finished calculation, we can break here directly instead of sleeping if (p.IsDone()) { break; } // Yield/sleep so other threads can work totalTicks += System.DateTime.UtcNow.Ticks - startTicks; Thread.Sleep(0); startTicks = System.DateTime.UtcNow.Ticks; targetTick = startTicks + maxTicks; // 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 if (queue.IsTerminating) { p.Error(); } } totalTicks += System.DateTime.UtcNow.Ticks - startTicks; p.duration = totalTicks * 0.0001F; #if ProfileAstar System.Threading.Interlocked.Increment(ref AstarPath.PathsCompleted); System.Threading.Interlocked.Add(ref AstarPath.TotalSearchTime, totalTicks); #endif } // Cleans up node tagging and other things ip.Cleanup(); AstarProfiler.StartFastProfile(9); if (p.immediateCallback != null) { p.immediateCallback(p); } if (OnPathPostSearch != null) { OnPathPostSearch(p); } // 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 hopefully) returnQueue.Enqueue(p); // Will advance to ReturnQueue ip.AdvanceState(PathState.ReturnQueue); AstarProfiler.EndFastProfile(9); // Wait a bit if we have calculated a lot of paths if (System.DateTime.UtcNow.Ticks > targetTick) { Thread.Sleep(1); targetTick = System.DateTime.UtcNow.Ticks + maxTicks; } } #if !ASTAR_FAST_BUT_NO_EXCEPTIONS } catch (System.Exception e) { #if !NETFX_CORE if (e is ThreadAbortException || e is ThreadControlQueue.QueueTerminationException) #else if (e is ThreadControlQueue.QueueTerminationException) #endif { if (astar.logPathResults == PathLog.Heavy) { Debug.LogWarning("Shutting down pathfinding thread #" + threadInfo.threadIndex); } return; } Debug.LogException(e); Debug.LogError("Unhandled exception during pathfinding. Terminating."); //Unhandled exception, kill pathfinding queue.TerminateReceivers(); } #endif Debug.LogError("Error : This part should never be reached."); queue.ReceiverTerminated(); }
/** Calculates the path until completed or until the time has passed \a targetTick. * Usually a check is only done every 500 nodes if the time has passed \a targetTick. * Time/Ticks are got from System.DateTime.UtcNow.Ticks. * * Basic outline of what the function does for the standard path (Pathfinding.ABPath). * \code * while the end has not been found and no error has ocurred * check if we have reached the end * if so, exit and return the path * * open the current node, i.e loop through its neighbours, mark them as visited and put them on a heap * * check if there are still nodes left to process (or have we searched the whole graph) * if there are none, flag error and exit * * pop the next node of the heap and set it as current * * check if the function has exceeded the time limit * if so, return and wait for the function to get called again * \endcode */ public override void CalculateStep(long targetTick) { int counter = 0; //Continue to search while there hasn't ocurred an error and the end hasn't been found while (CompleteState == PathCompleteState.NotCalculated) { searchedNodes++; //Close the current node, if the current node is the target node then the path is finished if (currentR.node == endNode) { CompleteState = PathCompleteState.Complete; break; } if (currentR.H < partialBestTarget.H) { partialBestTarget = currentR; } AstarProfiler.StartFastProfile(4); //Debug.DrawRay ((Vector3)currentR.node.Position, Vector3.up*2,Color.red); //Loop through all walkable neighbours of the node and add them to the open list. currentR.node.Open(this, currentR, pathHandler); AstarProfiler.EndFastProfile(4); //any nodes left to search? if (pathHandler.HeapEmpty()) { Error(); LogError("No open points, whole area searched"); return; } //Select the node with the lowest F score and remove it from the open list AstarProfiler.StartFastProfile(7); currentR = pathHandler.PopNode(); AstarProfiler.EndFastProfile(7); //Check for time every 500 nodes, roughly every 0.5 ms usually if (counter > 500) { //Have we exceded the maxFrameTime, if so we should wait one frame before continuing the search since we don't want the game to lag if (System.DateTime.UtcNow.Ticks >= targetTick) { //Return instead of yield'ing, a separate function handles the yield (CalculatePaths) return; } counter = 0; if (searchedNodes > 1000000) { throw new System.Exception("Probable infinite loop. Over 1,000,000 nodes searched"); } } counter++; } AstarProfiler.StartProfile("Trace"); if (CompleteState == PathCompleteState.Complete) { Trace(currentR); } else if (calculatePartial && partialBestTarget != null) { CompleteState = PathCompleteState.Partial; Trace(partialBestTarget); } AstarProfiler.EndProfile(); }
protected override void Prepare() { AstarProfiler.StartProfile("Get Nearest"); nnConstraint.tags = enabledTags; var startNNInfo = AstarPath.active.GetNearest(startPoint, nnConstraint); //Tell the NNConstraint which node was found as the start node if it is a PathNNConstraint and not a normal NNConstraint var pathNNConstraint = nnConstraint as PathNNConstraint; if (pathNNConstraint != null) { pathNNConstraint.SetStart(startNNInfo.node); } startPoint = startNNInfo.position; startIntPoint = (Int3)startPoint; startNode = startNNInfo.node; if (startNode == null) { FailWithError("Couldn't find a node close to the start point"); return; } if (!CanTraverse(startNode)) { FailWithError("The node closest to the start point could not be traversed"); return; } // If it is declared that this path type has an end point if (hasEndPoint) { var endNNInfo = AstarPath.active.GetNearest(endPoint, nnConstraint); endPoint = endNNInfo.position; endNode = endNNInfo.node; if (endNode == null) { FailWithError("Couldn't find a node close to the end point"); return; } // This should not trigger unless the user has modified the NNConstraint if (!CanTraverse(endNode)) { FailWithError("The node closest to the end point could not be traversed"); return; } // This should not trigger unless the user has modified the NNConstraint if (startNode.Area != endNode.Area) { FailWithError("There is no valid path to the target"); return; } #if !ASTAR_NO_GRID_GRAPH if (!EndPointGridGraphSpecialCase(endNNInfo.node)) #endif { // Note, other methods assume hTarget is (Int3)endPoint hTarget = (Int3)endPoint; hTargetNode = endNode; // Mark end node with flag1 to mark it as a target point pathHandler.GetPathNode(endNode).flag1 = true; } } AstarProfiler.EndProfile(); }
protected void ScanAllTiles(OnScanStatus statusCallback) { #if ASTARDEBUG System.Console.WriteLine("Recast Graph -- Collecting Meshes"); #endif #if BNICKSON_UPDATED editorTileSize = (int)(EditorVars.GridSize / cellSize); #endif //---- //Voxel grid size int gw = (int)(forcedBounds.size.x / cellSize + 0.5f); int gd = (int)(forcedBounds.size.z / cellSize + 0.5f); if (!useTiles) { tileSizeX = gw; tileSizeZ = gd; } else { tileSizeX = editorTileSize; tileSizeZ = editorTileSize; } //Number of tiles int tw = (gw + tileSizeX - 1) / tileSizeX; int td = (gd + tileSizeZ - 1) / tileSizeZ; tileXCount = tw; tileZCount = td; if (tileXCount * tileZCount > TileIndexMask + 1) { throw new System.Exception("Too many tiles (" + (tileXCount * tileZCount) + ") maximum is " + (TileIndexMask + 1) + "\nTry disabling ASTAR_RECAST_LARGER_TILES under the 'Optimizations' tab in the A* inspector."); } tiles = new NavmeshTile[tileXCount * tileZCount]; #if ASTARDEBUG System.Console.WriteLine("Recast Graph -- Creating Voxel Base"); #endif // If this is true, just fill the graph with empty tiles if (scanEmptyGraph) { for (int z = 0; z < td; z++) { for (int x = 0; x < tw; x++) { tiles[z * tileXCount + x] = NewEmptyTile(x, z); } } return; } AstarProfiler.StartProfile("Finding Meshes"); List <ExtraMesh> extraMeshes; #if !NETFX_CORE || UNITY_EDITOR System.Console.WriteLine("Collecting Meshes"); #endif CollectMeshes(out extraMeshes, forcedBounds); AstarProfiler.EndProfile("Finding Meshes"); // 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); //Create the voxelizer and set all settings var vox = new Voxelize(cellHeight, cellSize, walkableClimb, walkableHeight, maxSlope); vox.inputExtraMeshes = extraMeshes; vox.maxEdgeLength = maxEdgeLength; int lastInfoCallback = -1; var watch = System.Diagnostics.Stopwatch.StartNew(); //Generate all tiles for (int z = 0; z < td; z++) { for (int x = 0; x < tw; x++) { int tileNum = z * tileXCount + x; #if !NETFX_CORE || UNITY_EDITOR System.Console.WriteLine("Generating Tile #" + (tileNum) + " of " + td * tw); #endif //Call statusCallback only 10 times since it is very slow in the editor if (statusCallback != null && (tileNum * 10 / tiles.Length > lastInfoCallback || watch.ElapsedMilliseconds > 2000)) { lastInfoCallback = tileNum * 10 / tiles.Length; watch.Reset(); watch.Start(); statusCallback(new Progress(Mathf.Lerp(0.1f, 0.9f, tileNum / (float)tiles.Length), "Building Tile " + tileNum + "/" + tiles.Length)); } BuildTileMesh(vox, x, z); } } #if !NETFX_CORE System.Console.WriteLine("Assigning Graph Indices"); #endif if (statusCallback != null) { statusCallback(new Progress(0.9f, "Connecting tiles")); } //Assign graph index to nodes uint graphIndex = (uint)AstarPath.active.astarData.GetGraphIndex(this); GraphNodeDelegateCancelable del = delegate(GraphNode n) { n.GraphIndex = graphIndex; return(true); }; GetNodes(del); #if BNICKSON_UPDATED #if DEBUG if (useCenterTileOnly && (3 != tileXCount || 3 != tileZCount)) { EB.Debug.LogError("RecastGenerator.ScanAllTiles() : Incorrect amount of tiles generated if ceneter tile is all that is required"); } #endif int centerXTile = (tileXCount / 2); int centerZTile = (tileZCount / 2); #endif for (int z = 0; z < td; z++) { for (int x = 0; x < tw; x++) { #if BNICKSON_UPDATED // if we're only using the center tile, and this is not the center tile if (useCenterTileOnly && !(centerZTile == z && centerXTile == x)) { continue; } #endif #if !NETFX_CORE System.Console.WriteLine("Connecing Tile #" + (z * tileXCount + x) + " of " + td * tw); #endif if (x < tw - 1) { ConnectTiles(tiles[x + z * tileXCount], tiles[x + 1 + z * tileXCount]); } if (z < td - 1) { ConnectTiles(tiles[x + z * tileXCount], tiles[x + (z + 1) * tileXCount]); } } } AstarProfiler.PrintResults(); }
/** Calculates the path until completed or until the time has passed \a targetTick. * Usually a check is only done every 500 nodes if the time has passed \a targetTick. * Time/Ticks are got from System.DateTime.UtcNow.Ticks. * * Basic outline of what the function does for the standard path (Pathfinding.ABPath). * \code * while the end has not been found and no error has ocurred * check if we have reached the end * if so, exit and return the path * * open the current node, i.e loop through its neighbours, mark them as visited and put them on a heap * * check if there are still nodes left to process (or have we searched the whole graph) * if there are none, flag error and exit * * pop the next node of the heap and set it as current * * check if the function has exceeded the time limit * if so, return and wait for the function to get called again * \endcode */ public override void CalculateStep(long targetTick) { int counter = 0; //Continue to search while there hasn't ocurred an error and the end hasn't been found while (CompleteState == PathCompleteState.NotCalculated) { searchedNodes++; //Close the current node, if the current node is the target node then the path is finished if (currentR.node == endNode) { CompleteState = PathCompleteState.Complete; break; } if (currentR.h < partialBestTarget.h) { partialBestTarget = currentR; } //Loop through all walkable neighbours of the node and add them to the open list. currentR.node.Open(runData, currentR, hTarget, this); //any nodes left to search? if (runData.open.numberOfItems <= 1) { Error(); LogError("No open points, whole area searched"); return; } //Select the node with the lowest F score and remove it from the open list currentR = runData.open.Remove(); //Check for time every 500 nodes, roughly every 0.5 ms usually if (counter > 500) { //Have we exceded the maxFrameTime, if so we should wait one frame before continuing the search since we don't want the game to lag if (System.DateTime.UtcNow.Ticks >= targetTick) { //Return instead of yield'ing, a separate function handles the yield (CalculatePaths) return; } counter = 0; } counter++; } AstarProfiler.StartProfile("Trace"); if (CompleteState == PathCompleteState.Complete) { Trace(currentR); } else if (calculatePartial && partialBestTarget != null) { CompleteState = PathCompleteState.Partial; Trace(partialBestTarget); } AstarProfiler.EndProfile(); }
/** Prepares the path. Searches for start and end nodes and does some simple checking if a path is at all possible */ public override void Prepare() { AstarProfiler.StartProfile("Get Nearest"); //Initialize the NNConstraint nnConstraint.tags = enabledTags; NNInfo startNNInfo = AstarPath.active.GetNearest(startPoint, nnConstraint, startHint); //Tell the NNConstraint which node was found as the start node if it is a PathNNConstraint and not a normal NNConstraint PathNNConstraint pathNNConstraint = nnConstraint as PathNNConstraint; if (pathNNConstraint != null) { pathNNConstraint.SetStart(startNNInfo.node); } startPoint = startNNInfo.clampedPosition; startIntPoint = (Int3)startPoint; startNode = startNNInfo.node; //If it is declared that this path type has an end point //Some path types might want to use most of the ABPath code, but will not have an explicit end point at start if (hasEndPoint) { NNInfo endNNInfo = AstarPath.active.GetNearest(endPoint, nnConstraint, endHint); endPoint = endNNInfo.clampedPosition; hTarget = (Int3)endPoint; endNode = endNNInfo.node; } AstarProfiler.EndProfile(); if (startNode == null && (hasEndPoint && endNode == null)) { Error(); LogError("Couldn't find close nodes to the start point or the end point"); return; } if (startNode == null) { Error(); LogError("Couldn't find a close node to the start point"); return; } if (endNode == null && hasEndPoint) { Error(); LogError("Couldn't find a close node to the end point"); return; } if (!startNode.walkable) { Error(); LogError("The node closest to the start point is not walkable"); return; } if (hasEndPoint && !endNode.walkable) { Error(); LogError("The node closest to the end point is not walkable"); return; } if (hasEndPoint && startNode.area != endNode.area) { Error(); LogError("There is no valid path to the target (start area: " + startNode.area + ", target area: " + endNode.area + ")"); return; } }
void IUpdatableGraph.UpdateArea(GraphUpdateObject guo) { // Figure out which tiles are affected // Expand TileBorderSizeInWorldUnits voxels in all directions to make sure // all tiles that could be affected by the update are recalculated. var affectedTiles = GetTouchingTiles(guo.bounds, TileBorderSizeInWorldUnits); if (!guo.updatePhysics) { for (int z = affectedTiles.ymin; z <= affectedTiles.ymax; z++) { for (int x = affectedTiles.xmin; x <= affectedTiles.xmax; x++) { NavmeshTile tile = tiles[z * tileXCount + x]; NavMeshGraph.UpdateArea(guo, tile); } } return; } Voxelize vox = globalVox; if (vox == null) { throw new System.InvalidOperationException("No Voxelizer object. UpdateAreaInit should have been called before this function."); } AstarProfiler.StartProfile("Build Tiles"); var allMeshes = vox.inputMeshes; // Build the new tiles // If we are updating more than one tile it makes sense to do a more optimized pass for assigning each mesh to the buckets that it intersects. var buckets = PutMeshesIntoTileBuckets(vox.inputMeshes, affectedTiles); for (int x = affectedTiles.xmin; x <= affectedTiles.xmax; x++) { for (int z = affectedTiles.ymin; z <= affectedTiles.ymax; z++) { vox.inputMeshes = buckets[(z - affectedTiles.ymin) * affectedTiles.Width + (x - affectedTiles.xmin)]; stagingTiles.Add(BuildTileMesh(vox, x, z)); } } for (int i = 0; i < buckets.Length; i++) { ListPool <RasterizationMesh> .Release(buckets[i]); } for (int i = 0; i < allMeshes.Count; i++) { allMeshes[i].Pool(); } ListPool <RasterizationMesh> .Release(ref allMeshes); vox.inputMeshes = null; uint graphIndex = (uint)AstarPath.active.data.GetGraphIndex(this); // Set the correct graph index for (int i = 0; i < stagingTiles.Count; i++) { NavmeshTile tile = stagingTiles[i]; GraphNode[] nodes = tile.nodes; for (int j = 0; j < nodes.Length; j++) { nodes[j].GraphIndex = graphIndex; } } AstarProfiler.EndProfile("Build Tiles"); }
/// <summary>Prepares the path. Searches for start and end nodes and does some simple checking if a path is at all possible</summary> protected override void Prepare() { AstarProfiler.StartProfile("Get Nearest"); //Initialize the NNConstraint nnConstraint.tags = enabledTags; var startNNInfo = AstarPath.active.GetNearest(startPoint, nnConstraint); //Tell the NNConstraint which node was found as the start node if it is a PathNNConstraint and not a normal NNConstraint var pathNNConstraint = nnConstraint as PathNNConstraint; if (pathNNConstraint != null) { pathNNConstraint.SetStart(startNNInfo.node); } startPoint = startNNInfo.position; startIntPoint = (Int3)startPoint; startNode = startNNInfo.node; if (startNode == null) { FailWithError("Couldn't find a node close to the start point"); return; } if (!CanTraverse(startNode)) { FailWithError("The node closest to the start point could not be traversed"); return; } // If it is declared that this path type has an end point // Some path types might want to use most of the ABPath code, but will not have an explicit end point at this stage if (hasEndPoint) { var endNNInfo = AstarPath.active.GetNearest(endPoint, nnConstraint); endPoint = endNNInfo.position; endNode = endNNInfo.node; if (endNode == null) { FailWithError("Couldn't find a node close to the end point"); return; } // This should not trigger unless the user has modified the NNConstraint if (!CanTraverse(endNode)) { FailWithError("The node closest to the end point could not be traversed"); return; } // This should not trigger unless the user has modified the NNConstraint if (startNode.Area != endNode.Area) { FailWithError("There is no valid path to the target"); return; } #if !ASTAR_NO_GRID_GRAPH // Potentially we want to special case grid graphs a bit // to better support some kinds of games // If this returns true it will overwrite the // endNode, endPoint, hTarget and hTargetNode fields if (!EndPointGridGraphSpecialCase(endNNInfo.node)) #endif { // Note, other methods assume hTarget is (Int3)endPoint hTarget = (Int3)endPoint; hTargetNode = endNode; // Mark end node with flag1 to mark it as a target point pathHandler.GetPathNode(endNode).flag1 = true; } } AstarProfiler.EndProfile(); }
protected override void CalculateStep(long targetTick) { int counter = 0; // Continue to search as long as we haven't encountered an error and we haven't found the target while (CompleteState == PathCompleteState.NotCalculated) { // @Performance Just for debug info searchedNodes++; // The node might be the target node for one of the paths if (currentR.flag1) { // Close the current node, if the current node is the target node then the path is finnished for (int i = 0; i < targetNodes.Length; i++) { if (!targetsFound[i] && currentR.node == targetNodes[i]) { FoundTarget(currentR, i); if (CompleteState != PathCompleteState.NotCalculated) { break; } } } if (targetNodeCount <= 0) { CompleteState = PathCompleteState.Complete; break; } } // Loop through all walkable neighbours of the node and add them to the open list. currentR.node.Open(this, currentR, pathHandler); // Any nodes left to search? if (pathHandler.heap.isEmpty) { CompleteState = PathCompleteState.Complete; break; } // Select the node with the lowest F score and remove it from the open list AstarProfiler.StartFastProfile(7); currentR = pathHandler.heap.Remove(); AstarProfiler.EndFastProfile(7); // Check for time every 500 nodes, roughly every 0.5 ms usually if (counter > 500) { // Have we exceded the maxFrameTime, if so we should wait one frame before continuing the search since we don't want the game to lag if (System.DateTime.UtcNow.Ticks >= targetTick) { // Return instead of yield'ing, a separate function handles the yield (CalculatePaths) return; } counter = 0; } counter++; } }
/// <summary> /// Calculates the path until completed or until the time has passed targetTick. /// Usually a check is only done every 500 nodes if the time has passed targetTick. /// Time/Ticks are got from System.DateTime.UtcNow.Ticks. /// /// Basic outline of what the function does for the standard path (Pathfinding.ABPath). /// <code> /// while the end has not been found and no error has occurred /// check if we have reached the end /// if so, exit and return the path /// /// open the current node, i.e loop through its neighbours, mark them as visited and put them on a heap /// /// check if there are still nodes left to process (or have we searched the whole graph) /// if there are none, flag error and exit /// /// pop the next node of the heap and set it as current /// /// check if the function has exceeded the time limit /// if so, return and wait for the function to get called again /// </code> /// </summary> protected override void CalculateStep(long targetTick) { int counter = 0; // Continue to search as long as we haven't encountered an error and we haven't found the target while (CompleteState == PathCompleteState.NotCalculated) { searchedNodes++; // Close the current node, if the current node is the target node then the path is finished if (currentR.flag1) { // We found a target point // Mark that node as the end point CompleteWith(currentR.node); break; } if (currentR.H < partialBestTarget.H) { partialBestTarget = currentR; } AstarProfiler.StartFastProfile(4); // Loop through all walkable neighbours of the node and add them to the open list. currentR.node.Open(this, currentR, pathHandler); AstarProfiler.EndFastProfile(4); // Any nodes left to search? if (pathHandler.heap.isEmpty) { if (calculatePartial && partialBestTarget != null) { CompleteState = PathCompleteState.Partial; Trace(partialBestTarget); } else { FailWithError("Searched whole area but could not find target"); } return; } // Select the node with the lowest F score and remove it from the open list AstarProfiler.StartFastProfile(7); currentR = pathHandler.heap.Remove(); AstarProfiler.EndFastProfile(7); // Check for time every 500 nodes, roughly every 0.5 ms usually if (counter > 500) { // Have we exceded the maxFrameTime, if so we should wait one frame before continuing the search since we don't want the game to lag if (System.DateTime.UtcNow.Ticks >= targetTick) { // Return instead of yield'ing, a separate function handles the yield (CalculatePaths) return; } counter = 0; // Mostly for development if (searchedNodes > 1000000) { throw new System.Exception("Probable infinite loop. Over 1,000,000 nodes searched"); } } counter++; } AstarProfiler.StartProfile("Trace"); if (CompleteState == PathCompleteState.Complete) { Trace(currentR); } else if (calculatePartial && partialBestTarget != null) { CompleteState = PathCompleteState.Partial; Trace(partialBestTarget); } AstarProfiler.EndProfile(); }
public override void Scan() { AstarProfiler.Reset(); //AstarProfiler.StartProfile ("Base Scan"); //base.Scan (); //AstarProfiler.EndProfile ("Base Scan"); if (useCRecast) { ScanCRecast(); } else { MeshFilter[] filters; ExtraMesh[] extraMeshes; if (!CollectMeshes(out filters, out extraMeshes)) { nodes = new Node[0]; return; } Voxelize vox = new Voxelize(cellHeight, cellSize, walkableClimb, walkableHeight, maxSlope); vox.maxEdgeLength = maxEdgeLength; vox.forcedBounds = forcedBounds; vox.includeOutOfBounds = includeOutOfBounds; //g.GetComponent<Voxelize>(); vox.VoxelizeMesh(filters, extraMeshes); /*bool[,] open = new bool[width,depth]; * int[,] visited = new int[width+1,depth+1]; * * for (int z=0;z<depth;z++) { * for (int x = 0;x < width;x++) { * open[x,z] = graphNodes[z*width+x].walkable; * } * }*/ /*for (int i=0;i<depth*width;i++) { * open[i] = graphNodes[i].walkable; * } * * * int wd = width*depth; * * List<int> boundary = new List<int>(); * * int p = 0; * * for (int i=0;i<wd;i++) { * if (!open[i]) { * boundary.Add (i); * * p = i; * * int backtrack = i-1; * * * }*/ vox.ErodeWalkableArea(Mathf.CeilToInt(2 * characterRadius / cellSize)); vox.BuildDistanceField(); vox.BuildRegions(); VoxelContourSet cset = new VoxelContourSet(); vox.BuildContours(contourMaxError, 1, cset, Voxelize.RC_CONTOUR_TESS_WALL_EDGES); VoxelMesh mesh; vox.BuildPolyMesh(cset, 3, out mesh); Vector3[] vertices = new Vector3[mesh.verts.Length]; AstarProfiler.StartProfile("Build Nodes"); for (int i = 0; i < vertices.Length; i++) { vertices[i] = (Vector3)mesh.verts[i]; } matrix = Matrix4x4.TRS(vox.voxelOffset, Quaternion.identity, Int3.Precision * Voxelize.CellScale); //Int3.Precision*Voxelize.CellScale+(Int3)vox.voxelOffset //GenerateNodes (this,vectorVertices,triangles, out originalVertices, out _vertices); NavMeshGraph.GenerateNodes(this, vertices, mesh.tris, out _vectorVertices, out _vertices); AstarProfiler.EndProfile("Build Nodes"); AstarProfiler.PrintResults(); } }