Search Unity

Slerp Not setting Vector3 to final value.

Discussion in 'Scripting' started by mlnDb10wN, Jul 29, 2015.

  1. mlnDb10wN

    mlnDb10wN

    Joined:
    Sep 29, 2011
    Posts:
    8
    Hello All,

    I have been bashing my head on an issue that I am having with Mathf.Slerp(). I looked and couldn't find an answer and I am hoping the community can help. Basically I am trying to rotate an object until it gets to a certain rotation. I am using the clamp function to make sure I get to 1 and not over 1 due to change in deltaTime. My last frame may not rotate the object at the same speed as other frames because of this clamp.




    Code (CSharp):
    1.  
    2.   float timer = 0.0f;
    3.   Vector3 startingRot = earthAnchor.transform.localRotation.eulerAngles;
    4.   Vector3 goalRot = startingRot + new Vector3(0.0f, -45.0f, 0.0f);
    5.  
    6.   while (earthAnchor.transform.localEulerAngles != goalRot) {
    7.       timer += Time.deltaTime;
    8.       earthAnchor.transform.localEulerAngles = Vector3.Slerp(startingRot,goalRot,Mathf.Clamp01(timer));
    9.   yield return null;
    10.   }
    The problem is that sometimes I get 1 for Mathf.Clamp01(timer) but it never completes the Slerp and (earthAnchor.transform.localEulerAngles != goalPos) never evaluates as true because it is stuck and off by some amount like 3.5x10^-5 and stays at that amount. What am I doing wrong? I thought that it might be some rounding but I wasn't sure where this was happening.


    Thank you ahead of time,

    mlnDb10wN

    EDIT: Note that this is in a Coroutine.
     
  2. GroZZleR

    GroZZleR

    Joined:
    Feb 1, 2015
    Posts:
    3,201
    No reason for the Clamp at all, all of the (S)Lerp functions clamp t internally, but that still doesn't really explain the issue.

    Any particular reason you're not rotating with Quaternions? What might be happening is Unity is wrapping your angles such that your transform's angle will never actually equal your target angle despite visually being the same.

    I'd try something like this:

    Code (csharp):
    1.  
    2. float timer = 0.0f;
    3. Quaternion startingRot = earthAnchor.transform.rotation;
    4. Quaternion goalRot = startingRot * Quaternion.Euler(0f, -45f, 0f);
    5.  
    6. Quaternion currentRot = startingRot;
    7.  
    8. while(currentRot != goalRot)
    9. {
    10.     timer += Time.deltaTime;
    11.     currentRot = Quaternion.Slerp(startingRot, goalRot, timer);
    12.  
    13.     earthAnchor.transform.rotation = currentRot;
    14.  
    15.     yield return null;
    16. }
    17.  
    See if that works as you're not longer comparing the actual transform's value which is subject to wrapping.
     
    mlnDb10wN likes this.
  3. mlnDb10wN

    mlnDb10wN

    Joined:
    Sep 29, 2011
    Posts:
    8
    GroZZleR. This solution seemed to work. I have not seen an issue yet. Can you explain wrapping or point me to somewhere that explains this functionality? I looked around and didn't see much on the internet nor forums. Is it the same as rounding when converting IE: float to int? Or is this some different function? Thank you much. "Liked"
     
  4. GroZZleR

    GroZZleR

    Joined:
    Feb 1, 2015
    Posts:
    3,201
    Glad it's working.

    I can't seem to find any sort of documentation but I've come up with a practical example:

    Open a blank scene and create a 3D cube GameObject. Set the position to 0,0,0 and the rotation to 0,360,720. You'll notice the Cube is oriented exactly as if it were 0,0,0.

    When you press Play, Unity will change those angles by wrapping their values. I believe it only does this with the actual Transform values and not explicitly set Quaternion rotations.

    I'm positive that's what was happening here.
     
    mlnDb10wN likes this.
  5. mlnDb10wN

    mlnDb10wN

    Joined:
    Sep 29, 2011
    Posts:
    8
    Thanks again GroZZ
     
  6. Rokkimies

    Rokkimies

    Joined:
    May 18, 2015
    Posts:
    12
    Might not be related, but when I was scripting some sword movements (I can't animate) I noticed that Lerp and Slerp doesn't seem to go exactly at the point you set. At least in my case they went for point.xxxxxx coordinate, which messes stuff up if you have loops like
    Code (CSharp):
    1. if (transform.localPosition.x == 12)
    2. {
    3. dostuff();
    4. }
    When it's easily fixed with setting up the if-value for 12 to12.whatever it's still rather annoying and can be hard to spot unless you're fanatic Debug.Log of print() user.
     
  7. DonLoquacious

    DonLoquacious

    Joined:
    Feb 24, 2013
    Posts:
    1,667
    Or you could just never use equality comparisons with float values. They get shifted by like .000001 all the time, even if you assign them a whole number directly. They're called floats because they're floaty, so don't expect them to ever be whole numbers and you'll never be disappointed. ^_^
     
    Rokkimies likes this.
  8. Rokkimies

    Rokkimies

    Joined:
    May 18, 2015
    Posts:
    12
    Indeed, thanks for advice. Even if it seems pretty obvious now I had never thought about it really. In my defense: I've been programming for a month or so. Found StackOverflow and Unity forums so helpful that I decided for a once to try and contribute and got contributed myself =D
     
  9. DonLoquacious

    DonLoquacious

    Joined:
    Feb 24, 2013
    Posts:
    1,667
    It's all good, I only put it in bold so that others might see it later- it comes up pretty frequently.
     
  10. ArachnidAnimal

    ArachnidAnimal

    Joined:
    Mar 3, 2015
    Posts:
    1,820
    i do the following:

    Code (csharp):
    1.  
    2.   public static bool QuaternionsAreClose(Quaternion lhs, Quaternion rhs, float _threshold = 0.05f)
    3.    {
    4.      return (lhs == rhs) || (VectorsAreClose (lhs.eulerAngles, rhs.eulerAngles, _threshold));
    5.    }
    6.  
    7.    public static bool VectorsAreClose(Vector3 lhs, Vector3 rhs, float _threshold = 0.05f)
    8.    {
    9.      if (lhs == rhs)
    10.      {
    11.        return true;
    12.      }
    13.  
    14.  
    15.      Vector3 diff = lhs - rhs;
    16.      float mag = diff.magnitude;
    17.      return (floatsAreClose(mag, 0.0f, _threshold));
    18.    }
    19.  
    20.    /*
    21.     * Determing if floats are the same
    22.     * They are considered the same if they differ by less than a certain percentage
    23.     * This is to allow a Mathf.lerp function to stop if they are close enough
    24.     */
    25.    public static bool floatsAreClose(float f1, float f2, float _threshold = 0.05f)
    26.    {
    27.      return f1 == f2 || (Mathf.Abs (f2 - f1) <= Mathf.Abs (_threshold));
    28.    }
    29.  
    30.  
    31.  
    first it checks if the quanterions are the same, then checks if vectors are the same, then finally checks if the floats are close