# Quaternion Lerp never reaches target

I seem to have misunderstood something about how LERP works. Im not sure what is missing. The object is rotating as it should, but it cant seem to get it to end. The transform.rotation dont seem to reach its target even if it has done so visually.

Quaternion rotationTarget;
Vector3 travelTarget;
float rotationLerpProgress;
float rotationLerpDuration = 1f;

// Use this for initialization
void Start ()
{

}

// Update is called once per frame
void Update ()
{
if (Input.GetMouseButtonDown (0)) {
var ray = Camera.main.ScreenPointToRay (Input.mousePosition);
RaycastHit rayHit;
if (Physics.Raycast (ray, out rayHit)) {
var rayHitPoint = rayHit.point;
travelTarget = rayHitPoint;
var rotationDirection = rayHitPoint - transform.position;
rotationDirection.y = 0;

rotationTarget = Quaternion.LookRotation (rotationDirection);
rotationLerpProgress = Time.deltaTime;
}
}
if (transform.rotation != rotationTarget) {

if (rotationLerpProgress < rotationLerpDuration) {

transform.rotation = Quaternion.Lerp (transform.rotation, rotationTarget, rotationLerpProgress / rotationLerpDuration);
rotationLerpProgress += Time.deltaTime;
print (string.Format ("({2}) - progress: {0} - duration: {1}  ", rotationLerpProgress, rotationLerpDuration, rotationLerpProgress / rotationLerpDuration));
} else {

print (string.Format ("setting rotation ({0}) to target ({1})", transform.rotation, rotationTarget));
transform.rotation = rotationTarget;
}
} else {
print ("rotaton is at target! :)");
if (transform.position != travelTarget) {
}
}

}


Lerp really never reaches the "end". As it gets closer and closer the values add more decimals. So if you are lerping from 0-1 you may reach 0.999999 but never actually reach "1". You can get around this by rounding up near the end or using a larger value for the percentage of the lerp.

Actually as stated here: http://docs.unity3d.com/Documentation/ScriptReference/Input.GetMouseButtonDown.html

Input.GetMouseButtonDown(0) is reset each frame and will not return true until the user has released the mouse button and pressed it again.

The update will immediately run when Start runs. Try using a bool to trigger it (something like this):

using UnityEngine; using System.Collections;

public class NewMonoBehaviour : MonoBehaviour
{

Quaternion rotationTarget;
Vector3 travelTarget;
float rotationLerpProgress;
float rotationLerpDuration = 1f;
float rotationLerpCurrent;
bool inTransition;

// Use this for initialization
void Start()
{
rotationLerpCurrent = 0;
}

// Update is called once per frame
void Update()
{
if (Input.GetMouseButtonDown(0))
{
inTransition = true;
var ray = Camera.main.ScreenPointToRay(Input.mousePosition);
RaycastHit rayHit;
if (Physics.Raycast(ray, out rayHit))
{
var rayHitPoint = rayHit.point;
travelTarget = rayHitPoint;
Debug.Log("rh: " + rayHitPoint);
var rotationDirection = rayHitPoint - transform.position;
rotationDirection.y = 0;

rotationTarget = Quaternion.LookRotation(rotationDirection);
rotationLerpProgress = Time.deltaTime;
}
}

Debug.Log("tr: " + transform.rotation + " rt: " + rotationTarget);
Debug.Log("ea tr: " + transform.rotation.eulerAngles + " rt: " + rotationTarget.eulerAngles);

if (inTransition)
{

if (transform.rotation != rotationTarget)
{

if (rotationLerpProgress < rotationLerpDuration)
{

transform.rotation = Quaternion.Lerp(transform.rotation, rotationTarget, rotationLerpProgress / rotationLerpDuration);
rotationLerpProgress += Time.deltaTime;
print(string.Format("({2}) - progress: {0} - duration: {1}  ", rotationLerpProgress, rotationLerpDuration, rotationLerpProgress / rotationLerpDuration));
}
else
{

print(string.Format("setting rotation ({0}) to target ({1})", transform.rotation, rotationTarget));
transform.rotation = rotationTarget;
}
}
else
{
print("rotaton is at target! :)");
if (transform.position != travelTarget)
{
inTransition = false;
}

}

}
}
}

• Also note that comparing whole vectors and quaternions is expensive. If possible, compare only what you need to compare; Use Mathf.Approximately to compare floats, etc. – beeradg Jan 30 '14 at 1:20
• Thanks for the tips! I added the bool and it is better ofcourse. Now i dont have the comparison of Quaternions every frame. Still i am missing something when it comes to the lerping. The object spins around quite fast and it takes double that time for the debug to log that is har finished rotating. – Daarwin Jan 30 '14 at 13:13

To add to Trevor's answer, directly using the time delta as the lerp amount doesn't quite work.

You need to use:

Quaternion qResult = Quaternion.Lerp(
qCurrent, qTarget, (float)(1 - Math.Exp(-k * t)));
// k = excitation constant (lower k (~1-2) for sluggish movement, higher k (~10)
//     for move snappish behavior)
// t = time delta


This will give you a more reliable and consistent movement based on time step.

This is taken from some C#/XNA code but the implementation is the same.

I assume that the problem is that you're holding down mouse button 0, which causes the interpolation to re-start every frame, according to the line:

rotationLerpProgress = Time.deltaTime;

Which means that if (rotationLerpProgress < rotationLerpDuration) will always evaluate to false, since rotationLerpProgress is being reset to Time.deltaTime every frame.

Since the above conditional never evaluates to false, you never hit the transform.rotation = rotationTarget; line in its "else" block, which would be the correct way for this movement to complete.

Additionally, you never reach your target through repeated Quaternion.Lerp calls (you're using rotationLerpProgress / rotationLerpDuration == Time.deltaTime / 1.0f == 0.0016 every frame, assuming 60 fps). Doing this, each frame the orientation will move 1.6% of the way from where it currently is toward its target orientation -- forever approaching the desired orientation, but never actually reaching it.

The correct solve for this problem is to not reset the rotationLerpProgress variable every frame, but only when a new turn operation is commenced.

(For most games, the ideal behaviour would be to not use an interpolation here at all -- to rotate the object at a specific rate, not over a specific duration. So that a short turn would take a shorter time than a longer turn. But I assume that you're using a constant duration regardless of turn-distance for a reason which makes sense for your game.)

• Thanks for answering! But its not correct since the GetMouseButtonDown only returns true the first cycle it is pushed. If you use GetMouseButton it will return true for as long as the button is down. – Daarwin Jan 30 '14 at 12:29

It can take slightly more time to complete because you use new rotation as a starting parameter for lerp. So when it visually completes rotation, it waits before "progress" reaches "duration".

If you want complete accuracy, you need to store an original rotation on click:

if (Input.GetMouseButtonDown(0)) {
//.....
originalRotation = transform.rotation;
}


and then use lerp like this:

transform.rotation = Quaternion.Lerp(originalRotation, rotationTarget, rotationLerpProgress / rotationLerpDuration);


rigidbody.AddRelativeForce(Vector3.forward);


This is how i made it work.

        if (Input.GetMouseButtonDown (0)) {
target = Camera.main.ScreenToWorldPoint (Input.mousePosition);
rotateToTarget = true;
print ("NEW TARGET: " + target);
}

if (rotateToTarget == true && target != null) {
print ("Rotating towards target");
float step = rotationSpeed * Time.deltaTime;

targetRotation = Quaternion.LookRotation (transform.position - target.normalized, Vector3.forward);
targetRotation.x = 0.0f;//Set to zero because we only care about z axis
targetRotation.y = 0.0f;

player.transform.rotation = Quaternion.Slerp (transform.rotation, targetRotation, Time.deltaTime * rotationSpeed);

if (Mathf.Abs (player.transform.rotation.eulerAngles.z - targetRotation.eulerAngles.z) < 1) {

rotateToTarget = false;
travelToTarget = true;
player.transform.rotation = targetRotation;
print ("ROTATION IS DONE!");
}
}