Hello! I have a prototype 3D scene where I am trying to get enemies to path to a car and the enemies are attempting to path to a point off the navmesh (-9.201, -0.181171, -85.30832). I have set the target location to be the $Vehicle in the scene (or so I thought) but the agents are attempting to get to a different coordinate.
I am not sure why this is the case and at a loss of how to debug.
My goal is to have the enemies/agents always path to the car no matter its location on the oval track.
What additional information can I provide or find to help debug?
Any help or tips would be greatly appreciated, thanks!
Please try with official code examples from the documentation to verify that both your physics setup and your navigation setup are working on their own before combining them and before you add your own customization on top.
That velocity calculation alone looks fishy and is certainly not part of the documentation examples because move_and_slide() already uses physics delta internally.
I implemented the example code and I don’t have something hooked up correctly because the agents/enemies/zombies do not move when the scene is started.
The code doesn’t say I need to @onready var movement_target so I added that but it doesn’t make a difference either way, which adds to my confusion because I don’t understand how the example code knows what the movement_target is
I rebaked the NavMesh just in case that was the problem and still same behavior of the agents not moving.
My scene tree is unchanged from the screenshot in the OP, so if needed that can be used for reference.
Appreciate any tips you can provide. I am reading the documentation and not understanding what I am doing differently yet.
zombie.gd
extends CharacterBody3D
@export_group("Zombie Stats")
@export var movement_speed: float = 4.0
@export var Health = 1
@onready var movement_target = %Vehicle
@onready var navigation_agent: NavigationAgent3D = get_node("NavigationAgent3D")
func _ready() -> void:
navigation_agent.velocity_computed.connect(Callable(_on_velocity_computed))
func set_movement_target(movement_target: Vector3):
navigation_agent.set_target_position(movement_target)
func _physics_process(delta):
if navigation_agent.is_navigation_finished():
return
var next_path_position: Vector3 = navigation_agent.get_next_path_position()
var new_velocity: Vector3 = global_position.direction_to(next_path_position) * movement_speed
if navigation_agent.avoidance_enabled:
navigation_agent.velocity = new_velocity
else:
_on_velocity_computed(new_velocity)
func _on_velocity_computed(safe_velocity: Vector3):
velocity = safe_velocity
move_and_slide()
Instead of using a NavigationAgent node you could just query a pure navigation path for debug with the NavigationServer3D.map_get_path() function. That would rule out any agent setup issues that might interfere. If this function returns a path it is something with the nodes. If this function does not return a path there is no navigation mesh on the navigation map for whatever reason.
so use @onready var navigation_agent: NavigationAgent3D = NavigationServer3D.map_get_path()
instead of @onready var navigation_agent: NavigationAgent3D = get_node("NavigationAgent3D")
to debug what the issue is?
Thank you! I think I got it setup so that I am querying the pure nav path and it is coming back as not finding the path and that there are nav mesh issues.
To be clear I attached this script to a new Node3D that is in the main/world scene, my understanding from the documentation is that would be okay but if it needs to be different let me know!
So my question is… what now? My nav mesh is clearly there and I am using default values for it (cell size, height, etc.) so I am unclear what I need to do differently to fix the nav mesh so that it “appears” correctly.
Also, in my original code (that had the issues of the agents pathing to “not the movement_target”) the agents were at least pathing using the same nav mesh setup, so that is doubly confusing for me.
extends Node3D
func _ready():
var default_3d_map_rid: RID = get_world_3d().get_navigation_map()
var start_position: Vector3 = Vector3(0.0, 0.0, 0.0)
var target_position: Vector3 = Vector3(5.0, 0.0, 3.0)
var path: PackedVector3Array = NavigationServer3D.map_get_path(
default_3d_map_rid,
start_position,
target_position,
true
)
if path.size() > 0:
print("Path found, issue might be with the nodes.")
else:
print("No path found, there might be no navigation mesh on the navigation map.")
this should really pretty straightforward, you have navigationregion on where the zombie can walk or find path in, then the target is the player location, can be its global posiiton. then throw the get_next_path_position() in _physics_process for the zombie to move
i ever coded navigation on 2d but not 3d, but in the nutshell it’s like:
you first need to determine the navigation agent’s target position
then, set the agent’s velocity by lerping the direction from the navigation agent’s get_next_path_position() minus its global position
var direction = nav_agent.get_next_path_position()-global_position
direction=direction.normalized()
velocity=lerp(velocity,direction*randf_range(100,150),delta*SPEED)
yes all of these are inside the func _physics_process(delta) function block
i just tell what’s the basics of navigation agent, once you done the baking of the navigation region 's mesh, it’s usually just let the agent to find the path for you.
I read your code in the original post. you have send the update target location of the car every physics process, now it’s probably just the way it moves is not right, hence it failed to move as expected
try change the movement code similar to the snippet i shown, instead of move_toward, try lerping
Thanks for helping me out again @zdrmlpzdrmlp but I am still experiencing issues and hoping you could help.
I have implemented your suggestions (I believe) and I am still experiencing the behavior where the zombies path to a single point that is not the Vehicle (aka the player). Also, the zombies are moving very fast now and not sure if I have some leftover logic messing with that or not.
Here is my zombie.gd in it’s entirety
extends CharacterBody3D
@export_group("Zombie Stats")
@export var movement_speed: float = 4.0
@export var Health = 1
@onready var vehicle = $"../Vehicle"
@onready var nav_agent: NavigationAgent3D = get_node("NavigationAgent3D")
@onready var sfx_zombie_run_over = $"../sfx_zombie_run_over"
@onready var zombies_killed_label = get_node("hud/Score/Zombies_Killed/zombies_killed_label")
signal zombie_killed
#Official method that doesn't work
#func _ready() -> void:
#navigation_agent.velocity_computed.connect(Callable(_on_velocity_computed))
#Official method that doesn't work
#func set_movement_target(movement_target: Vector3):
#navigation_agent.set_target_position(movement_target)
func _physics_process(delta):
nav_agent.target_position = vehicle.global_position
var current_location = global_transform.origin
var next_location = nav_agent.get_next_path_position()
var new_velocity = (next_location - current_location).normalized() * movement_speed
var direction = nav_agent.get_next_path_position()-global_position
direction = direction.normalized()
velocity = lerp(velocity,direction*randf_range(100,150),delta*movement_speed)
velocity = velocity.move_toward(new_velocity, .25)
move_and_slide()
func update_target_location(target_location):
nav_agent.target_position = target_location
#Official method that doesn't work
#if navigation_agent.is_navigation_finished():
#return
#
#var next_path_position: Vector3 = navigation_agent.get_next_path_position()
#var new_velocity: Vector3 = global_position.direction_to(next_path_position) * movement_speed
#if navigation_agent.avoidance_enabled:
#navigation_agent.velocity = new_velocity
#else:
#_on_velocity_computed(new_velocity)
#func _on_velocity_computed(safe_velocity: Vector3):
#velocity = safe_velocity
#move_and_slide()
## Bullet damage logic
func Hit_Successful(Damage, _Direction: Vector3 = Vector3.ZERO, _Position: Vector3 = Vector3.ZERO):
Health -= Damage
if Health <= 0:
zombie_killed.emit()
queue_free()
print("zombie shot")
## Collision damage logic
func _on_body_entered() -> void:
zombie_killed.emit()
$"../sfx_zombie_run_over".play()
queue_free()
print("zombie ran over")
# This function will be called from the Main scene.
func initialize(start_position, player_position):
# We position the mob by placing it at start_position
# and rotate it towards player_position, so it looks at the player.
look_at_from_position(start_position, player_position, Vector3.UP)
your code for the physics process looks like this now?
func _physics_process(delta):
nav_agent.target_position = vehicle.global_position
var current_location = global_transform.origin
var direction = nav_agent.get_next_path_position()-global_position
direction = direction.normalized()
velocity = lerp(velocity,direction*randf_range(100,150),delta*movement_speed)
move_and_slide()