Hello,
For simplicity of explanation: I am making an AI similar to Autochess. There is a playing location (in my case a large size) and, for example, 20 units. I need to find a path for every unit once a second so that they never intersect. Units move only one cell. Thanks to the forum and documentation, I found out that I need to use BlockManager. Since it is very expensive to count all the paths in one frame, I consider 1 path in 1 frame (coroutine). However, there may be situations where the user has less than 20-30 frames. Therefore, I wanted to use AstarWorkItem to calculate all the paths in a separate thread. My logic is:
Block nodes of all units;
For each unit:
- Unlock unit node;
- Find a path;
- If the path size is> 0, then block the node “path [1]”, otherwise block the last node.
However, in step 3, I still do not have access to the path (is it not calculated?)
My code is:
for (int i = 0; i < pathes.Count; i++)
{
int k = i;
AstarPath.active.AddWorkItem(new AstarWorkItem(ctx => { traversalProvider.blockedNodes.Remove(AstarPath.active.GetNearest(ts[k].position).node); }));
AstarPath.active.AddWorkItem(new AstarWorkItem(ctx => { AstarPath.StartPath(pathes[k]); }));
AstarPath.active.AddWorkItem(new AstarWorkItem(ctx => { Debug.LogError(pathes[k].vectorPath.Count); }));
// AstarPath.active.AddWorkItem(new AstarWorkItem(ctx => { traversalProvider.selector.Add(pathes[k].path[0]);
// }));
}
In general, I want instead of each frame to cause logic for the next unit, just put all the units in the queue. Is it possible? Or the only way is to process 1 path in 1 frame?
Thanks.
P.S. I wrote my CustonTraversalProvider, which simply accepts an array of locked nodes.
Hi
I would do something like
for (int i = 0; i < paths.Count; i++) {
traversalProvider.blockedNodes.Remove(AstarPath.active.GetNearest(ts[i].position).node);
AstarPath.StartPath(paths[i]);
for (int w = 0; w < delayFrames; w++) yield return null;
paths[0].BlockUntilCalculated();
traversalProvider.selector.Add(paths[i].path[1]);
}
Then you can have delayFrames
set to 1 most of the time, but if you are running out of time you can set it to 0 to make all paths be calculated instantly.
Note that work items are always executed in the main thread.
That is, there is no way to perform a chain of calculation of paths and changes to traversalProvider in a separate thread?
Ok, I get it, thanks.
Well, you can use path.immediateCallback which will be called from the pathfinding thread itself.
So something like
void SearchPath(int pathIndex) {
traversalProvider.blockedNodes.Remove(AstarPath.active.GetNearest(ts[pathIndex].position).node);
paths[pathIndex].immediateCallback = (p) => {
traversalProvider.selector.Add(paths[pathIndex].path[1]);
if (pathIndex + 1 < paths.Count) SearchPath(pathIndex + 1);
};
AstarPath.StartPath(paths[pathIndex]);
}
void SearchAllPaths() {
SearchPath(0);
// If you need to calculate all paths immediately
// paths[paths.Count-1].BlockUntilCalculated();
}
Be very careful when doing this though. Multithreaded race conditions are hell to deal with.
See https://arongranberg.com/astar/docs/path.html#immediateCallback
I get an error:
UnityException: get_isPlaying can only be called from the main thread.
Constructors and field initializers will be executed from the loading thread when loading a scene.
Don't use this function in the constructor or field initializers, instead move initialization code to the Awake or Start function.
AstarPath.StartPath (Pathfinding.Path path, System.Boolean pushToFront) (at Assets/AstarPathfindingProject/Core/AstarPath.cs:1969)
ManyAgentsTest.SearchPathInGlobalQueue (System.Int32 pathIndex) (at Assets/ManyAgentsTest.cs:59)
ManyAgentsTest+<>c__DisplayClass9_0.<SearchPathInGlobalQueue>b__0 (Pathfinding.Path p) (at Assets/ManyAgentsTest.cs:57)
Pathfinding.PathProcessor.CalculatePathsThreaded (Pathfinding.PathHandler pathHandler) (at Assets/AstarPathfindingProject/Core/Misc/PathProcessor.cs:376)
UnityEngine.Debug:LogException(Exception)
Pathfinding.PathProcessor:CalculatePathsThreaded(PathHandler) (at Assets/AstarPathfindingProject/Core/Misc/PathProcessor.cs:407)
Pathfinding.<>c__DisplayClass24_0:<.ctor>b__0() (at Assets/AstarPathfindingProject/Core/Misc/PathProcessor.cs:110)
System.Threading.ThreadHelper:ThreadStart()
// Outside of play mode, all path requests are synchronous
if (!Application.isPlaying) {
BlockUntilCalculated(path);
}
(v 4.3.14 (pro), Unity 2019.3.0f6)
I commented out this lines, but how much is it provided and safe?