Rotate Gameobject Over Time

Rotate GameObject over time

Most examples out there including Unity examples from their official website are using Lerp in the wrong way. They didn't even bother to describe how it works in the API documentation. They just starch it in the Update() function and call it a day.

Mathf.Lerp, Vector3.Lerp, and Quaternion.Slerp work by changing from one position/rotation to another with the t value(last parameter) being passed in.That t value is also know as time.

The min of the t value is 0f and the max is 1f.

I will explain this with Mathf.Lerp to make it easier to understand. The Lerp functions are all the-same for both Mathf.Lerp, Vector and Quaternion.

Remember that Lerp takes two values and returns values between them. If we have a value of 1 and 10 and we do Lerp on them:

 float x = Mathf.Lerp(1f, 10f, 0f); will return 1.
float x = Mathf.Lerp(1f, 10f, 0.5f); will return 5.5
float x = Mathf.Lerp(1f, 10f, 1f); will return 10

As you can see, the t(0) returns the min of the number passed in, t(1) returns the max value passed in and t(0.5) will return mid point between the min and the max value. You are doing it wrong when you pass any t value that is < 0 or > 1. That code in you Update() function is doing just that. Time.time will increase every second and will be > 1 in a second, so you have problems with that.

It recommended to use Lerp in another function/Coroutine instead of the Updated function.

Note:

Using Lerp has a bad side of it when it comes to rotation. Lerp does not know how to rotate Object with the shortest path. So bear that in mind. For example, you have an Object with 0,0,90 position. Lets say you want to move the rotation from that to 0,0,120 Lerp can sometimes rotate left instead of right to reach that new position which means it take longer to reach that distance.

Let's say we want to make the rotation (0,0,90) from whatever the current rotation is. The code below will change the rotation to 0,0,90 in 3 seconds.

ROTATION OVER TIME:

void Start()
{
Quaternion rotation2 = Quaternion.Euler(new Vector3(0, 0, 90));
StartCoroutine(rotateObject(objectToRotate, rotation2, 3f));
}

bool rotating = false;
public GameObject objectToRotate;
IEnumerator rotateObject(GameObject gameObjectToMove, Quaternion newRot, float duration)
{
if (rotating)
{
yield break;
}
rotating = true;

Quaternion currentRot = gameObjectToMove.transform.rotation;

float counter = 0;
while (counter < duration)
{
counter += Time.deltaTime;
gameObjectToMove.transform.rotation = Quaternion.Lerp(currentRot, newRot, counter / duration);
yield return null;
}
rotating = false;
}

INCREMENTAL ANGULAR ROTATION OVER TIME:

And to just rotate the Object to 90 in z axis, the code below is a great example of that. Please understand there is a difference between moving Object to new rotational point and just rotating it.

void Start()
{
StartCoroutine(rotateObject(objectToRotate, new Vector3(0, 0, 90), 3f));
}

bool rotating = false;
public GameObject objectToRotate;

IEnumerator rotateObject(GameObject gameObjectToMove, Vector3 eulerAngles, float duration)
{
if (rotating)
{
yield break;
}
rotating = true;

Vector3 newRot = gameObjectToMove.transform.eulerAngles + eulerAngles;

Vector3 currentRot = gameObjectToMove.transform.eulerAngles;

float counter = 0;
while (counter < duration)
{
counter += Time.deltaTime;
gameObjectToMove.transform.eulerAngles = Vector3.Lerp(currentRot, newRot, counter / duration);
yield return null;
}
rotating = false;
}

All my examples are based on frame-rate of the device. You can use real-time by replacing Time.deltaTime with Time.delta but more calculation is required.

Rotate GameObject around pivot point over time

Modified the code from my other answer to get this effect. You can read that to understand how lerp work.

What changed and why:

1.Got the beginRotation point from the RotatePointAroundPivot function by passing Vector3.zero to the angle parameter and the current Object position outside of the while loop. This needs to be done once as the result will be used in the while loop.

Vector3 beginRotPoint = RotatePointAroundPivot(objPoint.transform.position, pivot, Vector3.zero);

This is done because lerp needs a starting point. That starting point should never changed otherwise, it break the lerp function.

2.Generate new angle each frame in the while loop. This is done by lerping from Vector3.zero to the target angle:

float t = counter / duration;
Vector3 tempAngle = Vector3.Lerp(Vector3.zero, angles, t);

3.Find the final pivot angle rotation. This is done by passing the starting point from #1 to the first parameter, the pivot point to the second parameter and finally the current angle generated from #2 to the third parameter.

Vector3 tempPivot = RotatePointAroundPivot(beginRotPoint, pivot, tempAngle);

4.Finally, assign the result from #3 to objPoint.transform.position instead of objPoint.transform.eulerAngles because you are not only moving the object. You are also rotating it.

objPoint.transform.position = tempPivot;

Complete code:

Your RotatePointAroundPivot function:

Vector3 RotatePointAroundPivot(Vector3 point, Vector3 pivot, Vector3 angles)
{
Vector3 dir = point - pivot; // get point direction relative to pivot
dir = Quaternion.Euler(angles) * dir; // rotate it
point = dir + pivot; // calculate rotated point

return point;
}

Modified rotateObject function that I described what changed above:

bool rotating = false;

IEnumerator rotateObject(GameObject objPoint, Vector3 pivot, Vector3 angles, float duration)
{
if (rotating)
{
yield break;
}
rotating = true;

Vector3 beginRotPoint = RotatePointAroundPivot(objPoint.transform.position, pivot, Vector3.zero);

float counter = 0;
while (counter < duration)
{
counter += Time.deltaTime;

float t = counter / duration;
Vector3 tempAngle = Vector3.Lerp(Vector3.zero, angles, t);

Vector3 tempPivot = RotatePointAroundPivot(beginRotPoint, pivot, tempAngle);
objPoint.transform.position = tempPivot;
Debug.Log("Running: " + t);
yield return null;
}
rotating = false;
}

USAGE:

Will rotate object 180-deg around pivot point in 5 seconds:

//The GameObject with the pivot point to move
public GameObject pointToRotate;
//The Pivot point to move the GameObject around
public Transform pivotPoint;

//The angle to Move the pivot point
public Vector3 angle = new Vector3(0f, 180f, 0f);

//The duration for the rotation to occur
public float rotDuration = 5f;

void Start()
{
StartCoroutine(rotateObject(pointToRotate, pivotPoint.position, angle, rotDuration));
}

Rotate GameObject back and forth

Just like moving GameObject back and forth, you can rotate GameObject back and forth with Mathf.PingPong. That's what it is used for. It will return value between 0 and 1. You can pass that value to Vector3.Lerp and generate the eulerAngle required to perform the rotation.

This can also be done with a coroutine but Mathf.PingPong should be used if you don't need to know when you have reached the destination. Coroutine should be used if you want to know when you reach the rotation destination.

public float speed = 0.36f;

Vector3 pointA;
Vector3 pointB;


void Start()
{
//Get current position then add 90 to its Y axis
pointA = transform.eulerAngles + new Vector3(0f, 90f, 0f);

//Get current position then substract -90 to its Y axis
pointB = transform.eulerAngles + new Vector3(0f, -90f, 0f);
}

void Update()
{
//PingPong between 0 and 1
float time = Mathf.PingPong(Time.time * speed, 1);
transform.eulerAngles = Vector3.Lerp(pointA, pointB, time);
}

EDIT:

With the coroutine method that you can use to determine the end of each rotation.

public GameObject objectToRotate;
public float speed = 0.36f;

Vector3 pointA;
Vector3 pointB;


void Start()
{
//Get current position then add 90 to its Y axis
pointA = transform.eulerAngles + new Vector3(0f, 90f, 0f);

//Get current position then substract -90 to its Y axis
pointB = transform.eulerAngles + new Vector3(0f, -90f, 0f);

objectToRotate = this.gameObject;
StartCoroutine(rotate());
}

IEnumerator rotate()
{
while (true)
{
//Rotate 90
yield return rotateObject(objectToRotate, pointA, 3f);
//Rotate -90
yield return rotateObject(objectToRotate, pointB, 3f);

//Wait?
//yield return new WaitForSeconds(3);
}
}

bool rotating = false;
IEnumerator rotateObject(GameObject gameObjectToMove, Vector3 eulerAngles, float duration)
{
if (rotating)
{
yield break;
}
rotating = true;

Vector3 newRot = gameObjectToMove.transform.eulerAngles + eulerAngles;

Vector3 currentRot = gameObjectToMove.transform.eulerAngles;

float counter = 0;
while (counter < duration)
{
counter += Time.deltaTime;
gameObjectToMove.transform.eulerAngles = Vector3.Lerp(currentRot, newRot, counter / duration);
yield return null;
}
rotating = false;
}

How do i rotate an object multiple times?

This will do what you want:

private IEnumerator RotatePlatform(float dir) {
Vector3 target = new Vector3(0, 0, (transform.eulerAngles.z + dir + 360) % 360);
while(Mathf.Abs(transform.eulerAngles.z - target.z) >= Mathf.Abs(dir * Time.deltaTime * 2)) {
transform.Rotate(new Vector3(0,0,dir) * Time.deltaTime);
yield return null;
}
transform.eulerAngles = target;
rotating = false;
}

The key things to take away from this is that I encapsulated the "make it animate" code into a coroutine so that the object rotates to the desired direction from any starting rotation to any rotation in either direction. The reason this works is that the coroutine's local variables hold their state between iterations (the Unity scheduled task system will halt executing the code on a yield instruction and resume at a later time: in this case, null means it will resume on the next frame). This way we can store the starting rotation and target rotation without having to do anything crazy.

It does this by getting the current rotation, adding the rotation amount, then making that value lie within 0-360: transform.eulerAngles.z + dir + 360) % 360. transform.eulerAngles.z will already be [0,360], subtracting 90 from that would leave [-90,270], then by adding 360 we insure that the value will always be greater than 0 (without affecting the resulting angle), then % 360 effectively subtracts off any excess quantities of 360.

The Mathf.Abs(transform.eulerAngles.z - target.z) >= Mathf.Abs(dir * Time.deltaTime * 2) statement checks to see if the "distance we have yet to rotate" is "greater than the amount we're going to rotate by" with some buffer: we don't want to overshoot and have our "how far do we need to go" be 358 degrees!

The other important change was that we want to use new Vector3(0,0,dir) rather than target as our value to rotate by so that the speed remains constant.

The last 2 instructions make sure the rotation actually achieves the desired value and, of course, toggle back the bool tracking whether or not pressing keys does anything (we only want 1 instance of our coroutine).

Then here's the Update() method I used to control it:

private void Update() {
if(!rotating) {
if(Input.GetKeyDown(KeyCode.D)) {
rotating = true;
StartCoroutine(RotatePlatform(90));
}
if(Input.GetKeyDown(KeyCode.A)) {
rotating = true;
StartCoroutine(RotatePlatform(-90));
}
}
}

I need to rotate a gameObject towards another gameobject in Unity with c#, but i want the rotation to be only in z

The simplest way for this is not going through Quaternion but Vector3 instead.

What many people don't know is: You can not only read but also assign a value to e.g. transform.right which will adjust the rotation in order to make the transform.right match with the given direction!

So what you can do is e.g.

public Transform target; 
public float rotSpeed;

void Update()
{
// erase any position difference in the Z axis
// direction will be a flat vector in the global XY plane without depth information
Vector2 targetDirection = target.position - transform.position;

// Now use Lerp as you did
transform.right = Vector3.Lerp(transform.right, targetDirection, rotationSpeed * Time.deltaTime);
}

If you need this in local space since e.g. your object has a default rotation on Y or X you can adjust this using

public Transform target; 
public float rotSpeed;

void Update()
{
// Take the local offset
// erase any position difference in the Z axis of that one
// direction will be a vector in the LOCAL XY plane
Vector2 localTargetDirection = transform.InverseTransformDirection(target.position);

// after erasing the local Z delta convert it back to a global vector
var targetDirection = transform.TransformDirection(localTargetDirection);

// Now use Lerp as you did
transform.right = Vector3.Lerp(transform.right, targetDirection, rotationSpeed * Time.deltaTime);
}

Rotate a GameObject slowly on click on it

You can use a Coroutine to slowly animate it.

float rotationAmount = .1f;
float delaySpeed = .1f;
private void OnMouseDown(){
StartCoroutine(SlowSpin());
}
IEnumerator SlowSpin(){
float count = 0;
while(count <= 90){
gameObject.transform.Rotate(new Vector3(0, 0, rotationAmount));
count += rotationAmount;
yield return new WaitForSeconds(delaySpeed);
}
}

Change the variables on lines 1 and 2 to suit your needs. However, don't set delaySpeed too high, otherwise your animation will become choppy.

How to rotate a 2D object by 90 degrees in unity

Create a coroutine that rotates based on deltaTime and keeps track of how far it has rotated, stopping once it hits 90. Use Mathf.Min to be sure not to rotate past 90.

private isRotating = false;

// speed & amount must be positive, change axis to control direction
IEnumerator DoRotation(float speed, float amount, Vector3 axis)
{
isRotating = true;
float rot = 0f;
while (rot < amount)
{
yield return null;
float delta = Mathf.Min(speed * Time.deltaTime, amount - rot);
transform.RotateAround(target.transform.position, axis, delta);
rot += delta;
}
isRotating = false;
}

Then when you want to start rotating, start the coroutine if it isnt already rotating:

if (!isRotating)
{
StartCoroutine(DoRotation(50f, 90f, Vector3.back));
}

It's slightly better form to save the Coroutine itself instead of using a flag. This lets you do stuff like stop the rotation at any point.

private Coroutine rotatingCoro;

// speed & amount must be positive, change axis to control direction
IEnumerator DoRotation(float speed, float amount, Vector3 axis)
{
float rot = 0f;
while (rot < amount)
{
yield return null;
float delta = Mathf.Min(speed * Time.deltaTime, amount - rot);
transform.RotateAround(target.transform.position, axis, delta);
rot += delta;
}

rotatingCoro = null;
}

// ...

if (rotatingCoro != null)
{
rotatingCoro = StartCoroutine(DoRotation(50f, 90f, Vector3.back));
}

// ...

// call to stop rotating immediately
void StopRotating()
{
if (rotatingCoro != null)
{
StopCoroutine(rotatingCoro);
rotatingCoro = null;
}
}

In unity script, how to rotate and rotate to around pivot position

The pivot point is the-same as the transform.position you're already using. If you don't like where the pivot point is or the location the rotation is rotating against, you have two options:

1. Create new empty GameObject. Move it to the position you want your GameObject to rotate around. Once satisfied by that location, make this new GameObject a child of the GameObject you will be rotating around. Use the position of this new GameObject as the pivot rotation point for the transform.RotateAround function.

Drag the empty GameObject to the customPivot slot in the script below. That should give you a new pivot point to rotate your GameObject around.

public Transform customPivot;

void Update()
{
transform.RotateAround(customPivot.position, Vector3.up, 20 * Time.deltaTime);
}

2. Open a 3D application and change the pivot point of your object, then save and re-import it into Unity.

Since this is just used to rotate object around, I suggest #1. If the problem is that the pivot point is not centered, I would suggest #2.



Related Topics



Leave a reply



Submit