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.
Answer
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
{
///
/// 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.
///
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);
}
///
/// 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.
///
public static void RotateOverTime(this Transform transform, Vector3 degrees, float seconds)
{
RotateOverTime[] oldRotateOverTimeComponents = transform.gameObject.GetComponents();
foreach (RotateOverTime oldRotateOverTimeComponent in oldRotateOverTimeComponents)
{
GameObject.Destroy(oldRotateOverTimeComponent);
}
RotateOverTime rotateOverTimeComponent = transform.gameObject.AddComponent();
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;
}
}
No comments:
Post a Comment