Godot Version
4.2
Question
Hello,
I am trying to update a NavigationPolygon on runtime, when adding a new building on scene. Building is a separate node with sprite, collision shapes and collision polygon as well.
The interesting thing is that code works perfectly if the building exists on scene before runtime, so I will add it manually on scene, then make a test run, then make a baking - it will work just fine, it will be detected by code and my NavigationAgent will avoid it as obstacle. But if I will instantiate a building during runtime - it won’t be visible for my NavAgent.
Here is the code I have:
This is my navigation region code:
extends NavigationRegion2D
@onready var nav_server = $"../NavigationServer"
var new_navigation_polygon : NavigationPolygon = get_navigation_polygon()
# Called when the node enters the scene tree for the first time.
func _ready():
parse_2d_collisionshapes(self)
new_navigation_polygon.make_polygons_from_outlines()
set_navigation_polygon(new_navigation_polygon)
# Called every frame. 'delta' is the elapsed time since the previous frame.
func bakeall():
parse_2d_collisionshapes(self)
new_navigation_polygon.make_polygons_from_outlines()
set_navigation_polygon(new_navigation_polygon)
nav_server.update_navserver(new_navigation_polygon)
func _process(delta):
pass
func update_map(object):
object.get_parent().remove_child(object)
add_child(object)
print('object added as a child')
print('start new baking')
bakeall()
print('baking finished')
func parse_2d_collisionshapes(root_node: Node2D):
for node in root_node.get_children():
if node.get_child_count() > 0:
parse_2d_collisionshapes(node)
if node is CollisionPolygon2D:
print('Collision polygon found: ', node)
var collisionpolygon_transform: Transform2D = node.get_global_transform()
var collisionpolygon: PackedVector2Array = node.polygon
var new_collision_outline: PackedVector2Array = collisionpolygon_transform * collisionpolygon
new_navigation_polygon.add_outline(new_collision_outline)
print(new_collision_outline)
So basically it goes through all own child and makes a navigation polygon out of that. This is my setup I have before making a run:
And it works absolutely perfect - my navagent considers both staticbodies as a obstacles and avoids them when they are on it’s path.
But then, if I will instantiate an object during runtime, I will call a bakeall() function - as far as I understand it kinda overwrites the navigation polygon and ‘bakes’ it again.
And then I have everything stored in NavigationServer2D script:
extends Node
var speed = 100
var path = []
var map
var region
@onready var NavReg = $"../NavigationRegion2D"
func _ready():
setup_navserver()
func setup_navserver():
#Create a new navigation map
map = NavigationServer2D.map_create()
NavigationServer2D.map_set_active(map, true)
#Create a new navigation region and add it to the map
region = NavigationServer2D.region_create()
NavigationServer2D.region_set_transform(region, Transform2D())
NavigationServer2D.region_set_map(region, map)
#Set navgiation mesh for the region
var navigation_poly = NavigationMesh.new()
navigation_poly = NavReg.navigation_polygon
NavigationServer2D.region_set_navigation_polygon(region, navigation_poly)
print('Server setup finished.')
func get_navigation_values(start_position, end_position):
path = NavigationServer2D.map_get_path(map, start_position, end_position, true)
return[path, true]
func update_navserver(new_poly):
NavigationServer2D.region_set_transform(region, Transform2D())
NavigationServer2D.region_set_navigation_polygon(region, new_poly)
In my understanding, whenever I put a new object and call Bakeall() function it should work fine and new obstacle should be visible to a NavAgent in the end. But it doesn’t happen - only objects that were on scene before runtime are obstacles. Even though they are similar (exactly the same) as I am instantiating.
Here is the navigation agent code:
extends CharacterBody2D
@onready var NavReg = $"../NavigationRegion2D"
@onready var nav = get_node("NavigationAgent2D")
@onready var navServer = get_node("../NavigationServer")
var speed = 100
var path = []
var map
func _physics_process(delta):
var walk_distance = 100 * delta
move_along_path(walk_distance)
func move_along_path(distance):
var last_point = self.position
while path.size():
var distance_between_points = last_point.distance_to(path[0])
if distance <= distance_between_points:
self.position = last_point.lerp(path[0], distance / distance_between_points)
return
distance -= distance_between_points
last_point = path[0]
path.remove_at(0)
self.position = last_point
set_process(false)
func _update_navigation_path(generated_path, value):
path = generated_path
path.remove_at(0)
set_process(value)
func _unhandled_input(event):
if not event.is_action_pressed("click"):
return
else:
var new_path = navServer.get_navigation_values(self.position, get_global_mouse_position())[0]
var new_value = navServer.get_navigation_values(self.position, get_global_mouse_position())[1]
_update_navigation_path(new_path, new_value)
I have a feeling that I’m missing something very obvious, but I can’t find it. Any help would be appreciated, thanks a lot in advance!