Take the 2-minute tour ×
Game Development Stack Exchange is a question and answer site for professional and independent game developers. It's 100% free, no registration required.

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) {
                            rigidbody.AddForce (Vector3.forward);               
                    }
            }                               

    }
share|improve this question

6 Answers 6

up vote 1 down vote accepted

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.

share|improve this answer

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)
                {
                    rigidbody.AddForce(Vector3.forward);
                    inTransition = false;
                }

            }

        }
    }
}
share|improve this answer
    
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 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. –  Lautaro Jan 30 at 13:13

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.)

share|improve this answer
    
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. –  Lautaro Jan 30 at 12:29

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.

share|improve this answer
    
Thanks for the tip. Ill look into that. –  Lautaro Jan 30 at 15:13

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);

Also rigidbody.AddForce makes object move in world space. Use relative instead:

rigidbody.AddRelativeForce(Vector3.forward);
share|improve this answer

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!");
                    }
            }
share|improve this answer

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.