There are a few different issues here.
First, you're constructing toRotation
as a relative rotation (some amount of change in rotation)
Quaternion toRotation = Quaternion.FromToRotation(transform.forward, direction);
But then you're applying toRotation
as a destination (a final rotation to ease toward)
transform.rotation = Quaternion.Lerp(transform.rotation, toRotation, speed * Time.time);
Imagine if we did this with position, starting with an object at x=8
that wants to move to x=10
. The change in x
is +2, but if we lerp toward that change value like so:
Mathf.Lerp(startPosition, endPosition - startPosition, weight)
...then as the interpolation weight gets closer to 1, our x
gets closer to 2 - not to 10 where we want it to be!
(When applied in a compounding way frame over frame, this will seek out an equilibrium point where x = lerp(x, 10 - x, weight)
, x = (1 - weight) * x + weight * 10 - weight * x
, 2 weight * x = weight * 10
, 2 x = 10
, x = 5
... still not where we wanted it to be!)
So, first we need to describe the destination orientation we want to have. It looks like you probably want something like this:
// Calculate direction as before.
Vector3 direction = target.position - transform.position;
// Use LookRotation to form a rotation that looks along that vector.
// (You can optionally pass a second vector as "up" to control the twist).
Quaternion toRotation = Quaternion.LookRotation(direction);
Then, if you want to rotate toward this location at a constant rate, as John Hamilton says, you can use the Quaternion.RotateTowards method:
float step = speed * Time.deltaTime;
transform.rotation = Quaternion.RotateTowards(transform.rotation, toRotation, step);
Or, if you want the aim to be faster the further off-target it is, and gradually ease-in as it locks on, you can keep the exponential ease you were using before, but with the destination as calculated above, and correcting the time adjustment like so:
// Here "speed" is replaced by "sharpness," a parameter between 0 & 1
// that controls how smooth vs fast the aim is.
// referenceFramerate is a constant like, say, 30.0f. It adjusts the
// "units" of sharpness to be the same as you'd use at that framerate
blend = 1f - Mathf.Pow(1f - sharpness, Time.deltaTime * referenceFramerate);
transform.rotation = Quaternion.Lerp(transform.rotation, toRotation, blend);