/** Opens nodes until there are none left to search (or until the max time limit has been exceeded) */ 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++; 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); // Insert into internal search tree if (saveParents) { parents[currentR.node] = currentR.parent.node; } 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 (DateTime.UtcNow.Ticks >= targetTick) { //Return instead of yield'ing, a separate function handles the yield (CalculatePaths) return; } counter = 0; if (searchedNodes > 1000000) { throw new Exception("Probable infinite loop. Over 1,000,000 nodes searched"); } } counter++; } }
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++; } }
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++; //--- 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.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 (DateTime.UtcNow.Ticks >= targetTick) { //Return instead of yield'ing, a separate function handles the yield (CalculatePaths) return; } counter = 0; if (searchedNodes > 1000000) { throw new Exception("Probable infinite loop. Over 1,000,000 nodes searched"); } } counter++; } }
/** 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.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) { Error(); LogError("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(); }
/// <summary> /// Main pathfinding method. /// This method will calculate the paths in the pathfinding queue. /// /// See: CalculatePathsThreaded /// See: StartPath /// </summary> 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.CompleteState == PathCompleteState.NotCalculated) { // 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.CompleteState == PathCompleteState.NotCalculated) { // 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.CompleteState != PathCompleteState.NotCalculated) { 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; } } }
/// <summary> /// Main pathfinding method (multithreaded). /// This method will calculate the paths in the pathfinding queue when multithreading is enabled. /// /// See: CalculatePaths /// See: StartPath /// </summary> void CalculatePathsThreaded(PathHandler pathHandler) { #if UNITY_2017_3_OR_NEWER UnityEngine.Profiling.Profiler.BeginThreadProfiling("Pathfinding", "Pathfinding thread #" + (pathHandler.threadID + 1)); #endif #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(); #if UNITY_2017_3_OR_NEWER profilingSampler.Begin(); #endif // 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.CompleteState == PathCompleteState.NotCalculated) { // 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.CompleteState == PathCompleteState.NotCalculated) { // 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 UNITY_2017_3_OR_NEWER profilingSampler.End(); #endif } #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(); } finally { #if UNITY_2017_3_OR_NEWER UnityEngine.Profiling.Profiler.EndThreadProfiling(); #endif } #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(); //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); p.PrepareBase(runData); //Now processing the path //Will advance to Processing p.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 p.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.debugPath = p; AstarProfiler.StartFastProfile(1); //Initialize the path, now ready to begin search p.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); p.CalculateStep(targetTick); p.searchIterations++; 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.TotalSearchedNodes, p.searchedNodes); System.Threading.Interlocked.Add(ref AstarPath.TotalSearchTime, totalTicks); #endif } // Cleans up node tagging and other things p.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 p.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(); }
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 finnished if (currentR.G >= searchLength) { nodesEvaluatedRep++; if (chosenNodeR == null) { chosenNodeR = currentR; } else if (rnd.NextDouble() <= 1.0f / nodesEvaluatedRep) { chosenNodeR = currentR; } if (currentR.G >= searchLength + spread) { CompleteState = PathCompleteState.Complete; break; } } else if (currentR.G > maxGScore) { maxGScore = (int)currentR.G; maxGScoreNodeR = 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()) { if (chosenNodeR != null) { CompleteState = PathCompleteState.Complete; } else if (maxGScoreNodeR != null) { chosenNodeR = maxGScoreNodeR; CompleteState = PathCompleteState.Complete; } else { LogError("Not a single node found to search"); Error(); } 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++; } AstarProfiler.StartProfile("Trace"); if (CompleteState == PathCompleteState.Complete) { Trace(chosenNodeR); } AstarProfiler.EndProfile(); }