I have a procedually generated tilemap and bake the navmesh from it, my agents sometimes get a strange path (the red one below), especially on nav region edges.
So I try to get the path from server directly and optimize it myself, but I still need everything else from NavigationAgent2D, like moving and avoidence.
How can I set the optimized path (like the green one below) to an agent directly?
You can not set a manual path to a NavigationAgent directly.
What you can do, but only very theoretically, is to get the NavigationPathQueryResult2D from the agent and modify its arrays. Depending on what or how you are doing this you might risk bugs or crashes.
Without much additional info just by looking at those paths this looks like you have the tilemap build-in navmesh still active. A baked navmesh will never give you such angled paths as there are no edges in that place to snap to corners like that. So solve those navmesh setup and layout problems and that problem should disappear on its own.
Ok I tried it, yes it’s really hacking and buggy, also cause crash…
Below is my tilemap setup, my game map is procedually generated and no other gameplay is using tilemap, this tilemap is just for baking the navmesh only so I turned off/disabled as many things as I can see here, and there is only one tile with collision is used.
I make this simple function to process the path, as I tested it will simplify and fix most angled path problem.
func simplify(path: PackedVector2Array) -> PackedVector2Array:
if path.size() < 3:
return path.duplicate()
var new_path: PackedVector2Array = PackedVector2Array()
new_path.append(path[0])
var query := PhysicsRayQueryParameters2D.create(Vector2.ZERO, Vector2.ZERO, 2)
var i = 0
while i < path.size() - 1:
var j = i + 1
while j < path.size():
query.from = path[i]
query.to = path[j]
var result: Dictionary = actor.get_world_2d().direct_space_state.intersect_ray(query)
if not result.is_empty():
break
j += 1
new_path.append(path[j - 1])
i = j - 1
return new_path
Right now I’m facing the problem that I don’t know how to apply the new path back to the agent. I can use this path to move the actor myself but I think then I lose all the goodies from agent.
For those polygon “edge-stars” at the chunk corners the only good option is line of sight checks.
There is no build-in option for that with the path query but you are mostly doing it with the physic rays already as a post process.
I dont see a problem with not using the NavigationAgent node and replacing it with NavigationServer.query_path() and your own path following or extra post processing. That is part of the expectation that more advanced uses start to do this as the NavigationAgent cant cover everything.
Long edges or very thin polygons are a general problem.
The pathfinding travels from closest edge point to closest edge point to build the polygon corridor for the path. If you disable the path postprocessing option you will see the raw path that the pathfinding takes from polygon to polygon.
After that the path postprocessing like the funnel use that polygon corridor. It no longer considers any polygon options that are not already inside that corridor, so if the corridor is bad the final path will be bad. We see that a lot with e.g. TileMap build-in navmesh that creates really bad corridors and therefor bad paths.
Like in this case snapping to a corner on the other side. If you zoom in it is likely that there is a very tiny or thin polygon and the closest position on edge test picks that edge and makes astar start to travel in the “wrong direction” over other polygons. Because astar does not go back and revisits all the old polygons if it goes down a bad road the result will be a detour.
Thank you for the deep explaination, it’s really helpful
I’ve got it work with getting the path, processing it myself and moving the actor manually.
Also I notice that NavigationServer2D.query_path sometimes give a path that run through unwalkable areas as shown below, the paths is directly from this function without my modification.
And as I tested it ONLY happens when query with: NavigationPathQueryParameters2D.PATH_POSTPROCESSING_CORRIDORFUNNEL, is this part of the known issues you mentioned above?
The path query can not return a path that is not on (or very close) to a navmesh, it is technically impossible.
All positions it can pick for a path are either
on the surface of a navmesh polygon or its edges.
on the edge portals between different navigation meshes created by the edge connection margin. Those might not be exactly on the navmesh due to the margin but still very close to a real edge.
the 2 positions of a navigation link. Because it adds both positions to the path result you will naturally see a path line between those two link positions but links are supposed to be script handled instead of directly moved on.
The corridorfunnel can only work with the polygons and their corners from the path corridor. So by itself it can not cause a path that goes over such open space with no navmesh. The corridor must already have a (hidden) polygon that spans over that area. Or the layout is borked due to surface geometry errors so has faulty navmesh connections build, in which case all bets are off.
Not saying this is the case here but broken paths like this happens very frequently in 2D when users have a large secondary navmesh with no debug somewhere buried on a different canvas item that they forget about. Because that navmesh spans over half their world it has a large polygon and if that gets merged as a edge connection with the intended polygons we see such erroneous “goes over half the world” paths the moment the pathfinding decides to pick that connection. The normal path postprocessings will likely ignore this because they pick the closest or the middle edge, but the funnel works with the actual polygon corners so the error becomes very clear with that when the polygon corner is on the other end of the world.