Currently I’m trying to implement a simple navigation system for the entities in my game. Based on this little example I’m trying to dynamically carve empty areas into a NavigationRegion2D polygon mesh, whenever an entity is standing still. However calling make_polygons_from_outlines() seems to completely freeze the game. No errors are logged and debugger monitor does not show any huge numbers under the Navigation tab.
I’m also not sure if this is a good way to achieve what I want. Feel free to suggest a better way to modify the navigation region polygon mesh, so that the entity collision shapes are carved out of the mesh.
Some specs from my part:
Computer: M1 mac
Version: 4.3
I use rust-godot bindings via gdext (should not affect this, since I’m not calling any rust code with the nav related stuff)
The game is isometric
Example:
Attaching the following code to a NavigationRegion2D (which is the root node) scene will cause a freeze.
You could just bake your collision shapes to a navigation mesh since Godot 4.2. Your screenshot shows the prominent “Bake NavigationPolygon” button in the tool bar and you can do the same with $NavigationRegion.bake_navigation_polygon() in scripts or by using the NavigationServer2D parse and bake API.
That script example you linked is from Godot 4.0 that did not have 2D navmesh baking and is completely outdated by now, the entire make_polygons_from_outlines() function is deprecated since Godot 4.2. One of the 7 Errors that your project has is likely a warning about using this outdated function.
Yeah, I realized after I posted this, that the example was outdated. Not sure why I did not notice
I actually since tried the method that you suggested, but for some reason calling bake_navigation_polygon() did not bake new static bodies with colliders added as children into the region. On top of that, it seems like colliders inside a CharacterBody2D are not considered for the baking, which also complicates things.
Currently I’m trying to use the TileMapLayer, and it actually works, though the performance is absolutely horrendous (every notify_runtime_tile_data_update() call freezes the game for a moment). But if I get the performance fixed, then I can call it a day!
As you’ve probably guessed, I’m doing all of this because the avoidance on NavigationAgents does not work towards non moving agents particularly well. For example a wall of standing agents between a moving agent and its target is probably going to lead to that moving agent in getting stuck (due to avoidance not affecting the pathfinding overall).
Also, if you know any good examples about the things relating to what I’m trying to solve, feel free to provide me a link! I’m fairly new to godot and video game development in general
Rebaking navmesh for moving objects when they stop and stand still for a while is not only an option, it is the only option. You need to change the navmesh because the navmesh is what defines the pathfinding paths.
Avoidance can not solve pathing problems caused by navmesh that has “wrong” information, e.g. says that a part can be used that shoudn’t, because avoidance works with velocities, it is a different system.
CharacterBody nodes are not parsed by the navmesh baking. These node types are normally used only with very dynamic and constantly moving objects. That makes them unsuitable for navmesh baking.
So you either add a temp node when your character stands still that can get parsed and baked by the navmesh baking, or you parse, add and bake the geometry procedual with the NavigationServer to a navmesh.
TileMap(Layer) is not a performance friendly node for runtime changes on larger maps. The navmesh baking can only parse TileMaps very inefficiently and in full due to the TileMaps restricted API. What kinda works inside the editor where performance is less critical becomes a problem for runtime changes with unwieldy large TileMaps.
In general those entire “mini-tiles” layouts that TileMap are known for are not performance friendly for the navigation system. Having so many mini polygons cost a performance fortune while yielding low pathfinding quality. Those TileMap tiles normally all need to be merged to more reasonable units and / or require a chunk system for acceptable performance. The navmesh baking of a NavigationRegion2D kinda does this for a TileMap but the parsing of the TileMap is still slow.
The Godot demo repo has a new 2D navmesh chunk demo that shows how to slice large geometry into more update friendly region chunks. If you do runtime changes you dont want to have a single region because that means every change requires a full rebuild of everything. You want to have many reasonably sized chunks for quicker partial updates.
Most TileMap projects work around the TileMap navigation performance problems by parsing copies of their static geometry once at game start to a NavigationmeshSourceGeometryData object. Then doing the runtime changes procedual in script on copies of those static geometry to avoid the performance cost of parsing everything again. They bake with the NavigationServer instead of a NavigationRegion as the server gives more control over the geometry and baking steps. Their entire projects reduce the TileMap to visuals, never touching it again for anything navigation relate in the process after the first parse.
I tried this again with the NavigationRegion2D and copying the collision shapes from the agents into the region as children of temporary static bodies. It works perfectly! I probably just did some mistake earlier, when the rebaking did not happen.
The godot-demo-projects github repo looks great. I will certainly check it out in the future if I have similar questions!
Thanks for taking the time to respond in such detail. Really appreciate it