You want to smoothly rotate a 3D object to point in a new direction.
When you first encounter this problem, you may find yourself thinking in terms of Euler angles - the three values representing the angles to the x/y/z axes. While Godot will allow you to see the object’s Euler angles in the
rotation property, it is not recommended to use them to work in 3D. There are a number of reasons why this the case, such as a problem called “gimbal lock”, where you lose one degree of freedom when one of your rotations reaches 90 degrees.
If you’re interested in the background behind Euler angles and the problems they introduce, like gimbal lock, here’s a video that explains it well.
We can avoid using 3D Euler angles in Godot by using the object’s
transform property. This property represents the body’s position and orientation in space. It uses a mathematical construct called a matrix to do this, but you don’t really need to understand the underlying math in order to make use of it.
Let’s say you have a 3D object such as a missile or arrow and you want it to point at its target. You can do this using the
func _process(delta): var target_position = $Target.transform.origin $Arrow.look_at(target_position, Vector3.UP)
This code would make our node (
$Arrow) always point at the target’s position, no matter how it moves.
look_at() requires 2 parameters: the target position, and an “up vector”. Imagine an airplane pointing its nose towards a target - there are an infinite number of ways it could be oriented, because the plane could roll about its axis. This second parameter is how you define what you want the final orientation to be.
The above code works, but it snaps the rotation instantly to the target. This might be fine if you have a very slow-moving target, but looks unnatural. It would look better if we move smoothly, or “interpolated”, the rotation smoothly between the starting orientation and the ending.
Godot has us covered here too. Rather than
look_at(), we can use the
looking_at() method, which doesn’t rotate the node, but returns the transform that would be looking at the target. Combine this with the
interpolate_with() method, which returns an intermediate transform between a current one and a target one, and we can smoothly transition between the current orientation and our desired one.
var speed = 5 func _process(delta): var target_position = $Target.transform.origin var new_transform = $Arrow.transform.looking_at(target_position, Vector3.UP) $Arrow.transform = $Arrow.transform.interpolate_with(new_transform, speed * delta)
Note that since
interpolate_with() operates on the
transform, it can be used to interpolate both rotation and position of an object.
That’s it! Use this handy method to rotate your 3D objects, and stop thinking about angles!