In my project I have 2 scene instances within the main scene. These are the Player and the Enemy. For some reason the enemies global coordinates are way different from the players coordinates. I need a way for these two global coordinates to match up so I don’t have issues with parity between the two scenes. Also the enemies seem to locate to the top left of their nav area instead of following the player like they are meant to.
Enemy Code (PlayerInst is the autoload for the player script, used to reference the players position):
extends CharacterBody2D
@onready var health_label := $HealthBar
@onready var health := 2200
@onready var nav : NavigationAgent2D = $NavigationAgent2D
@export var SPEED := 100
func _ready() -> void:
health_label.text = str("Health: ", health)
nav.target_position = Vector2(PlayerInst.global_position)
func _physics_process(delta: float) -> void:
nav.target_position = PlayerInst.global_position
print("Enemy:", global_position)
var direction = nav.get_next_path_position() - global_position
direction = direction.normalized()
velocity = direction * SPEED
move_and_slide()
The enemies getting stuck in the corner and their positions compared to the player despite them being next to each other:
I really don’t know what I am doing with the global coordinates system as well as navigation so it would be nice if someone could help explain to me why this is happening and how to fix it. Thanks!
The large difference in global positions is likely because your player and enemy scenes are instanced at different points in your scene hierarchy. Each scene instance has its own local coordinate system that translates to different global coordinates
The enemies getting stuck in the top-left corner typically happens when the navigation mesh isn’t properly set up and the target position isn’t being properly converted between coordinate spaces.
Couple of things I would try.
1 Ensure Proper Coordinate Conversion
func _physics_process(delta: float) -> void:
# Convert global player position to local navigation space if needed
var target_pos = PlayerInst.global_position
# Debug print both positions to understand the difference
print("Player global:", PlayerInst.global_position)
print("Enemy global:", global_position)
nav.target_position = target_pos
# Make sure this line doesn't execute before the navigation has calculated a path
if nav.is_navigation_finished():
return
var direction = nav.get_next_path_position() - global_position
direction = direction.normalized()
velocity = direction * SPEED
move_and_slide()
2 Verify your navigation setup is correctly working as intended.
Ensure you have a properly configured NavigationRegion2D in your scene. Check that the navigation mesh covers the area where enemies need to move make sure the navigation agent’s properties (path desired distance, target desired distance) are appropriate
3 Make sure you navigation is updated.
In newer Godot versions, you need to wait for navigation to update:
func _ready() -> void:
health_label.text = str("Health: ", health)
call_deferred("actor_setup")
func actor_setup():
# Wait for the first physics frame to ensure navigation is ready
await get_tree().physics_frame
nav.target_position = PlayerInst.global_position
Let me know if you need help setting any of these up with your code or something like that. These are mostly based on my interpretations of Godot Docs. Hope it helps!
I did implement your suggestions. Though the problem still remains. I was thinking that it was coordinate conversion and that might be it like you suggested. The player coordinates are given correctly to the script and the target position is set as the player coords.
From what i can see, the “next pos” is set to the same as “player pos”, which is what should be happening except that the “next pos” is to the far top left of the “player pos” due to different coordinates between scenes. Shown below:
I’m just not sure how should I go about properly converting the coordinates from the player, into coordinates from the enemy scene. Would it be something with local coordinates?
Based on your new information and the screenshot, it looks like we’re dealing with a coordinate space conversion problem between your navigation system and the player position.
When you’re using NavigationAgent2D, there’s sometimes a mismatch between:
The global coordinate system (where your player position is reported)
and 2. The navigation mesh coordinate system (where paths are calculated)
Try something like this.
extends CharacterBody2D
@onready var health_label := $HealthBar
@onready var health := 2200
@onready var nav : NavigationAgent2D = $NavigationAgent2D
@onready var navigation_region = get_parent().get_node("NavigationRegion2D") # Adjust path as needed
@export var SPEED := 100
func _ready() -> void:
health_label.text = str("Health: ", health)
# Defer navigation setup to ensure the scene is fully ready
call_deferred("actor_setup")
func actor_setup():
# Wait for navigation to be ready
await get_tree().physics_frame
# Set initial target
update_target_position()
func update_target_position():
# Convert player's global position to be relative to the navigation region if needed
var target = PlayerInst.global_position
# Debug output
print("Player global:", PlayerInst.global_position)
print("Enemy global:", global_position)
# Set the target position
nav.target_position = target
func _physics_process(delta: float) -> void:
# Update target each frame
update_target_position()
# Wait for path calculation before moving
if not nav.is_navigation_finished():
# Get next position in the path
var next_pos = nav.get_next_path_position()
print("Next pos:", next_pos)
# Calculate direction to next position
var direction = (next_pos - global_position).normalized()
# Move toward next position
velocity = direction * SPEED
move_and_slide()
(I didn’t really have the time to write all this out so I wrote some basic instructions and had a AI expand on what I wrote. I tested it in my local env and it worked but lmk if it doesn’t work for you.)
Some other things you might try, Check the navigation region setup.
Make sure your NavigationRegion2D, Covers the entire playing area, Has a properly baked navigation mesh and Is set to auto-rebuild if the scene is dynamic.
If the navigation system continues to cause issues, you could try a simpler approach as well. Might not work with your specific set up but try something like:
func _physics_process(delta: float) -> void:
var direction = (PlayerInst.global_position - global_position).normalized()
velocity = direction * SPEED
move_and_slide()
And lastly. Check your scene hierarchy, Make sure your NavigationRegion2D and your characters are properly placed in the scene hierarchy. They should typically be siblings under the same parent for the coordinates to match properly.
I figured it out! Ended up just completely redesigning my code. I figured out that the position variable from the player using an autoload was quite unreliable. So instead I referenced the player instance directly in the main scene, and then made it constantly create new paths towards the player. Im not too sure what was wrong but it works now.
Thanks for the help!
Here is my enemy code for anybody else with the same problem:
extends CharacterBody2D
@onready var health_label := $HealthBar
@onready var health := 2200
@onready var nav : NavigationAgent2D = $NavigationAgent2D
@export var player: Node2D
@export var SPEED := 80
func _ready() -> void:
health_label.text = str("Health: ", health)
nav.target_position = player.global_position
func _physics_process(delta: float) -> void:
if nav.is_navigation_finished():
return
var direction = to_local(nav.get_next_path_position()).normalized()
velocity = direction * SPEED
move_and_slide()
func makepath() -> void:
nav.target_position = player.global_position
func _on_timer_timeout() -> void:
makepath()