Path3D racetrack with fences following the path

Godot Version

4.3

Question

Hi all,

I created a racetrack with Path3D and want some fences and stugg at the sides. I sawe a video about a script with CSGPolygon#D but Is it also possible another way?

Someone who can help me with some other kind of tutorial?

CSGPolygon3D is a great option for path-following geometry without scripts. With a script one could create meshes along the path by sampling points on the Path3D’s curve.

Ok So it is still a good way to use that.

I only can not find some tutorial of placing objects besides the road. I dont have to place it everywhere but every few meters.

Now I saw this tutorial (https://www.youtube.com/watch?v=4nOEVPjVmjc)but that is not working for version 4.3 or 4.4 if Im right. Anyone know if there are more tutorials like this? Or example games.

Not sure why that wouldn’t work for 4.3, I know 4.4 upgraded their CSG library, but the scripting part should also remain the same.

What troubles did you run into?

The objects I want to spawn are on the same place. If I put 10 then ten on one place, 50 then 50 one one place and so on.

and can you paste your script in charge of spawning along the path?

@tool
extends Path3D


@export_range(0, 100) var item_count = 1:
	set (value):
		item_count = value
		spawn_item()
	get:
		return item_count

func spawn_item():
	var offsets = []
	var points = curve.get_baked_points()
	var upvectors = curve.get_baked_up_vectors()

	for child in $Spawn.get_children():
		child.free()

	for i in range(item_count):
		offsets.append(float(1)/float(item_count+1))

	for offset_idx in range(offsets.size()):
		var idx = clamp(int(points.size()*(offsets[offset_idx])), 0.0, points.size()-1)
		var point = points[idx]
		var upVector = upvectors [idx]

		var item_ = Node3D.new()
		item_.name = "item_"
		$Spawn.add_child(item_)
		item_.translate(point)

		var fence = preload("res://fence.tscn").instantiate()
		fence.name = "fence"
		item_.add_child(fence)

		fence.translate(Vector3(0.05,0.0,0.0))
		
		if idx+1 > points.size()-1:
			item_.look_at(points[0], upVector)
		else:
			item_.look_at(points[idx+1], upVector)
			
		item_.set_owner(get_tree().get_edited_scene_root())
		fence.set_owner(get_tree().get_edited_scene_root())

This might be an integer multiplication, try modifying that line to this and using more explicit types for the entire script.

var idx: int = clampi(float(points.size())*offsets[offset_idx], 0, points.size()-1)

Of course adding a breakpoint or print statements to test each variable will be enlightening.

Screenshot 2025-01-10 195944

Thanks for your input. After your change it still did not work.

I restarted the script and now have this what is working. Now every few meters there is a fence and are visible. I go to expand with more code but you helped me a lot.

@tool
extends Path3D

@export var fence_scene = preload("res://fence.tscn")
@export var spacing = 3.0

func _ready():
	# Zorg ervoor dat de node "VisibleItems" bestaat
	if !$VisibleItems2:
		var visible_items = Node3D.new()
		visible_items.name = "VisibleItems2"
		add_child(visible_items)
	spawn_fences()

func spawn_fences():
	# Verwijder bestaande hekken van "VisibleItems"
	for child in $VisibleItems2.get_children():
		child.queue_free()

	var points = curve.get_baked_points()
	var up_vectors = curve.get_baked_up_vectors()
	var distance = 0.0

	for i in range(points.size()):
		if distance >= spacing:
			var fence_instance = fence_scene.instantiate()
			fence_instance.translate(points[i])
			$VisibleItems2.add_child(fence_instance)
			fence_instance.look_at(points[(i + 1) % points.size()], up_vectors[i])
			distance = 0.0
		distance += points[i].distance_to(points[(i + 1) % points.size()])

What I have to figure out now is how the fence can follow the path3d and turns itself with it.

Now have this script with a test path, only the fences are not next to the line. How can I let them follow exactly next to the line?

@tool
extends Path3D

@export var fence_scene = preload("res://fence.tscn")
@export var spacing = 3.0

func _ready():
	# Zorg ervoor dat de node "VisibleItems" bestaat
	if !$VisibleItems2:
		var visible_items = Node3D.new()
		visible_items.name = "VisibleItems2"
		add_child(visible_items)
	spawn_fences()

func spawn_fences():
	# Verwijder bestaande hekken van "VisibleItems"
	for child in $VisibleItems2.get_children():
		child.queue_free()

	var points = curve.get_baked_points()
	var up_vectors = curve.get_baked_up_vectors()
	var distance = 0.0
	var last_index = 0

	for i in range(points.size()):
		distance += points[last_index].distance_to(points[i])
		if distance >= spacing:
			var fence_instance = fence_scene.instantiate()
			fence_instance.translate(points[i])
			$VisibleItems2.add_child(fence_instance)

			var next_index = (i + 1) % points.size()
			fence_instance.look_at(points[next_index], up_vectors[i])
			fence_instance.rotate_object_local(Vector3(0, 1, 0), curve.get_baked_points()[i].angle_to(Vector3.RIGHT))

			distance = 0.0
			last_index = i

Nevermind, already figured it out. The object in the fence.tscn was placed wrong. I turned it 90 degrees and now it is going much better.