How To Get 3D Position Of The Mouse Cursor

:information_source: Attention Topic was automatically imported from the old Question2Answer platform.
:bust_in_silhouette: Asked By Supatier

I’m trying to get the 3D position of my mouse cursor much like a gridmap coordinates just the X and Y with Z always constant. Using input event mouse motion or global position / relative only returns in pixel units, i need the 3D units. Thank you.

:bust_in_silhouette: Reply From: path9263

This is kind of a tricky question because your mouse doesn’t really have a 3D position. Your mouse has a 2D position on your screen (screen space) but that third, depth position will always stay the same. What you are probably looking for is raycasting. Raycasting allows you to project a ray out from your mouse’s 2D position in the direction that the camera is looking, then you test if that ray collides with anything or alternatively you can return the 3D position at a given projected distance. This image does a good job of illustrating a raycast:
enter image description here
If you want to learn more about Raycasting, this video and the text tutorial it links to will explain the concept in full detail although they are both not made specifically for Godot. Do take a look at Godot’s documentation on Raycasting though as it does simplify a lot of the concepts for you already.

Another option that is included in Godot that may be useful to you is the _input_event function on CollisionObjects. This function is called when a collisionObject is clicked on (or even just as the mouse passes over it) and it will give you the mouse position during the event as a Vector3 as well as which object was clicked. Consider connecting it with a signal to another script to make use of it elsewhere.

Thanks, but, any bare bone examples (assuming the distance to the object is known). I can’t quite figure it out how to rotate the raycast on the x and y axis based on the mouse location on the screen.

I was thinking on making a spotlight.
Maybe collision objects is the solution?

Supatier | 2018-04-23 01:56

:bust_in_silhouette: Reply From: ArdaE

Here’s the code you need. Set position2D to the mouse position within the viewport and z to your constant Z.

var dropPlane  = Plane(Vector3(0, 0, 1), z)
var position3D = dropPlane.intersects_ray(
                             camera.project_ray_origin(position2D),
                             camera.project_ray_normal(position2D))

this worked like a charm!

Ramshell | 2019-11-08 20:03

Can you show me the full code?

I couldn’t get the z const…
var dropPlane = Plane(Vector3(0, 0, 1), z)

Okan Ozdemir | 2019-11-30 21:14

Okan, you decide what z is based on where you want your drop plane. You can just replace it with a number.

ArdaE | 2019-11-30 23:14

Thanks Arda! You are a riot. That will nicely do

Okan Ozdemir | 2019-12-01 09:55

func _input(event):
	if Input.is_action_pressed("click"):
		var position2D = get_viewport().get_mouse_position()
		var dropPlane  = Plane(Vector3(0, 0, 10), z)
		var position3D = dropPlane.intersects_ray(camera.project_ray_origin(position2D),camera.project_ray_normal(position2D))
		print(position3D)

I made this code it only appears on the right side of the screen and 3rd point is constantly the same number

Okan Ozdemir | 2019-12-07 08:07

2 Likes
:bust_in_silhouette: Reply From: angelellij

I know this is a pretty old question and was probably solved. I am currently doing something similar, creating dots in an empty map. My solution was the next one:

static func get_event_spatial_position(mousePos:Vector2, viewport:Viewport, obj:Spatial)->Vector3:
#Parameters
var vSize = viewport.get_size()
var obj_rotation = obj.transform.basis.inverse()
var camera_pos = viewport.get_camera().transform.origin

#Corrections
var initial_correction = 720 / vSize.y #Viewport always streches keeping the 720 (1080/720)
var last_correction = 465

#Camera
camera_pos -= Vector3(0,0,1)
camera_pos = camera_pos * 1.1
camera_pos = obj_rotation * camera_pos 

#Vector2 Mouse Position
mousePos -= vSize/2
mousePos *= Vector2(1,-1)
mousePos *= initial_correction

#Vector3 Spatial Position
var spatialPos = Vector3(mousePos.x, mousePos.y, 0)

spatialPos = obj_rotation * spatialPos 
spatialPos /= last_correction 
spatialPos += camera_pos

return spatialPos

The thing I couldn’t figure out was the last correction. That one was a constant value i made by trial an error that made the dot go where it should.

Also, this code makes it always apear in position 0 of what would be the axis when rotating the camara (if z is depth that will be 0, if x is depth that would be 0). To change this you would need to change the starting z parameter in spatialPos.

Lastly, I do camera_pos -= Vector3(0,0,1) because that is its starting position.