Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. We have updated the language to the Editor Terms based on feedback from our employees and community. Learn more.
    Dismiss Notice
  3. Join us on November 16th, 2023, between 1 pm and 9 pm CET for Ask the Experts Online on Discord and on Unity Discussions.
    Dismiss Notice

Quaternion.Lerp to rotate more than 360 degree

Discussion in 'Scripting' started by Keysaw, Dec 26, 2016.

  1. Keysaw

    Keysaw

    Joined:
    Oct 14, 2015
    Posts:
    19
    Hello !

    I spent the whole day trying to understand why what I need to do (which should be pretty simple) seems impossible... I'm sorry if my question seems to be an recurrent one, but I think I've read every thread about it without finding a proper answer.

    What I'm trying to achieve

    I have a cube. I need this cube to rotate in a random direction with a random angle (that can be more than 360°). And I need this rotation movement to be "Lerp".

    So for a given duration (let's say one second), the cube rotates and slowly stops.

    My problem

    I understand that Quaternions cannot achieve exactly what I want, because they cannot exceed 180° without going the other way. So my actual function works well, except that the rotation angle is quite small.

    I also tried a lot of things playing with Euler Angles, but I didn't manage either to get the result I need. No matter how I implement it, the rotation direction of the cube seems to change randomly during the Lerp, instead of turning always in the same direction (like a wheel).


    So, I would like to know if there is a way to play with Quaternions and achieve this simple effect. Maybe I just don't enough understand the way Quaternions work, and I'm missing something obvious, but... This is really driving me crazy, as it seems to be a pretty simple effect.

    I hope I gave enough details, if not feel free to ask me is anything else might help. Thank you a lot in advance!

    Here is the code I used to make the cube rotate (which works, but the maximum value of the random angle does not influence the rotation when it exceeds 180°). I get back to the most simple code, but I tried a bunch of much more complicated ways:

    Code (CSharp):
    1. private IEnumerator Rotation()
    2.     {
    3.         // Initialize the time variables
    4.         float currentTime = 0f;
    5.         float currentDuration = 1f;
    6.  
    7.         // Calculate a new random rotation
    8.         Quaternion newRotation = Quaternion.Euler(Random.Range(0f, 3600f), Random.Range(0f, 3600f), Random.Range(0f, 3600f));
    9.  
    10.         while(currentTime < currentDuration)
    11.         {
    12.             currentTime += Time.deltaTime;
    13.  
    14.             // Lerp the rotation
    15.             transform.rotation = Quaternion.Lerp(transform.rotation, newRotation, currentTime / currentDuration);
    16.  
    17.             yield return null;
    18.         }
    19.  
    20.         yield return null;
    21.     }
     
    Slashik likes this.
  2. AndyGainey

    AndyGainey

    Joined:
    Dec 2, 2015
    Posts:
    216
    Lerp is used to follow the shortest transformation from one value to another, and quaternions represent orientations, not rotations. So your large degree values are being lost upon conversion to quaternions, and then Lerp just tries to get from the start orientation to the final orientation with as little rotation as possible.

    As an alternative, I might suggest doing this using an angle/axis method: First figure out the current angle/axis of the objects orientation. Then generate a random axis, and a random angle in whatever large range you want. I recommend plus or minus a random amount off of the current angle just measured. Then interpolate from the original axis to the final axis, and also interpolate from the original angle to the final (large) angle. Convert the interpolated angle/axis back into a quaternion and set the orientation each frame.

    Also note that your use of Lerp isn't the intended pattern, and won't actually provide a linear interpolation. The first parameter is supposed to remain the same throughout all calls to Lerp over a transformation, and the last parameter is supposed to smoothly change from 0 to 1 over the course of that transformation. What you have will instead animate somewhat quickly at first, and gradually slow down as it approaches but never exactly reaches the final state. It also will not will last exactly the duration you want it to, making it hard to control.

    This code might fix both problems:

    Code (CSharp):
    1.         private IEnumerator Rotation()
    2.         {
    3.             // Initialize the time variables
    4.             float currentTime = 0f;
    5.             float duration = 1f;
    6.  
    7.             // Figure out the current angle/axis
    8.             Quaternion sourceOrientation = transform.rotation;
    9.             float sourceAngle;
    10.             Vector3 sourceAxis;
    11.             sourceOrientation.ToAngleAxis(out sourceAngle, out sourceAxis);
    12.    
    13.             // Calculate a new target orientation
    14.             float targetAngle = (Random.value - 0.5f) * 3600f + sourceAngle; // Source +/- 1800
    15.             Vector3 targetAxis = Random.onUnitSphere;
    16.  
    17.             while (currentTime < duration)
    18.             {
    19.                 // Might as well wait first, especially on the first iteration where there'd be nothing to do otherwise.
    20.                 yield return null;
    21.  
    22.                 currentTime += Time.deltaTime;
    23.                 float progress = currentTime / duration; // From 0 to 1 over the course of the transformation.
    24.  
    25.                 // Interpolate to get the current angle/axis between the source and target.
    26.                 float currentAngle = Mathf.Lerp(sourceAngle, targetAngle, progress);
    27.                 float currentAxis = Vector3.Slerp(sourceAxis, targetAxis, progress);
    28.                
    29.                 // Assign the current rotation
    30.                 transform.rotation = Quaternion.AngleAxis(currentAngle, currentAxis);
    31.             }
    32.         }
     
  3. Keysaw

    Keysaw

    Joined:
    Oct 14, 2015
    Posts:
    19
    Thank you very much for showing me this approach!

    It works exactly how I wanted to, and the angle / axis thing allows me to make something much more flexible!

    I only kept my old interpolation approach (even if I know it's not right) because I have a custom formula that let me control the exact duration and "smoothness" of my Lerp.

    Thank you again for this quick and detailed response, I couldn't expect better!
     
  4. pinnakam

    pinnakam

    Joined:
    Dec 12, 2017
    Posts:
    3
    can you people help me on this
    1. Rotate 1 degree of the first Axis
    2. From that position, do a full 360 rotation on the other Axis (so you will rotate back to the same position at 1 Degree out)
    3. Rotate another Degree on the first Axis
    4. From that 2nd degree, rotate the other access 360 degrees again, etc...
    Repeat until you've rotated 360 degrees on every single degree rotation of the first axis
    SO that's the thing i need.
     
  5. vladibo

    vladibo

    Joined:
    Jan 29, 2013
    Posts:
    38
    Code (CSharp):
    1. var rot = Quaternion.AngleAxis(180, v3.up);
    2.  
    3. t.rotation =
    4.           Quaternion.Slerp(Quaternion.identity, rot , x) *
    5.           Quaternion.Slerp(Quaternion.identity, rot , x);
    6.  
    x goes from 0 to 1