Global positions between scenes wont match up. Nav agent goes towards top left direction

Godot Version

4.3

Question

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:
image

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:

image

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:

  1. 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.

1 Like

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.
image

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