Limited Radius 3D Ray Casting

Godot Version

`Godot 4.2.2

Question

` Hello,

Can someone direct me to where I can do a more limited version of Raycasting in a 3D project?

Working on creating some RTS like building placement but in third person. I have found some tutorials for more traditional RTS placement cameras that are using Raycasting to find the placement from Camera. However I would like to restrict the placement radius to close to the character, sort of like Engineer Sentry Placement in TF2, and I am struggling to find how to restrict it.

Thanks!

Share your current code please and the scene structure, then we can help you with restricting it.

That would make sense haha.

Heres where I am ray casting to find where the mouse is pointing and then setting the inseparables position. The Spawnable is a StaticBody3D :

func _process(delta):
	if GameManager.CurrentState == GameManager.State.Building:
		var camera = get_viewport().get_camera_3d()
		var from = camera.project_ray_origin(get_viewport().get_mouse_position())
		var to = from + camera.project_ray_normal(get_viewport().get_mouse_position()) * 10
		
		var cursorPos = Plane(Vector3.UP, transform.origin.y).intersects_ray(from, to)
		
		print("mouse ps:" +str(cursorPos))
		CurrentSpawnable.position = Vector3(cursorPos.x, cursorPos.y, cursorPos.z)
		
	pass>

First of all you need some reference to the Player, so that we can grab its position. You also need to define the maximum distance from the Player that you want to allow the building.
Then, in the script we need to check if the cursor position is within the defined limit and if not, lerp to be within the limit.
See below a rough implementation of that idea. I haven’t tested this code, just wrote it here raw, so in case you have any issues with it - let me know.

const RAYCAST_LENGTH = 10000.0
@export var player: Node3D
@export var max_distance: float = 5.0
func _process(delta):
	if GameManager.CurrentState == GameManager.State.Building:
		var camera = get_viewport().get_camera_3d()
		var from = camera.project_ray_origin(get_viewport().get_mouse_position())
		var to = from + camera.project_ray_normal(get_viewport().get_mouse_position()) * RAYCAST_LENGTH
		
		var cursorPos: Vector3 = Plane(Vector3.UP, transform.origin.y).intersects_ray(from, to)
		print("mouse ps:" +str(cursorPos))
		
		var spawn_position: Vector3
		var distance_to_player: float = cursorPos.distance_to(player.global_position)
		if distance_to_player <= max_distance:
			spawn_position = cursorPos
		else:
			spawn_position = lerp(player.global_position, cursorPos, max_distance / distance_to_player)
		CurrentSpawnable.position = spawn_position

Hello,

I am not sure the lerp is doing the right thing. Through some troubleshooting I am getting values like this

mouse pos:(-4.695286, 0, 6.307405)
DistToPlayer: 7.86314630508423
LerpVector:(-7.383944, -0.000053, 9.91921)
final Position(-7.383944, -0.000053, 9.91921)

Any ideas? I did change distance to Player to Float because Godot didnt like it as a vector 3

Thinking more about it, would there be a method to use a sphere as areas of valid placement? Or am I getting lost in another direction?

You’re right, distance_to_player should be a float, my mistake.
And the lerp had distance_to_player / max_distance instead of max_distance / distance_to_player.
I corrected it all now in the original script above, please try again.
I also increased the RAYCAST_LENGTH to be 10000.0, because otherwise for some reason it was less accurate.

Well, the max_distance in my code is exactly that - a radius of a sphere. Then, the lerp() function makes sure that the final position is within that radius. Mathematically it is all around a sphere, you just have to imagine it :slight_smile:

This is how it works in my little demo.

Hooray that works! Thank you! Still trying to get my head around ray cast length but this was all very helpful!!!

1 Like