Godot 4.0 Collision avoidance for NavigationAgent2D

:information_source: Attention Topic was automatically imported from the old Question2Answer platform.
:bust_in_silhouette: Asked By Skydome

So how is collision avoidance handled by NavigationAgent2D in 4.0? When I’m using it. It doesn’t ignore collisions at all.

:bust_in_silhouette: Reply From: cm

See my answer here, but to sum up:

The basic idea is that you set your navigation agent’s velocity in _physics_process, not your player’s velocity.

Listen for the navigation agent’s velocity_computed signal use the “safe” value supplied there for your player.

The code from my previous answer:

func _ready() -> void:
    nav_agent.velocity_computed.connect(on_nav_velocity_computed)

func _physics_process(delta: float) -> void:
    var origin = npc.global_transform.origin
    var target = nav_agent.get_next_location()
    var velocity = (target - origin).normalized()
    nav_agent.set_velocity(velocity * SPEED)

func _on_nav_velocity_computed(safe_velocity: Vector3) -> void:
    npc.linear_velocity = safe_velocity

Thank you so much! I’m going to try this now.

Skydome | 2022-05-15 17:36

Collisions are still happening. It doesn’t even attempt to avoid. :frowning:

My physics

func _physics_process(delta):

nav2d.set_target_location(Target.global_position)
var origin = global_transform.origin
var target = nav2d.get_next_location()
var velocity2 = (target - origin).normalized()
nav2d.set_velocity(velocity2 * SPEED)
print(velocity2)
move_and_collide(v*delta)

My Velocity computed

func on_velocity_computed(safe_velocity: Vector2):
v=safe_velocity

What am I missing?

Skydome | 2022-05-16 13:43

Assuming that this code is attached to a RigidDynamicBody2D node, you should only set linear_velocity in on_velocity_computed and do not call move_and_collide.

cm | 2022-05-16 14:14

I just put together a minimal example to confirm that it’s working as expected. The scene and script are pasted below.

nav_test.tscn

[gd_scene load_steps=5 format=3 uid="uid://c7mav27smumdb"]

[ext_resource type="Script" path="res://nav_test.gd" id="1_snbcw"]

[sub_resource type="NavigationPolygon" id="NavigationPolygon_lguo6"]
vertices = PackedVector2Array(725, 192, 581, 246, 485, 243, 449, 159, 497, 112, 599, 58, 767, 46, 944, 153, 817, 232, 993, 333, 839, 310, 761, 363, 856, 465, 663, 513, 587, 369, 473, 516, 344, 348, 305, 501, 198, 457, 204, 109, 248, 106, 320, 201, 101, 306, 107, 159, 151, 122)
polygons = [PackedInt32Array(0, 1, 2, 3, 4, 5, 6), PackedInt32Array(0, 6, 7, 8), PackedInt32Array(8, 7, 9, 10), PackedInt32Array(11, 10, 9, 12, 13), PackedInt32Array(14, 11, 13, 15), PackedInt32Array(16, 14, 15, 17, 18), PackedInt32Array(19, 20, 21, 16, 18, 22, 23, 24)]
outlines = [PackedVector2Array(151, 122, 107, 159, 101, 306, 198, 457, 305, 501, 473, 516, 663, 513, 856, 465, 993, 333, 944, 153, 767, 46, 599, 58, 497, 112, 449, 159, 485, 243, 581, 246, 725, 192, 817, 232, 839, 310, 761, 363, 587, 369, 344, 348, 320, 201, 248, 106, 204, 109)]

[sub_resource type="CircleShape2D" id="CircleShape2D_dxbss"]
radius = 16.0

[sub_resource type="CircleShape2D" id="CircleShape2D_58o3v"]
radius = 16.0

[node name="nav_test" type="Node2D"]
script = ExtResource( "1_snbcw" )

[node name="Position2D" type="Position2D" parent="."]
position = Vector2(533, 186)

[node name="NavigationRegion2D" type="NavigationRegion2D" parent="."]
navpoly = SubResource( "NavigationPolygon_lguo6" )

[node name="npc" type="RigidDynamicBody2D" parent="."]
position = Vector2(240, 152)
gravity_scale = 0.0
can_sleep = false

[node name="CollisionShape2D" type="CollisionShape2D" parent="npc"]
shape = SubResource( "CircleShape2D_dxbss" )

[node name="NavigationAgent2D" type="NavigationAgent2D" parent="npc"]
radius = 16.0
time_horizon = 43.55

[node name="RigidDynamicBody2D" type="RigidDynamicBody2D" parent="."]
position = Vector2(310, 276)
gravity_scale = 0.0
can_sleep = false

[node name="CollisionShape2D" type="CollisionShape2D" parent="RigidDynamicBody2D"]
shape = SubResource( "CircleShape2D_58o3v" )

[node name="NavigationObstacle2D" type="NavigationObstacle2D" parent="RigidDynamicBody2D"]
estimate_radius = false
radius = 16.0

nav_test.gd

extends Node2D

@onready var npc = $npc
@onready var agent = $npc/NavigationAgent2D
@onready var goal = $Position2D


func _ready() -> void:
	agent.velocity_computed.connect(on_velocity_computed)
	agent.target_reached.connect(on_target_reached)
	agent.set_target_location(goal.global_position)


func _physics_process(delta: float) -> void:
	var next_location = agent.get_next_location()
	var v = (next_location - npc.global_position).normalized()
	agent.set_velocity(v * 64.0)


func on_velocity_computed(safe_velocity: Vector2) -> void:
	npc.linear_velocity = safe_velocity


func on_target_reached() -> void:
	print("reached goal")
	get_tree().quit()

cm | 2022-05-16 14:42

Thank you so much for continuously helping me. I was using kinematic body, which is renamed as character body I believe.

So I will try with the rigid dynamic body. I had been using kinematic for navigation in godot 3.4.4. I only recently started with 4.0. So I guess I shouldn’t use character body for navigation agent? Or is there also a way to use character body with navigation agent.

I will try rigiddynamicbody2d and let you know.

Skydome | 2022-05-16 20:50

Ok so I had to do some other modding projects but got back to this today. It’s sort of working now with the rigid dynamic body. The body now rotates(which I didn’t want rotation but let’s see…) to avoid the collision but it’s not very good.

Tile map collisions are ignored almost and even object collisions the enemy constantly gets stuck on. It did avoid one obstacle by rotating.
I’m not sure if this is the limit to obstacle avoidance with navigation 2d or if I am doing it wrong.

Skydome | 2022-06-01 14:17

I recently uploaded a minimal example with obstacle avoidance to the godot discord server here: nav_test_2d.zip Note that this link is over a week old but still works. I have no idea how long it will last.

It’s a little different than the one I posted earlier. This uses Godot4 alpha8, and all nav settings are default except for the agent and obstacle radius. I found that the navigation struggles with tight curves - it will attempt to hug inside corners, and dynamic obstacles there seem to confuse it. Tweaking settings (time horizon for instance) may help a little. It might also help to set up a simple waypoint system for complex areas, rather than a single goal at the end which requires many turns.

Anyway, in this example the path to the goal is direct with a moving obstacle in the way, which the agent avoids as expected. It may help to start from something like this and slowly add complexity.

cm | 2022-06-01 14:43

Hi thank you again. I just downloaded it. I will check it out.
I’ll also be trying out AStar 2d with dynamic obstacle avoidance. I am seeing some tutorials on it.

Skydome | 2022-06-01 14:58

Have you had any kind of success using pathfinding with obstacles? I tested everything too and got no results, even the scene that the colleague shared messages behind also didn’t work in godot 4, I strongly believe it’s bugged

rapojim | 2023-04-23 20:31

Yes Astar2d is what I am using and it’s working well. Dynamic obstacle avoidance. Jmbiv has a tutorial on astar which is where I got my start.

Skydome | 2023-04-24 09:01