Problem with Interpolation

Hi there!
I have an object that rotates to different axes.
I use the “Space” button to return it to base coordinates (0,0,0). via Linear Interpolation.
Sometimes it works well, sometimes very strange. What’s wrong with code?
I really appreciate any help.

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

/* Rotate object X, Y and Z axes by QWASDE */

public class TableControl : MonoBehaviour
{
    public float rotateSpeed = 25f;
    private float zInput; // rotate to player and from player
    private float xInput;  //
    private float yInput; // rotate to the left and right

    [Header("For Interpolation")]
    public Vector3 curRot;
    public Vector3 baseRot = new Vector3(0, 0, 0);
    public float timeDuration = 5;
    public bool checkToStart = false;

    [Header("Set Dynamically")]
    public Vector3 finRot;
    public bool movingToZero = false;
    public float timeStart;

    void Start()
    {

    }
    void Update()
    {
        zInput = Input.GetAxis("RotateZ");
        xInput = Input.GetAxis("RotateX");
        yInput = Input.GetAxis("RotateY");

        /* Rotate at any axes */
        transform.Rotate(Vector3.right, Time.deltaTime * rotateSpeed * zInput, Space.World);
        transform.Rotate(Vector3.down, Time.deltaTime * rotateSpeed * xInput, Space.World);
        transform.Rotate(Vector3.back, Time.deltaTime * rotateSpeed * yInput, Space.World);

        if (Input.GetKey(KeyCode.Space))
        {
            movingToZero = true;
            timeStart = Time.time;
            Debug.Log("time " + timeStart);
        }
        if (movingToZero)
        {
            float xAngle = transform.eulerAngles.x;
            float yAngle = transform.eulerAngles.y;
            float zAngle = transform.eulerAngles.z;
            curRot = new Vector3(xAngle, yAngle, zAngle);
            float u = (Time.time - timeStart) / timeDuration;
            if (u >= 1)
            {
                u = 1;
            }
            finRot = (1 - u) * curRot + u * baseRot;
            transform.rotation = Quaternion.Euler(finRot);
        }
        if (transform.rotation == Quaternion.Euler(baseRot))
        {
            movingToZero = false;
        }
    }
}

A major benefit of using Quaternions is to avoid issues like these which is potentially related to Gimbal Lock. You won’t bypass that issue by extracting and using Euler. Always use Quaternion directly and in the case of interpolation, the Quaternion type gives you Slerp.

2 Likes

I don’t know about Gimbal Lock, really helpful information, I changed my code to make it more simple, but unfortunately, it doesn’t return objects to start coordinates correctly. What’s wrong now? I need smooth rotation from the current Euler to the base.

public class QuaternionSlerpScript : MonoBehaviour
{
    Quaternion baseAngle = Quaternion.Euler(0, 0, 0);

    void Update()
    {
        if (Input.GetKey(KeyCode.Space))
        {
            Quaternion curAngle = transform.localRotation;
            transform.localRotation = Quaternion.Slerp(curAngle, baseAngle, 0.3f);
        }
    }
}

The third argument to Slerp (or Lerp) is a percent from 0.0f, to 1.0f, clamped.

Your code says “give me back something that is 30% between curAngle and baseAngle”

Because you say 30% it never changes.

To smoothly move that third term from 0.0 to 1.0, you would gradually increase it over time, a little bit each frame, to go from 0% to 100%, from curAngle to baseAngle

You can do this with an animation, or perhaps with any tweening approach or package. Personally I favor this approach for simple linear easing:

Smoothing movement between any two particular values:

You have currentQuantity and desiredQuantity.

  • only set desiredQuantity
  • the code always moves currentQuantity towards desiredQuantity
  • read currentQuantity for the smoothed value

Works for floats, Vectors, Colors, Quaternions, anything continuous or lerp-able.

The code: SmoothMovement.cs · GitHub

1 Like