[4.3] Instantiated scene spawning along Path3D broken in 4.3

Godot Version

4.3

Question

Hello Godoteers! It seems that 4.3 has broken some Path3D spawning logic that was working for me in 4.2 and after playing around with it for a few hours I am lost on what the problem could be and thus am stuck. Did something change with spawning along a Path3D in 4.3 and I missed it? Any help would be greatly appreciated!

Problem

In my project I have a Path3D that spawns 10 instantiated scene (zombies and a roadblock) along the PathFollow3D (see code below). This was working correctly in 4.2 but when I upgraded to 4.3 (and made no other changes to this code!) all 10 instantiated scenes spawn on top of each other instead of being equally distributed along the Path.

4.2 :slight_smile:
see how the roadblocks and “zombies” are spawned along the path around the track

4.3 :frowning:
see how the roadblocks and “zombies” are spawned all on top of each other

Debug Method

I made two copies of a 4.2 version of my project (same spawning code as my latest version) and opened it in 4.2 and the spawning works correctly (instantiated scenes equally distributed along Path3D) and I upgraded the other copy to 4.3. This clearly shows that with no other changes made something changed in the 4.3 to break the spawning.

I recreated the Path3D and PathFollow3D in 4.3 but the problem still persists.

This leads me to believe that 4.3 doesn’t like something in my code.

Any ideas on what could be wrong and how I fix it?

Scene Tree

main_world.gd

extends Node3D

var zombie_road_block_scene = preload("res://assets/zombie_road_block.tscn")


func _on_zombie_spawner_area_3d_vehicle_entered_zombie_spawner():
	print("_on_zombie_spawner_area_3d_vehicle_entered_zombie_spawner called")
	
	# Remove existing roadblocks from the world
	for child in get_children():
		if child.has_node("roadblock"):
			child.get_node("roadblock").queue_free()
	
	# Spawn road blocks
	var progress_step = 1.0 / 10.0 # Divide the range of progress ratios into 10 equal parts
	var current_progress = 0.0 # Start at the beginning of the path

	for i in range(10): # Spawn 10 road blocks
		var zombie_road_block = zombie_road_block_scene.instantiate()   
		var road_block_spawn_location = get_node_or_null("ZombieSpawnPath/ZombieSpawnLocation")
		
		## Find roadblock spawn location debug
		#if road_block_spawn_location:
			#print("Spawn location found: ", road_block_spawn_location.global_transform.origin)
		#else:
			#print("Spawn location not found!")

		# Create a new transform based on the road_block_spawn_location and remove the offset
		var spawn_transform = Transform3D(road_block_spawn_location.global_transform.basis, Vector3.ZERO)
		spawn_transform.origin = road_block_spawn_location.global_transform.origin - Vector3(0, 0, 5)  # Subtract the -5 Z-axis offset

		# Assign a progress ratio from one of the 10 equal parts with some randomness
		road_block_spawn_location.progress_ratio = current_progress + randf() * progress_step * 0.5
		var path_direction = road_block_spawn_location.global_transform.basis.z

		## Ray cast position debug
		#Get the path direction at the current progress ratio
		#var debug_raycast = debug_raycast_scene.instantiate()
		#debug_raycast.global_transform.origin = spawn_transform.origin
		#add_child(debug_raycast)
		
		# Generate random offsets perpendicular to the path direction
		var random_magnitude = randf_range(-1, 1) # Random magnitude for the offset
		var perpendicular_direction = path_direction.cross(Vector3.UP).normalized() # Perpendicular to the path direction
		var offset = perpendicular_direction * random_magnitude # Offset is now correctly perpendicular to the path

		# Apply the offset to the spawn location
		spawn_transform.origin += offset

		# Generate a random rotation angle in degrees, excluding 330-30, 60-120, 150-210, and 240-300 degrees
		var random_rotation_degrees
		while true:
			random_rotation_degrees = randi() % 360
			if (random_rotation_degrees >= 30 and random_rotation_degrees < 60) or \
				(random_rotation_degrees >= 120 and random_rotation_degrees < 150) or \
				(random_rotation_degrees >= 210 and random_rotation_degrees < 240) or \
				(random_rotation_degrees >= 300 and random_rotation_degrees < 330):
				break

		# Convert the rotation angle to radians
		var random_rotation = deg_to_rad(random_rotation_degrees)

		# Create a new transform for the zombie_road_block with the random rotation
		var zombie_road_block_transform = Transform3D()
		zombie_road_block_transform = zombie_road_block_transform.rotated(Vector3.UP, random_rotation)
		zombie_road_block_transform.origin = spawn_transform.origin

		# Apply the new transform to the zombie_road_block
		zombie_road_block.global_transform = zombie_road_block_transform
		
		zombie_road_block.scale = Vector3(7.3, 7.3, 7.3)
		
		add_child(zombie_road_block)
		
		## Set the vehicle reference for each zombie and connect signals
		zombie_road_block.get_node("zombie_a").vehicle = fiesta_vehicle
		zombie_road_block.get_node("zombie_b").vehicle = fiesta_vehicle
		zombie_road_block.get_node("zombie_c").vehicle = fiesta_vehicle
		zombie_road_block.get_node("zombie_a").sfx_zombie_run_over = sfx_zombie_run_over
		zombie_road_block.get_node("zombie_b").sfx_zombie_run_over = sfx_zombie_run_over
		zombie_road_block.get_node("zombie_c").sfx_zombie_run_over = sfx_zombie_run_over
		for zombie in zombie_road_block.get_children():
			if zombie.is_in_group("Enemies"):
				zombie.vehicle = fiesta_vehicle
				zombie.zombie_killed.connect(hud._on_zombie_killed)
				zombie.zombie_melee_attack.connect(vehicle_parent.zombie_melee_attack)
		
		# Move to the next part of the progress range for the next road block
		current_progress += progress_step

Try to explicitly write types, I know they add more and more type safety with each version in Godot, maybe some of the math is getting truncated to an int? Do you get any errors or warnings?

var progress_step: float = 1.0 / 10.0
var current_progress: float = 0.0

Thank you @gertkeno! I am a novice coder and know I need to do static typing et al

	var progress_step: float = 1.0 / 10.0 # Divide the range of progress ratios into 10 equal parts
	var current_progress: float = 0.0 # Start at the beginning of the path

I gave this a go but I am still getting the same result. No warnings or errors.

EDIT:
I went through and added static typing I was getting a warning about and no change in result.

Thanks again for your help, do you have any other ideas on what could be causing the issue I’m seeing post-4.3 upgrade?

I would try to print out a few of the math steps; maybe progress_ratio isn’t instantly updating the global_transform anymore? See if this prints out different locations

print(road_block_spawn_location.global_transform.origin)

I used print(spawn_transform.origin) (print(road_block_spawn_location.global_transorm.origin) errored out) and the 10 instantiated scenes are all spawning at the same location (approx).

(18.84491, -0.018829, -108.8023)
(18.81812, -0.018829, -107.233)
(18.8332, -0.018829, -108.1162)
(18.83657, -0.018829, -108.3139)
(18.81503, -0.018829, -107.0523)
(18.84243, -0.018829, -108.6572)
(18.83044, -0.018829, -107.9545)
(18.83408, -0.018829, -108.1679)
(18.81938, -0.018829, -107.3068)
(18.81296, -0.018829, -106.9308)

I think that’s your main lead is progress_ratio isn’t ending up at spawn_transform. See if progress_ratio updates progress

road_block_spawn_location.progress_ratio = current_progress + randf() * progress_step * 0.5
print("new progress: ", road_block_spawn_location.progress)

I wonder if the local transform, instead of the global_transform will work?

print(road_block_spawn_location.transform.origin)

Thanks @gertkeno! I found PathFollow3D progress and progress_ratio properties not working after upgrading from 4.2.2 to 4.3 · Issue #95612 · godotengine/godot · GitHub that seems to be the same issue that I am experiencing and it looks like the fix was committed yesterday. Do you know how I get this fix locally? Do I redownload Godot 4.3? Sorry for the noob question

2 Likes

The only way for you get this fix now is compiling the source code, otherwise you need to wait the 4.4 dev3

1 Like

Someone mentioned sampling the curve directly, it’s a good option without having to wait or download a beta version. Since nothing seems to “walk” along the path this solution could be a better option all around anyways, you could do away with the PathFollow3D node.

curve.sample_baked_with_rotation(pathfollow.progress)

Here’s what I think you could start with.

@onready var zombie_spawn_path: Path3D = $ZombieSpawnPath

var spawn_path_max: float = zombie_spawn_path.curve.get_baked_length()
var progress_step: float = spawn_path_max / 10.0
var current_progress: float = 0.0

for i in 10:
	var zombie_road_block = zombie_road_block_scene.instantiate()   

	var spawn_transform: Transform3D = zombie_spawn_path.curve.sample_baked_with_rotation(current_progress)
	spawn_transform.origin.z += 5
1 Like