/** Main pathfinding method.
* This method will calculate the paths in the pathfinding queue.
*
* \see CalculatePathsThreaded
* \see StartPath
*/
IEnumerator CalculatePaths(PathThreadInfo threadInfo)
{
int numPaths = 0;
// 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\n" +
"threadInfo 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 = 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");
//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);
p.PrepareBase(runData);
//Now processing the path
//Will advance to Processing
p.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);
}
numPaths++;
//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");
p.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.debugPath = p;
//Initialize the path, now ready to begin search
AstarProfiler.StartProfile("Path Initialize");
p.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");
p.CalculateStep(targetTick);
AstarProfiler.EndFastProfile(2);
p.searchIterations++;
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.Error();
}
targetTick = System.DateTime.UtcNow.Ticks + maxTicks;
}
totalTicks += System.DateTime.UtcNow.Ticks - startTicks;
p.duration = totalTicks * 0.0001F;
}
// Cleans up node tagging and other things
p.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);
p.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;
numPaths = 0;
}
}
//Debug.LogError ("Error : This part should never be reached");
}