You must be looking at Godot 3 tutorials. Be aware that since Godot 2 was very niche, many Godot 3 tutorials released before Godot 4 do not specify for which version of Godot they are.
About the documentation: you can the version of Godot for which you are reading the documentation in the bottom left, and also in the URL.
As per intersect_ray
, the concept is the same, but the API is different. In particular: in Godot 3 intersect_ray
takes many optional arguments. Bug for Godot 4 they were unified into an object.
So in GODOT 3 you would be doing something like this:
var space_state := get_world().direct_space_state
var result := space_state.intersect_ray(origin, end)
You can look at the documentation for intersect_ray
in Godot 3 for the extra parameters. The article Ray-casting (in Godot 3) has further explanation.
The same in GODOT 4 would be like this:
var space_state := get_world_3d().direct_space_state
var query := PhysicsRayQueryParameters3D.create(origin, end)
var result := space_state.intersect_ray(query)
You can look at the PhysicsRayQueryParameters3D
class for the extra properties. The article Ray-casting (in Godot 4) has further explanation.
To get the position in 3D the mouse is hovering over in Godot 4, you first need to get the mouse position and the current Camera3D
:
var viewport := get_viewport()
var mouse_position := viewport.get_mouse_position()
var camera := viewport.get_camera_3d()
Then we can get the origin and direction of the ray for that point:
var origin := camera.project_ray_origin(mouse_position)
var direction := camera.project_ray_normal(mouse_position)
Since we need origin and end, we compute end using the origin the direction, and some maximum length for the ray... Let us use far
from the Camera3D
for the length of the ray:
var ray_length := camera.far
var end := origin + direction * ray_length
And, of course, let us use intersect_ray
:
var space_state := get_world_3d().direct_space_state
var query := PhysicsRayQueryParameters3D.create(origin, end)
var result := space_state.intersect_ray(query)
And, of course, read the position:
var mouse_position_3D:Vector3 = result["position"]
Which reminds me, if there was no intersection, you get an empty Dictionary
, let us deal with that:
var mouse_position_3D:Vector3 = result.get("position", end)
Here if the result is empty (and thus, does not have a "position"
key) you get the end of the ray as position.
Know that if you want to deal with this by other means, you can check if the result is empty instead:
var mouse_position_3D := end
if not result.is_empty():
mouse_position_3D = result["position"]
Also, you can write result.position
instead of result["position"]
, but I'm using the latter here to highlight that it is a key in a Dictionary
.
You can, of course make the code shorter (I broke it into simple steps for the explanation). You could use a more sensible ray length. And perhaps cache viewport
so you don't have to get it every call... (you only would need to get it when the Node
where this code is running enters the scene tree).
In fact, you can use get_world_3d
on the Viewport
, in case you don't want to run this code on a Node3D
:
var space_state := viewport.get_world_3d().direct_space_state
And, of course, you can configure PhysicsRayQueryParameters3D
to exclude objects, to intersect areas in addition (or instead of) bodies, and to have an specific collision mask.