Child scene's relative position jumps around when other scene instantiated

Godot Version

Godot v4.4.1.stable - Linux Mint 22.1 (Xia)

Question

Hi, I am completely new to Godot, but have some programming knowledge and have worked with Unity before.

I want to spawn cars on a lane. For this, I have created a spawner script that spawns PathFollowers (which extends PathFollows2D) as a child of a given PathSegment (which extends Path2D) and adds some hazards (for example cars) as a child of the PathFollower node. I have done my best to incorporate the composition principles I’ve seen in some tutorials and have therefore ended up with this structure. For example, when two cars are spawned, the scene tree looks like this:

.
├── ...
└── Path2D
    ├── PathFollow2D
    │   └── AnimatableBody2D (root node of car.tscn)
    │       ├── Sprite2D
    │       ├── CollisionShape2D
    │       └── ...
    └── PathFollow2D
        └── AnimatableBody2D
            ├── Sprite2D
            ├── CollisionShape2D
            └── ...

The problem is that every time the spawner script spawns a new PathFollower with its car, every other car’s AnimatableBody2D’s relative position jumps in such a way that all cars are stacked up on each other at the start position. However, the PathFollow2D’s position works 100% as intended. This makes me think that I have somehow messed up the creation of my car scenes because different instantiated cars should have no reason to interact with each other.

Because I’ve messed around a little bit, I’ve come up with a somewhat minimal version of the script that doesn’t incorporate any path logic:

Spawner:

extends Node
class_name Spawner

@export var spawn_path_segment: PathSegment
@export var entity_scene_to_spawn = preload("res://gameplay/hazards/cars/car.tscn")
@export var spawn_interval: float = 5.0

@onready var _spawn_timer: Timer = %SpawnTimer

func _ready():
	_spawn_timer.wait_time = spawn_interval
	_spawn_timer.timeout.connect(_on_SpawnTimer_timeout)
	_spawn_timer.start()

func _on_SpawnTimer_timeout():
	spawn_entity_with_path_follower()

func spawn_entity_with_path_follower():
	var spawned_entity: Node = entity_scene_to_spawn.instantiate()
	
	var spawned_follower: PathFollower = PathFollower.new()
	spawned_follower.add_child(spawned_entity)
	spawn_path_segment.add_child(spawned_follower)

PathFollower:

extends PathFollow2D
class_name PathFollower

#Normally, there would be some path logic here but this suffices to make the problem clear
func _physics_process(delta):
	move_local_x(0.2)

If you need more information, I can absolutely provide that. Any help would be appreciated!

Sincerely

What do you see if you call spawn_path_segment.print_tree_pretty() at the end of spawn_entity_with_path_follower()?

┖╴PathSegment
    ┖╴@PathFollow2D@2
       ┖╴AnimatableBody2D
          ┠╴Sprite2D
          ┠╴CollisionShape2D
          ┠╴Traffic Surveillance
          ┃  ┠╴Long Range
          ┃  ┃  ┖╴CollisionPolygon2D
          ┃  ┖╴Short Range
          ┃     ┖╴CollisionShape2D
          ┖╴Evasion
             ┖╴CollisionShape2D

(Traffic surveillance and Evasion are in preparation for features I’ve not implemented yet and don’t do anything)

Is any of your code setting progress or progress_ratio?

In the “minimal setup” actually not. When the problem first appeared, I tried to move my cars along their lane, but when I tried to pinpoint the problem I actually commented out all the code with any reference to progress, and this is the version I shared with you.
I also want to show you a screen capture of my problem so that you can see exactly what is going on: ScreenApp (as you can see, the PathFollow2D does what it’s told - and it did that when it was told to follow the path too - the problem is its child which changes its local position). I hope this video helps :slight_smile: