0
\$\begingroup\$

How can I rotate a Transform over a specified time in a single line of code?

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.

\$\endgroup\$
  • 1
    \$\begingroup\$ I think the question is still good to have on the site, but it can remain closed as a duplicate. Alternatively, you can post your answer to the duplicate and I can up vote it there. Also, you're welcome! \$\endgroup\$ – MichaelHouse Mar 20 '15 at 1:45
1
\$\begingroup\$

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;
    }
}
| improve this answer | |
\$\endgroup\$
  • \$\begingroup\$ Not sure, but aren't quaternions used to do exactly that? \$\endgroup\$ – Vaillancourt Mar 19 '15 at 22:42
  • 1
    \$\begingroup\$ @AlexandreVaillancourt Rotating over a specified amount of time is very challenging in Unity in my experience, unless there is something new with Unity 5. This script makes it a one liner. \$\endgroup\$ – Evorlor Mar 19 '15 at 22:44

Not the answer you're looking for? Browse other questions tagged or ask your own question.