How can I rotate a Transform over time?

How can I rotate a Transform over time?

I have had a lot of trouble with this in the past. I wrote a script which rotates a Transform by a specified rotation over n seconds, or rotates a Transform towards a Vector3 over n seconds, in a one liner. I hope it helps others in the future. See below.

Create a script called TransformExtentions. Paste the code into that script.

You can call Transform.RotateTowardsOverTime(Vector3, float) on your Transform where the Vector3 is the target you want to rotate towards and the float is the number of seconds you want it to take.

You can call Transform.RotateOverTime(Vector3, float) on your Transform where the Vector3 is the rotation you want to apply and the float is the number of seconds you want it to take. You can apply rotations greater than 360 degrees or less than -360 degrees.

Calling Transform.RotateTowardsOverTime(Vector3, float) or Transform.RotateOverTime(Vector3, float) will cancel out any active rotation calls on the Transform.

If you pass in 0 seconds, the Transform will rotate infinitely. If you pass in negative seconds, the Transform will rotate in reverse.

using UnityEngine;
using System.Collections;
using System;

public static class TransformExtensions
{
    /// <summary>
    /// Rotates the transform to a specified rotation over a set number of seconds.
    /// For an infinite rotation, multiply the degrees by a float to adjust the speed, and set the duration to 0 seconds.
    /// Calling RotateOverTime() or RotateTowardsOverTime() will cancel any pending rotations on this transform.
    /// </summary>
    public static void RotateTowardsOverTime(this Transform transform, Vector3 degrees, float seconds)
    {
        Vector3 rotationToBeMade = degrees - transform.rotation.eulerAngles;
        if (degrees.z > 270.0F && transform.rotation.eulerAngles.z < 90.0F)
        {
            rotationToBeMade.z = -(360.0F - degrees.z + transform.rotation.eulerAngles.z);
        }
        if (transform.rotation.eulerAngles.z > 270.0F && degrees.z < 90.0F)
        {
            rotationToBeMade.z = 360.0F - transform.rotation.eulerAngles.z + degrees.z;
        }
        RotateOverTime(transform, rotationToBeMade, seconds);
    }

    /// <summary>
    /// Rotates the transform by a specified number of degrees over a set number of seconds.
    /// For an infinite rotation, multiply the degrees by a float to adjust the speed, and set the duration to 0 seconds.
    /// Calling RotateOverTime() or RotateTowardsOverTime() will cancel any pending rotations on this transform.
    /// </summary>
    public static void RotateOverTime(this Transform transform, Vector3 degrees, float seconds)
    {
        RotateOverTime[] oldRotateOverTimeComponents = transform.gameObject.GetComponents<RotateOverTime>();
        foreach (RotateOverTime oldRotateOverTimeComponent in oldRotateOverTimeComponents)
        {
            GameObject.Destroy(oldRotateOverTimeComponent);
        }

        RotateOverTime rotateOverTimeComponent = transform.gameObject.AddComponent<RotateOverTime>();
        rotateOverTimeComponent.hideFlags = HideFlags.HideInInspector;
        rotateOverTimeComponent.Degrees = degrees;
        rotateOverTimeComponent.Seconds = seconds;
    }

class RotateOverTime : MonoBehaviour
{
    public Vector3 Degrees { get; set; }
    public float Seconds { get; set; }

    private Vector3 rotationCompleted = Vector3.zero;
    private Vector3 speed;
    private Vector3 startRotation;

    void Start()
    {
        speed = GetBalancedRotationSpeeds(Degrees, Seconds);
        startRotation = transform.eulerAngles;
    }

    void FixedUpdate()
    {
        UpdateRotation();
        if (IsRotationComplete())
        {
            Destroy(this);
        }
    }

    private Vector3 GetBalancedRotationSpeeds(Vector3 degrees, float seconds)
    {
        if (seconds == 0)
        {
            seconds = 1;
        }
        float degreesWeight = (Degrees.x + Degrees.y + Degrees.z) / 3;
        float speedModifier = degreesWeight / seconds;
        float totalChangeInDegrees = Math.Abs(degrees.x) + Math.Abs(degrees.y) + Math.Abs(degrees.z);
        float xWeight = Math.Abs(degrees.x) / totalChangeInDegrees;
        float yWeight = Math.Abs(degrees.y) / totalChangeInDegrees;
        float zWeight = Math.Abs(degrees.z) / totalChangeInDegrees;
        float xSpeed = xWeight * speedModifier * 3;
        float ySpeed = yWeight * speedModifier * 3;
        float zSpeed = zWeight * speedModifier * 3;
        return new Vector3(xSpeed, ySpeed, zSpeed);
    }

    private void UpdateRotation()
    {
        rotationCompleted += Time.deltaTime * speed;
        Vector3 rotation = Quaternion.Euler(rotationCompleted + startRotation).eulerAngles;
        bool rotationIsValid = !(float.IsNaN(rotationCompleted.x) || float.IsNaN(rotationCompleted.y) || float.IsNaN(rotationCompleted.z) && float.IsNaN(startRotation.x) || float.IsNaN(startRotation.y) || float.IsNaN(startRotation.z) || float.IsNaN(rotation.x) || float.IsNaN(rotation.y) || float.IsNaN(rotation.z));
        if (!rotationIsValid)
        {
            Destroy(this);
        }
        transform.eulerAngles = rotation;
    }

    private bool IsRotationComplete()
    {
        bool xRotationIsComplete = Math.Abs(rotationCompleted.x) >= Math.Abs(Degrees.x);
        bool yRotationIsComplete = Math.Abs(rotationCompleted.y) >= Math.Abs(Degrees.y);
        bool zRotationIsComplete = Math.Abs(rotationCompleted.z) >= Math.Abs(Degrees.z);
        return xRotationIsComplete && yRotationIsComplete && zRotationIsComplete && Seconds != 0;
    }
}