How to make character face at cursor's direction even when moving around

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

I’ve been trying to make a top-down game where the character faces towards the position of the mouse. I searched online and I figured out I have to use raycasting. More specifically, I implemented the following code:

func _input(event):
	if event is InputEventMouseMotion:
		var from = camera.project_ray_origin(event.position)
		var to = from + camera.project_ray_normal(event.position) * ray_length
		$player.look_at(to*Vector3(1,0,1), from)

where player is the character in question and ray_length is the distance travelled by the ray. It is worth noting that ‘player’ is a separate scene made up of a KinematicBody and a mesh.
Though everything works as expected when the player is in the middle, everything starts malfunctioning when I move the player using move_and_slide(). This is when the object stops rotating fully in a circle.

What am I doing wrong?

:bust_in_silhouette: Reply From: BraindeadBZH

First, if you want to update something in realtime it is better to use the _process function and if you have to move a physical object, like a KinematicBody, _physics_process.

Second, if I remember correctly, to orient a node in the direction of the mouse is to start with get_local_mouse_position (Doc). And the rotation is calculated with something like Vector2(-mouse_vec.y, mouse_vec.x).angle().

Thank you for your comment!

get_local_mouse_position throws an error:
“Method get_local_mouse_position is not declared in the current class”

johnygames | 2019-07-13 23:54

This is in 3D, get_local_mouse_position() is only available for 2D nodes. Call get_viewport().get_mouse_position() for global position and subtract the node position to get the local mouse position.

Dlean Jeans | 2019-07-14 08:13

Yes, you’re correct I didn’t realized the question was ask for 3D.

BraindeadBZH | 2019-07-14 10:12

:bust_in_silhouette: Reply From: Dlean Jeans

Just tried this and it works way better than the raycast method:

func _physics_process(delta):
	var offset = -PI * 0.5
	var screen_pos = get_viewport().get_camera().unproject_position(player.global_transform.origin)
	var mouse_pos = get_viewport().get_mouse_position()
	var angle = screen_pos.angle_to_point(mouse_pos)
	player.rotation.y = -angle + offset

Raycasting requires physics bodies, if no bodies are present then it would not work anymore.

Wow, that works!

It looks very complicated, though. I do not know how I was supposed to come up with that solution :slight_smile:
Anyway, it works better than expected, because it works even when the camera is not facing top-down in a 90 degree angle to the floor. Kudos!

johnygames | 2019-07-14 09:54

Dunno how I came up with that, but it’s exactly how you handle it in a 2D top-down shooter with the only extra step is to unproject the player position from 3D to 2D, not that complicated.

Before that, I also worked on a 3D top-down shooter with raycasting for mouse handling so I know it doesn’t work very well.

Dlean Jeans | 2019-07-14 10:06