Quaternion.Slerp not smoothly rotating for some reason.

Hey everyone! I’m trying to make a teeter totter like object rotate and for some reason the rotation is instant, rather than a gradual movement. I had looked into Slerp and Lerp previously and am unable to get it to work with either. I was wondering if anyone had any insight as I’m sure i’m missing something stupid and easy xD.

Thank you! Here is the method.

private void Rotate(float rotateAmount)
    {
        var oldRotation = transform.rotation;
        transform.Rotate(0, 0, rotateAmount);
        var newRotation = transform.rotation;

        for (float t = 0; t <= 1.0; t += Time.deltaTime)
        {
            transform.rotation = Quaternion.Slerp(oldRotation, newRotation, t);
        }
        transform.rotation = newRotation;
    }
}

There are a number of issues with your approach here.

  1. You are setting the object’s rotation to the final goal rotation at the start of the Rotate function
transform.Rotate(0, 0, rotateAmount);

In other words, before you’ve even attempted to begin the process of gradual rotation over time, you’ve simply done the entire rotation in one line of code. Your object is already done rotating from the get go!

  1. You are then attempting to perform all of the smaller interval rotations in a single for loop. Presumably you are calling this function exactly once per frame.
for (float t = 0; t <= 1.0; t += Time.deltaTime)
        {
            transform.rotation = Quaternion.Slerp(oldRotation, newRotation, t);
        }

So think about it… if you enter a new frame and then rotate something by, say, 1 degree, but do so 180 times before the next frame has even begun to render, then as far as the player of your game can see, the object simply rotated 180 degrees instantaneously - even if it did so in 180 1-degree increments on your computer’s processor.

You are so close with this part, though! The funny thing is that your usage of Quaternion.Slerp IS actually successfully rotating the object from start to finish in a succession of smaller rotations. The problem is that it’s doing so all at once in a single function call, which, again presumably is being called from your update() function, so the entire rotation process is occurring all at once in one frame rather than spreading out over successive frames.

  1. Finally, you set the object’s rotation equal to newRotation. If you scroll up and see what you last assigned new rotation to be, you’ll realize you set it equal to the object’s rotation! This is some goofy circular logic that accomplishes nothing. and, if you remember from point number one, you had already set the object’s rotation to the final rotation destination. So for the third time in a row, you are just setting the object’s rotation directly to it’s final goal rotation rather than incrementing through a series of smaller rotations.

In short, here is what your code does:

  • Immediately set the object;s rotation to the final intended rotation
  • Store that final rotation in a variable called “newRotation”
  • Now successfully rotate the object as you intended - except all in one frame so the user will never see or know about it.
  • Even though the rotation is ALREADY once again at it’s final destination, you indrectly set it equal to this destination yet again by setting it’s value to “newRotation”, which was only ever set to be equal to the final destination rotation in the first place.

Well crap, I apologize, but I’ve run out of time to give you a proper solution at the moment. I’ll be back eventually for that if somebody doesn’t beat me to it (though they probably will as I think this is a common issue). In the meantime, however, I encourage you to think about how you might fix the problem yourself. A couple hints - your main game loop (ie your update function) should be the only “loop” over which you are iterating your rotations. Don’t do it in a single for loop. And you’ll need to keep track of how far through/how long the rotation has been occurring in a varaible outside of the rotate function itself. That way your function will know how far through the “slerp” process it should iterate on each call.

Told ya I’d be back!

Okay, so here’s my solution:

This looks a bit more complex and bloated than what you were going for, but I wanted to try to help you understand how to account for some variable and situations you didn’t consider - for example how quickly the object should rotate.

Also, because the third argument of the Quaternion.Slerp() function is supposed to be a decimal fraction representing the progress through the total rotation, you need to figure out WHAT the proper decimal to represent the rotation is each frame. You obtain this value by dividing Time.deltaTime (or how long the last frame took) by the amount of time that the entire rotation will take.

This might be a bit overwhelming and confusing at first, so just ask about anything that isn’t clear to you from what I’m giving you here.

public class SlerpRotator : MonoBehaviour {

    public float rotateAmount = 180.0f; //Amount to rotate in degrees
    public float rotationRate = 20.0f;  //speed of rotation in degrees/sec

    private float rotationDuration; // How long the rotation will take

    private float rotationProgress = 0.0f; // The current progress through the total rotation as a
                                           // Decimal between 0 and 1

    private Quaternion startingRotation;  //The initial rotation of the object before beginning the Slerp

    private Quaternion destinationRotation; // The Quaternion representation of the completed rotation

    void Start () {
        rotationDuration = rotateAmount / rotationRate; //A 180-degree rotation at 20 degrees per second takes
                                                      //9 seconds

        startingRotation = transform.rotation; //Keep a record of the starting rotation - we need it to use the
                                               //Slerp function properly

        //Convert z-axis rotation in degrees to a quaternion
        destinationRotation = transform.rotation * Quaternion.Euler(0.0f, 0.0f, rotateAmount);
    }
   
    // Update is called once per frame
    void Update () {

        float rotateAmount = Time.deltaTime / rotationDuration; // Amount to rotate as a decimal fraction of
                                                                // The whole intended rotation

        if (rotationProgress < 1.0f - rotateAmount)
        {
            rotationProgress += rotateAmount;

            /* NOTE!  Quaternion.Slerp takes three arguments:
                1.  The starting rotation as a quaternion
                2.  The ending/target rotation as a quaternion
                3.  How far the object should have progressed through the total rotation as a decimal fraction
                    (That is a value between 0 and 1)
            */

            transform.rotation = Quaternion.Slerp(startingRotation, destinationRotation, rotationProgress);
        } else
        {
            rotationProgress = 1;  //The rotation is complete
            transform.rotation = Quaternion.Slerp(startingRotation, destinationRotation, rotationProgress);

            // Alternately (and preferably for performance - I only use the above code to help
            // you understand how Slerp function works):
               
                // transform.rotation = destinationRotation
        }
       
    }

}
1 Like