- Home /
The question is answered, right answer was accepted
transform.localRotation lerp precision
Hello,
I've got a method which is responsible for lerping rotation using steps, each step is 5% of rotation distance:
IEnumerator Test()
{
int steps = 0;
for (int i = 0; i < 20; i++)
{
float stepValue = currentPosition.localRotation.y - leftTileTarget.localRotation.y * .05f;
//print("1. stepValue: " + stepValue);
//print("2. local rotation value: " + transform.localRotation.y);
float newRotationValue = Mathf.Abs(transform.localRotation.y) + stepValue;
//print("3. new rotation value: " + newRotationValue);
float step = newRotationValue / Mathf.Abs(leftTileTarget.localRotation.y);
print("4. step " + step);
// TODO how to avoid changing decimal points value
transform.localRotation = Quaternion.Lerp(currentPosition.localRotation, leftTileTarget.localRotation, step);
//print("5. local rotation y after lerp: " + transform.localRotation.y);
//print("6. lerp value: " + Quaternion.Lerp(currentPosition.localRotation, leftTileTarget.localRotation, step));
steps++;
if (transform.localRotation == leftTileTarget.localRotation)
{
print(steps);
break;
}
yield return null;
}
}
I've got also similar function which lerp to target position also using 5% distance step:
IEnumerator Test2()
{
int steps = 0;
for (int i = 0; i < 20; i++)
{
float positionDistance = Mathf.Abs(currentPosition.localPosition.z - leftTileTarget.localPosition.z);
float positionStepPercentageValue = positionDistance * .05f; // move step float value necessary for calculating step percentage value
float newPositionValue = Mathf.Abs(transform.localPosition.z) + positionStepPercentageValue; // new z value after incementation by moveValue
float positionStep = newPositionValue / positionDistance; // step calculated using new position and percentage value of step distance provided by CheckRectPosition()
transform.localPosition = new Vector3(0, 0, Mathf.Lerp(currentPosition.localPosition.z, leftTileTarget.localPosition.z, positionStep));
steps++;
if (transform.localPosition == leftTileTarget.localPosition)
{
print(steps);
break;
}
yield return null;
}
print(steps);
}
My problem is rotation lerp - When I'm lerping to target rotation after first lerp I'm getting issue with decimal number: Lerp step value is: 0,03043807 provided as a interpolation step. But its lerping to 0,03074131 (minus is intended and it's fine) But the 0,0003 difference is generating huge miscalculation which results in finishing in 16 steps instead of 20...
And ideas what I'm doing wrong and how can I solve this?
When precision is something you're after - do not use Quaternion.Lerp
(linear/vector interpolation) but Quaternion.Slerp
(spherical/rotation interpolation).
Your first line alone contains a math error that turns everything wrong:
float stepValue = currentPosition.localRotation.y - leftTileTarget.localRotation.y * .05f;
as 20-10*0.5
is 15 not 5.
I found that math error and I used $$anonymous$$ath.Abs() - same as in Test2(). Thanks for suggesting Slerp I will check your solution when I get back to home. :)
Answer by andrew-lukasik · Nov 14, 2020 at 11:55 PM
This is a proper way to interpolate rotations:
using UnityEngine;
public class InterpolateRotationInNSteps : MonoBehaviour
{
[SerializeField] Transform _target;
[SerializeField][Min(1)] int _numSteps = 20;
#if UNITY_EDITOR
void OnDrawGizmos ()
{
if( _target==null ) return;
Vector3 pos = transform.position;
Quaternion rot_initial = transform.rotation;
Quaternion rot_target = _target.rotation;
Gizmos.color = Color.black; Gizmos.DrawLine( pos , pos + rot_initial*Vector3.right );
Gizmos.color = Color.white; Gizmos.DrawLine( pos , pos + rot_target*Vector3.right );
for( int step=1 ; step<=_numSteps ; step++ )
{
float progress = (float)step / (float)_numSteps;
Quaternion rot_now = Quaternion.Slerp( rot_initial , rot_target , progress );
Gizmos.color = Color.Lerp( Color.blue , Color.green , progress );
Gizmos.DrawLine( pos , pos + rot_now*Vector3.right );
}
}
#endif
}