How to clear a NavigationPolygon?

Godot Version

4.3

Question

Hello,

I’m working on a top down view game where the player have to clear a room before entering the next one. When a room is leaved, it’s queue freed and the new one is instanciate.
Every room have a NavigationRegion2D. Until now I drew the polygon and baked from the editor manually for every room and it worked very well. But to gain time on room creation I wanted to do that via code, but I encounter a problem : on the first room the navigation area is fine, but from the second one it keeps the previously deleted areas from the shape

Room 1 :

Room 2 : (this is the exact shape of the previous room)

I tried to let the NavigationPolygon empty in the NavigationRegion and create a new one everytime, to use the same, get it and clear it and use NavigationServer2D.bake_from_source_geometry_data with a NavigationMeshSourceGeometryData2D where I add_traversable_outline = to a polygon who cover the entire room.

To bake the shape, I’m getting the coordinates of the corners of the room (obtained with wall’s TileMapLayer used cells) and then remove every collisionshape in a “Navigation” group

So everytime a room is instanciate, I call a method with this code:

		var navigation_region = get_current_room().get_node("NavigationRegion2D")
		var current_nav_polygon = navigation_region.get_navigation_polygon()
		current_nav_polygon.clear()
		
		var nav_polygon = NavigationPolygon.new()
		nav_polygon.parsed_collision_mask = 9
		nav_polygon.source_geometry_mode = 2
		nav_polygon.source_geometry_group_name = "Navigation"
		nav_polygon.agent_radius = 5
		
		var top_left = Vector2(map_limits[0],map_limits[2])
		var bottom_left = Vector2(map_limits[0],map_limits[3])
		var bottom_right = Vector2(map_limits[1],map_limits[3])
		var top_right = Vector2(map_limits[1],map_limits[2])
		var poly_outlines:PackedVector2Array=[top_left,bottom_left,bottom_right,top_right]

		nav_polygon.add_outline(poly_outlines)
		navigation_region.navigation_polygon = nav_polygon

		navigation_region.bake_navigation_polygon()

I wanted to use a NavigationMeshSourceGeometryData2D to add_obstruction_outline, but the collision polygons are only on a collision layer of the tile set and they don’t always fit the size of the tiles.

Do anyone know how to solve this ?

EDIT : I’m testing with 2 differents rooms, both are scenes and their path are in dictionary. I choose randomly one, then delete the entry from the dictionary, so the door lead to the other one and the dictionary is restored with both. Sometimes leaving a room leads to the same. In this case the navigation works in the second room

Room 1:

Room 2:

Room3:

Technically all you need to do is call NavigationPolygon.clear() for removing all navmesh data from the resource as that clears both vertices and polygon indices arrays.

What it does not delete is your manually added outlines, for that you need to call clear_outlines() as well.

There is no auto-update of the region this way so you need to manually call NavigationRegion2D.set_navigation_polygon() after your changes of the NavigationPolygon resource. Same when you bypass the nodes and do everything with the NavigationServer2D. You need to manually call all the update setters as nodes are not aware of those external changes.

The reason why all this needs to be called manually is because the NavigationPolygon has an old legacy design with split functions for vertices and polygons or outlines, so there is no way to know when a user is really done with editing them and if their data is not in sync everything explodes, that is why they do not use e.g. changed signals or other notifications. Servers in general do not notify Nodes in the SceneTree if they can avoid it. Nodes are oneway tickets from node to server but not reverse so if you use server api to change things nodes have no idea what is happening.

The NavigationMeshSourceGeometryData2D does not particularly care about TileMap internal cell borders, it is just outline geometry after all that gets parsed. So if your cell geometry is larger or smaller than the TileMap cell it is parsed anyway as is.

Thanks for your help.

It didn’t solve the problem. As the room and it’s RegionNavigation child are queue free and every instance of a room starts with a NavigationRegion without any Polygon ressource, there is nothing to clear.

Even if I replace the ressource in a new RegionNavigation node at every room, I tried to move the polygon and outlines clear earlier (before starting instantiate new room) but it didn’t changed anything.

Then I just tried to call again the bake_navigation_polygon() after baking just finished with the signal and it solve the problem. Could it be possible that baking appends to early after previous room queue_free and/or after new room instantiation?
I’ll continue to find an other solution because I’m not very happy to bake twice.

Baking twice shouldn’t be required and if a scene setup works without issues in the editor but identical not at runtime the most likely cause of this is node processing and setup order. E.g. TileMap does internal deferred updates so in most e.g. _ready() functions half the nodes are still half empty and have no data that can be parsed and baked. You can test this by waiting a frame and if that fixes all the issues it is the node init.

The actual baking does nothing special at the end of the baking process, it just sets the vertices and polygon indices arrays. If there was a Callable parameter that Callable gets called on the next sync. If you bake with a NavigationRegion2D that is also what the region does internally, calls the same parse and bake function on the NavigationServer2D with a Callable and on that callback uses NavigationRegion2D.set_navigation_polygon() with the updated NavigationPolygon resource.

1 Like

OK thanks again for your answers. It helps a lot, I have now a better understanding of how NavigationRegion and NavigationPolygon work.

await for next frame before baking indeed solve the problem.