Make an object face (rotate towards) the mouse cursor in 3D

Godot Version

4.5.1

Question

Greetings everyone, and happy Sunday!

I’m currently working on a side-scrolling platforming game using 3D models, somewhat similar to Little Big Planet.

Right now, I’m trying to make it so the player is able to hold a weapon and aim it by pointing the mouse cursor in the general direction he wishes to shoot in.
(Similar to what is shown in this 2D tutorial here: https://youtu.be/chIFtkuFnVw)

Since the player will only be able to move up/down and left/right, the idea is that the weapon should only be able to independently rotate around one axis, and not move in any other way.

And while this was simple enough in 2D using the look_at() function, I haven’t found any instructions online as to how I could implement this in 3D that worked for me.
(And LLMs appear to not have much information on how Godot works.)

Following the guides I found, which generally involved raycasting, the weapon would point in all sorts of directions, but I couldn’t get it to follow the mouse in any way (even without locking any axis).

I would be grateful if anyone could suggest a solution, which a 3D newbie like me can understand :slight_smile:

look_at() should work in 3D as well. You just need to properly calculate the target point in 3D space.

1 Like

How to properly calculate this is exactly my problem. :frowning:

For example, this code…

var mouse_position: Vector2 = (get_viewport().get_mouse_position())

var target_position = camera.project_position(mouse_position, 10)
var target_position_vector = Vector3(target_position.x,target_position.y,target_position.y)

if target_position != null:
    blaster.look_at(target_position_vector)

…does this…

…while this code…

var mouse_position: Vector2 = (get_viewport().get_mouse_position())

var ray_start: Vector3 = camera.project_ray_origin(mouse_position)
var ray_direction: Vector3 = camera.project_ray_normal(mouse_position)

var plane = Plane(Vector3(0,1,0))

var ray_intersect = plane.intersects_ray(ray_start, ray_direction)

if ray_intersect:
    blaster.look_at(Vector3(ray_intersect.x, ray_intersect.y, ray_intersect.z))

..does this…

Your second code should work. You just need to construct the proper plane. The plane normal should be the global axis you want gun to rotate around, and plane origin should be at global position of gun’s rotation pivot. Currently the plane you’re constructing is the ground plane.

1 Like

I tried setting the Plane’s normal to be the Z axis, which made the weapon follow the mouse somewhat, and by default the rotation appears to happen from the center of the weapon, which is fine.

var plane = Plane(Vector3(0,0,1))

Although it’s the wrong side that is following, and the front part (which is a child object of the weapon) appears to teleport to the other side when a certain angle is reached.

Rotate the gun mesh so it points in the negative z direction. That’s the direction look_at() uses as a pointing vector, or send true as the third argument to the call to use positive z. You also might want to supply different up axis as the second argument, because default up vector (0, 1, 0) might coincide with the look vector, causing the call to fail. Try using the rotation axis instead.

1 Like

Even before asking for help, I tried to rotate the weapon, but it didn’t have any effect.

When rotating it in the viewport before running the game, it snaps to the same rotation once the game is started, and the same thing happens when setting the rotation through something like…

blaster.set_rotation(Vector3(blaster.rotation.x, blaster.rotation.y, -blaster.rotation.z))

…and setting the third argument of look_at() to true didn’t appear to have an effect either.

(The weapon is a child of the player and controlled by the player’s script, if that makes a difference.)

What did have an effect, was changing the second argument of look_at().

Setting it to Vector3(0,0,1) resulted in this…

…while setting it to Vector3(1,0,0) resulted in this…

Rotate the mesh, not the node that’s doing the lookat. If you’re calling look_at() directly on the mesh instance you’ll need to rotate its mesh data in a 3D modeling app, or introduce an empty Node3D at the top of the gun hierarchy and position its children to point up its z axis.

1 Like

Thank you, I was finally able to fix it now! :slight_smile:

I created an empty Node3D and alligned the weapon’s MeshInstance3D with its Z axis.
After that, I referenced the Node3D to rotate the weapon instead of the MeshInstance3D, which allowed it to rotate as expected.

1 Like