0 votes

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

Godot version 4.0
in Engine by (202 points)

1 Answer

+1 vote
Best answer

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
by (272 points)
edited by

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

Collisions are still happening. It doesn't even attempt to avoid. :(

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?

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.

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()

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.

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.

I recently uploaded a minimal example with obstacle avoidance to the godot discord server here: navtest2d.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.

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.

Welcome to Godot Engine Q&A, where you can ask questions and receive answers from other members of the community.

Please make sure to read Frequently asked questions and How to use this Q&A? before posting your first questions.
Social login is currently unavailable. If you've previously logged in with a Facebook or GitHub account, use the I forgot my password link in the login box to set a password for your account. If you still can't access your account, send an email to webmaster@godotengine.org with your username.