I tested this in Godot 4.7.1. look_at() applies the rotation immediately. Since a top-down character usually only rotates around the Y axis, you can interpolate the yaw with lerp_angle():
@export var turn_speed := 8.0
func _process(delta: float) -> void:
var mesh := %MeshInstance3D
var target := %Camera3D.project_position(
get_viewport().get_mouse_position(),
6.9
)
var direction := target - mesh.global_position
if is_zero_approx(direction.x) and is_zero_approx(direction.z):
return
# Godot's default forward direction is -Z.
var target_yaw := atan2(-direction.x, -direction.z)
# This stays between 0 and 1, even during a long frame.
var weight := 1.0 - exp(-turn_speed * delta)
var new_rotation := mesh.global_rotation
new_rotation.y = lerp_angle(new_rotation.y, target_yaw, weight)
mesh.global_rotation = new_rotation
Adjust turn_speed to control the response. If the model faces +Z instead of Godot’s default -Z, add PI to target_yaw.
Also, project_position() defines z_depth as a distance into the scene, so it should normally be positive. With a straight-down camera 6.9 units above the ground, 6.9 reaches the ground plane while -6.9 projects behind the camera.
Thank you for this. I was wondering what the if statement was doing. I was also wondering if there was a way to prevent the rotation slowing down as you reach the target value. I tried move_toward, but as I moved my mouse to the lower half of the screen near the middle, the mesh would rotate the other way to reach my target point. Again, thank you so much for this,