Slerp Not setting Vector3 to final value.

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.

  float timer = 0.0f;
  Vector3 startingRot = earthAnchor.transform.localRotation.eulerAngles;
  Vector3 goalRot = startingRot + new Vector3(0.0f, -45.0f, 0.0f);

  while (earthAnchor.transform.localEulerAngles != goalRot) {
      timer += Time.deltaTime;
      earthAnchor.transform.localEulerAngles = Vector3.Slerp(startingRot,goalRot,Mathf.Clamp01(timer));
  yield return null;
  }

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.

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:

float timer = 0.0f;
Quaternion startingRot = earthAnchor.transform.rotation;
Quaternion goalRot = startingRot * Quaternion.Euler(0f, -45f, 0f);

Quaternion currentRot = startingRot;

while(currentRot != goalRot)
{
    timer += Time.deltaTime;
    currentRot = Quaternion.Slerp(startingRot, goalRot, timer);

    earthAnchor.transform.rotation = currentRot;

    yield return null;
}

See if that works as you’re not longer comparing the actual transform’s value which is subject to wrapping.

1 Like

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”

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.

1 Like

Thanks again GroZZ

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

if (transform.localPosition.x == 12)
{
dostuff();
}

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.

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. :slight_smile:

1 Like

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

It’s all good, I only put it in bold so that others might see it later- it comes up pretty frequently.

i do the following:

  public static bool QuaternionsAreClose(Quaternion lhs, Quaternion rhs, float _threshold = 0.05f)
   {
     return (lhs == rhs) || (VectorsAreClose (lhs.eulerAngles, rhs.eulerAngles, _threshold));
   }

   public static bool VectorsAreClose(Vector3 lhs, Vector3 rhs, float _threshold = 0.05f)
   {
     if (lhs == rhs)
     {
       return true;
     }


     Vector3 diff = lhs - rhs;
     float mag = diff.magnitude;
     return (floatsAreClose(mag, 0.0f, _threshold));
   }

   /*
    * Determing if floats are the same
    * They are considered the same if they differ by less than a certain percentage
    * This is to allow a Mathf.lerp function to stop if they are close enough
    */
   public static bool floatsAreClose(float f1, float f2, float _threshold = 0.05f)
   {
     return f1 == f2 || (Mathf.Abs (f2 - f1) <= Mathf.Abs (_threshold));
   }

first it checks if the quanterions are the same, then checks if vectors are the same, then finally checks if the floats are close