A proper way to raycast from screen to 3D world space

Godot Version

Godot 4.2.2.stable

Question

Hi folks!

I’m developing a simple top-down 3D shooter and I have questions about converting screen coordinates to 3D world coordinates using raycasts.

To make the main character rotate around its axis and aim in a specific direction, the way I’m doing is to capture the mouse position on the screen and project the 2D coordinates to the 3D coordinates using raycast from the Camera3D. However, the way it is done in Godot seems to me a little bit laborious.

After reading the official documentation and watching some videos related to the topic, I came with this solution:

func _unhandled_input(event: InputEvent) -> void:
	if event is InputEventMouseMotion:
		const RAY_LENGTH = 5000.0
		
		var camera = get_viewport().get_camera_3d()
		var ray_origin = camera.project_ray_origin(event.position)
		var ray_end = ray_origin + camera.project_ray_normal(event.position) * RAY_LENGTH
		
		var space_state = get_world_3d().direct_space_state
		var intersection = space_state.intersect_ray(PhysicsRayQueryParameters3D.create(ray_origin, ray_end))
		if not intersection.is_empty():
			aim_at(intersection.position)

Comparing to Unity, where we can do something like this:

RaycastHit hit;
Ray ray = camera.ScreenPointToRay(Input.mousePosition);
if (Physics.Raycast(ray, out hit)) {
	// Do something
}

in Godot, I had to do all that stuff to perform the same type of operation.

So my question(s) is/are:

  • In Godot, is this the recommended/official way of doing this type of raycast?
  • Should I always specify a length for the raycast or is it possible to do with an infinite length?
  • Is there a more practical/simplified way of doing this type of operation?

Thanks in advance! :slight_smile:

Actually the best way in my oppinion is with CollisionObject3D — Godot Engine (stable) documentation in English with the signal input_event.

You pretty much do not need to do any transforms, just use the position argument or make a ray cast at all.

EDIT:
But, actually, I think what you could do is to not simply use any raycast at all, or any colliders, you could just transform your character into screen coordinates, detect your mouse click, then detect & set the angle between your screen character coordinates and mouse.

I guess this is the way to do it in Godot.

Does it have to be a raycast?

If not, the camera 3d has some methods built in that can help with projections.
Maybe this can work too (Did only a quick test, so no guarantie):

extends Camera3D


func _process(delta: float) -> void:
	var mouse_pos = get_viewport().get_mouse_position()
	var world_pos = project_position(mouse_pos, position.z)
	print(world_pos)