Search code examples
c#unity-game-enginerotationtween

How to rotate cube smoothly


i am trying to rotate cube smoothly to 90 degrees every time i press space key. here in my code every time i decrease speed to less than 1 its rotation is not consistent at 90 decrease and speed at anything more than 1 its rotating instantly not smoothly. Here is my code

Vector3 to = new Vector3(0, 0, 90);
public float speed = 0.5f;

void Update()
{
    if (Input.GetKeyDown(KeyCode.Space))
    {
        RotateOne();
    }
}

void RotateOne()
{
    transform.eulerAngles = Vector3.Lerp(transform.rotation.eulerAngles, to, speed * Time.deltaTime);
    
    to += new Vector3(0, 0, 90);
}

Solution

  • You almost had it ;)

    The main issue is that you only rotate once a tiny little bit when you click the key.

    You rather want to rotate continously and only increase the target rotation once when you click.

    A second issue is you using eulerAngles for a continuous rotation. From the API:

    When using the .eulerAngles property to set a rotation, it is important to understand that although you are providing X, Y, and Z rotation values to describe your rotation, those values are not stored in the rotation. Instead, the X, Y & Z values are converted to the Quaternion's internal format.

    When you read the .eulerAngles property, Unity converts the Quaternion's internal representation of the rotation to Euler angles. Because, there is more than one way to represent any given rotation using Euler angles, the values you read back out may be quite different from the values you assigned. This can cause confusion if you are trying to gradually increment the values to produce animation.

    To avoid these kinds of problems, the recommended way to work with rotations is to avoid relying on consistent results when reading .eulerAngles particularly when attempting to gradually increment a rotation to produce animation. For better ways to achieve this, see the Quaternion * operator.

    // In general instead of eulerAngles always prefer calculating with
    // Quaternion directly where possible
    private Quaternion to;
    
    void Start()
    {
        to = transform.rotation;
    }
    
    void Update()
    {
        if (Input.GetKeyDown(KeyCode.Space))
        {
            RotateOne();
        }
    
        // You want to do this always, not only in the one frame the key goes down
        // Rather use RotateTowards for a linear rotation speed (angle in degrees per second!)
        transform.rotation = Quaternion.RotateTowards(transform.rotation, to, speed * Time.deltaTime);
    
        // Or if you still rather want to interpolate
        //transform.rotation = Quaternion.Lerp(transform.rotation, to, speed * Time.deltaTime);
    }
    
    void RotateOne()
    {  
        to *= Quaternion.Euler(0, 0, 90);
    }
    

    NOTE though there will be one little issue with this: The moment you hit the key 3 or 4 times it will suddenly rotate back! This is because RotateTowards and Lerp use both the shortest way towards the target rotation.

    In order to fully avoid this in your case you could rather use a Corotuine and stack your inputs like e.g.

    private int pendingRotations;
    private bool isRotating;
    
    void Update()
    {
        if (Input.GetKeyDown(KeyCode.Space))
        {
            pendingRotations++;
            if(!isRotating) StartCoroutine(RotateRoutine());
        }
    }
    
    IEnumerator RotateRoutine()
    {
        // just in case
        if(isRotating) yield break;
        isRotating = true;
        
        var targetRotation = transform.rotation * Quaternion.Euler(0, 0, 90);
    
        while (transform.rotation != targetRotation)
        {
            transform.rotation = Quaternion.RotateTowards(startRotation, targetRotation, speed * Time.deltaTime);
    
            // tells Unity to "pause" the routine here, render this frame
            // and continue from here in the next fame
            yield return null;
        }
    
        // in order to end up with a clean value
        transform.rotation = targetRotation;
    
        isRotating = false;
        pendingRotations--;
    
        // are there more rotations pending?
        if (pendingRotations > 0)
        {
            // start another routine
            StartCoroutine(RotateRoutine());
        }
    }