Smooth LookAt in Unity
Turn an object smoothly towards a target instead of snapping like transform.LookAt(), with smoothing that feels the same at every frame rate.

Quest objectives
- Turn towards a target with Quaternion.LookRotation and Slerp
- Make the smoothing independent of the frame rate
- Keep characters upright by turning around the Y axis only
- Turn at a constant speed with RotateTowards
transform.LookAt(target) turns an object to face a target in one go. For a turret, a character or a camera that is usually too abrupt: it should turn towards the target over a few frames instead. This component does that. Add it to the object that should turn.
The script
using UnityEngine;
public class SmoothLookAt : MonoBehaviour {
[SerializeField] Transform target;
[Tooltip("How quickly the rotation catches up. Higher is snappier.")]
[SerializeField] float sharpness = 12f;
[Tooltip("Turn around the Y axis only, so the object stays upright.")]
[SerializeField] bool yawOnly = true;
public Transform Target {
get => target;
set => target = value;
}
void LateUpdate() {
if (target == null) {
return;
}
Vector3 direction = target.position - transform.position;
if (yawOnly) {
direction.y = 0f;
}
// Nothing to look at when the target is right here
if (direction.sqrMagnitude < 0.0001f) {
return;
}
Quaternion lookRotation = Quaternion.LookRotation(direction);
// The same smoothing at every frame rate
float t = 1f - Mathf.Exp(-sharpness * Time.deltaTime);
transform.rotation = Quaternion.Slerp(transform.rotation, lookRotation, t);
}
}Every frame it works out where the target is and turns part of the way there:
The direction is the target's position minus its own. With Yaw Only on, its height is set to zero, so the object only turns left and right. That is what you want for a character or the base of a turret, which would otherwise lean towards targets above or below them.
If the target is right where the object is, there is no direction to look in, and the script leaves the rotation alone.
Quaternion.LookRotationwould only complain about a zero vector in the Console.Quaternion.LookRotationmakes the rotation that looks along the direction, andQuaternion.Slerpturns the current rotation part of the way towards it.
The script runs in LateUpdate, after every Update, so it sees where a moving target ended up in this frame. The target is a private field that you set in the Inspector, and other scripts change it through Target.
The same at every frame rate
The usual way to write the last step is this:
// Don't: this turns at a different speed at every frame rate
transform.rotation = Quaternion.Slerp(
transform.rotation,
lookRotation,
sharpness * Time.deltaTime
);Thanks to Time.deltaTime it looks independent of the frame rate, but it isn't. At 60 frames per second every frame turns a fifth of the remaining way, at 20 frames more than half. The lower the frame rate, the faster the object turns, and below 12 frames per second the value is over 1 and the object snaps to the target at once.
1f - Mathf.Exp(-sharpness * Time.deltaTime) fixes that: the part of the angle that is left shrinks by the same factor every second, however many frames that second has. sharpness reads as "how quickly": at 12 the object has turned about 70% of the way after a tenth of a second and 95% after a quarter.
Turning at a constant speed
Smoothing slows down towards the end, which feels natural for heads and cameras. A turret or a mechanical part turns at a constant speed instead. Quaternion.RotateTowards turns by at most the given number of degrees:
[SerializeField] float degreesPerSecond = 180f;
// ...and in LateUpdate(), instead of the last two lines:
transform.rotation = Quaternion.RotateTowards(
transform.rotation,
lookRotation,
degreesPerSecond * Time.deltaTime
);Trying it out
Add SmoothLookAt to a cube, drag a second object into Target and press Play. Move the target around in the Scene view, and the cube turns its front, the blue Z axis, after it. Change Sharpness while the game runs to find the value that suits your object.