Rotate GameObject over time - c#

I a new here and i try to start working with Unity Engine.
Could somebody explain me, how works Quaternion.Slerp? Because I want to rotate some object in different angles 90, 180 and 270. My code you can see below. Unfortunately when I add 180 degrees, object make crazy things and than put rotation to (0, 180, 180) for this game object. I would like to get (180,0,0)
public float speed = 0.1F;
private float rotation_x;
void Update()
{
if (Input.GetButtonDown("Fire1"))
{
rotation_x = transform.rotation.eulerAngles.x;
rotation_x += 180;
}
transform.rotation = Quaternion.Slerp(transform.rotation, Quaternion.Euler(rotation_x, transform.eulerAngles.y, transform.eulerAngles.z), Time.time * speed);
}

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.

Before anything, you can't add 180 on euler angles like that, and that's mainly what is causing your problem. You'd better use quaternion directly instead, or work on the transform itself.
You can think of a quaternion as an orientation in space. In contrary to what have been said, I do recommend learning how to use them if you can. However, I don't recommend using euler angles at all... as they're suject to different writing conventions, and will fail sometimes. You can look at 'gimbal lock' if you want details about that.
Simply a slerp or lerp (standing for spherical linear interpolation, or linear interpolation respectively) is a way to interpolate (go from one orientation to another, by increasing t from 0 to 1, in a coroutine or anywhere else) between orientation A and B. The difference between the two is that the slerp is giving you the shortest path from A to B.
In the end, when t = 1, lerp(A,B,t) and slerp(A,B,t) will give you B.
In your case, if you want to instantly rotate an object in space to a specific orientation, I suggest you use Quaternion.AngleAxis which is the most forward way to describe mathematically a quaternion.
If you want to add a rotation, say 90° to you actual orientation (without animation between the two), you can do something like this :
transform.rotation *= Quaternion.AngleAxis(axis_of_rotation, angle)
or use transform.rotate (depending on the parameters, it can be a right multiply, or left : local, or world transform).
Programmers' answer is detailling how to animate your transform. But I do suggest you to investigate quaternion themselves, as it will give you global understanding of space transforms.

Related

Not able to Clamp my camera rotation value

I made a camera that rotates around an object... Everything is working fine. But I was not able to clamp or restrict the camera rotation. Here's the code..
//First - Get the Initial Position
if (Input.GetMouseButtonDown(0))
{
mPreviousPosition = mCamRef.ScreenToViewportPoint(Input.mousePosition);
}
//Second - the difference amount and change in x
if (Input.GetMouseButton(0))
{
Vector3 newPosition = mCamRef.ScreenToViewportPoint(Input.mousePosition);
Vector3 direction = mPreviousPosition - newPosition;
float rotationAroundYAxis = -direction.x * 180;
mCamRef.transform.position = mTargetToRotateAround.position;
rotationAroundYAxis = Mathf.Clamp(rotationAroundYAxis, -60f,60f);
mCamRef.transform.rotation = Quaternion.Euler(Vector3.up * rotationAroundYAxis);
mCamRef.transform.Translate(new Vector3(mDistanceToTarget.x, mDistanceToTarget.y, -mDistanceToTarget.z));
mPreviousPosition = newPosition;
}
You are clamping
rotationAroundYAxis = Mathf.Clamp(rotationAroundYAxis, -0.6f, 0.6f);
but then use it for Transform.Rotate which rotates from the current rotation about the given amount => you always rotate something.
You would probably rather use e.g. Quaternion.Euler
mCamRef.transform.rotation = Quaternion.Euler(Vector3.up * rotationAroundYAxis);
However, note that clamping a rotation using +- 0.6° makes barely any sense ...
From you comments you want to clamp to +-60° so rather use
rotationAroundYAxis = Mathf.Clamp(rotationAroundYAxis, -60, 60);
Though I still don't understand how you want to get a rotation in angles from a direction vector ...

Rotate Object Smooth 90 degrees

God morning! I want to rotate an object smooth 90 degrees in an if statement. I've looked and searched for the solution everywhere but i couldn't find any fitting solution for my needs. This is what i want it to look like:
if (Swipe.Left)
{
Object smooth rotate 90 degrees down //left
}
I hope someone knows how i could do this! I'm thankful for any help :)
EDIT : I've tried this before but it seems like it does not work with if statements:
Quaternion newRotation = Quaternion.AngleAxis(90, Vector3.down);
transform.rotation = Quaternion.Slerp(transform.rotation, newRotation, .03f);
Since your Swipe.Left returns true during only one frame you have to sustain the logic for a longer period. To do that let's enable a flag on swipe and disable the flag when the target rotation is reached.
if (Swipe.Left)
{
swiped = true;
newRotation = Quaternion.AngleAxis(90, Vector3.down);
slerpEase = .03f;
}
if (swiped)
{
transform.rotation = Quaternion.Slerp(transform.rotation, newRotation, slerpEase);
slerpEase *= 0.95f;
if (Mathf.Approximately(1f, Quaternion.Dot(transform.rotation, newRotation)))
swiped = false; // reached the target rotation
}
Also notice that I used a variable named slerpEase to smoothly slow down the rotation as the object rotates over time. You may want to change 0.95f to make it dependent on delta time.
Note that, if you rotate it around two axis, comparing angles will be a bit tricky due to Gimbal lock.
I would use a coroutine for this, with an AnimationCurve to determine the smoothness of the turn. This will let you fine tune the appearance you want just using the inspector, and it allows for nice code re-use if you want different degrees.
[SerializeField] private AnimationCurve swipeRotateAnimationCurve; // configure in inspector
private Coroutine swipeRotateCoroutine = null;
private float swipeRotationDuration = 2f; // duration of rotation in seconds
// ...
if (Swipe.Left)
{
// Cancel any currently running coroutine
if (swipeRotateCoroutine != null)
{
StopCoroutine(swipeRotateCoroutine);
swipeRotateCoroutine = null;
}
swipeRotateCoroutine = StartCoroutine(DoHorizontalSwipeRotate(-90f));
}
else if (Swipe.Right)
{
// Cancel any currently running coroutine
if (swipeRotateCoroutine != null)
{
StopCoroutine(swipeRotateCoroutine);
swipeRotateCoroutine = null;
}
swipeRotateCoroutine = StartCoroutine(DoHorizontalSwipeRotate(90f));
}
// ...
private IEnumerator DoHorizontalSwipeRotate(float degreesRight)
{
float t = 0;
Quaternion startRot = transform.rotation;
// update rotation until
while (t < 1f)
{
// let next frame occur
yield return null;
// update timer
t = Mathf.Min(1f, t + Time.deltaTime/swipeRotationDuration);
// Find how much rotation corresponds to time at t:
float degrees = degreesRight * swipeRotateAnimationCurve.Evaluate(t);
// Apply that amount of rotation to the starting rotation:
transform.rotation = startRot * Quaternion.Euler(0f, degrees, 0f);
}
// allow for next swipe
swipeRotateCoroutine = null;
}
So When it comes to directions in Unity it is not always what we think it should be I've already answered a question related to this here. So in your case to rotate an object clock-wise, you need to use Vector3.forward instead of Vector3.down. This covers defining an axis around witch your object is going to rotate.
Now let's talk about the direction like rotate to right/rotate to left or clockwise/counter Clockwise. When you want to rotate your object clockwise you use -ve(imaginative) angle and when you want to rotate your object counter clockwise you use +ve(positive) angle.
So your code should be as follows to rotate your object clockwise:
Quaternion newRotation = Quaternion.AngleAxis(90, Vector3.forward);
transform.rotation = Quaternion.Slerp(transform.rotation, newRotation, 0.03f);

Unity - Rotation Animation through code? [duplicate]

I a new here and i try to start working with Unity Engine.
Could somebody explain me, how works Quaternion.Slerp? Because I want to rotate some object in different angles 90, 180 and 270. My code you can see below. Unfortunately when I add 180 degrees, object make crazy things and than put rotation to (0, 180, 180) for this game object. I would like to get (180,0,0)
public float speed = 0.1F;
private float rotation_x;
void Update()
{
if (Input.GetButtonDown("Fire1"))
{
rotation_x = transform.rotation.eulerAngles.x;
rotation_x += 180;
}
transform.rotation = Quaternion.Slerp(transform.rotation, Quaternion.Euler(rotation_x, transform.eulerAngles.y, transform.eulerAngles.z), Time.time * speed);
}
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.
Before anything, you can't add 180 on euler angles like that, and that's mainly what is causing your problem. You'd better use quaternion directly instead, or work on the transform itself.
You can think of a quaternion as an orientation in space. In contrary to what have been said, I do recommend learning how to use them if you can. However, I don't recommend using euler angles at all... as they're suject to different writing conventions, and will fail sometimes. You can look at 'gimbal lock' if you want details about that.
Simply a slerp or lerp (standing for spherical linear interpolation, or linear interpolation respectively) is a way to interpolate (go from one orientation to another, by increasing t from 0 to 1, in a coroutine or anywhere else) between orientation A and B. The difference between the two is that the slerp is giving you the shortest path from A to B.
In the end, when t = 1, lerp(A,B,t) and slerp(A,B,t) will give you B.
In your case, if you want to instantly rotate an object in space to a specific orientation, I suggest you use Quaternion.AngleAxis which is the most forward way to describe mathematically a quaternion.
If you want to add a rotation, say 90° to you actual orientation (without animation between the two), you can do something like this :
transform.rotation *= Quaternion.AngleAxis(axis_of_rotation, angle)
or use transform.rotate (depending on the parameters, it can be a right multiply, or left : local, or world transform).
Programmers' answer is detailling how to animate your transform. But I do suggest you to investigate quaternion themselves, as it will give you global understanding of space transforms.

Why doesn't my IEnumerator function loop? [duplicate]

I a new here and i try to start working with Unity Engine.
Could somebody explain me, how works Quaternion.Slerp? Because I want to rotate some object in different angles 90, 180 and 270. My code you can see below. Unfortunately when I add 180 degrees, object make crazy things and than put rotation to (0, 180, 180) for this game object. I would like to get (180,0,0)
public float speed = 0.1F;
private float rotation_x;
void Update()
{
if (Input.GetButtonDown("Fire1"))
{
rotation_x = transform.rotation.eulerAngles.x;
rotation_x += 180;
}
transform.rotation = Quaternion.Slerp(transform.rotation, Quaternion.Euler(rotation_x, transform.eulerAngles.y, transform.eulerAngles.z), Time.time * speed);
}
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.
Before anything, you can't add 180 on euler angles like that, and that's mainly what is causing your problem. You'd better use quaternion directly instead, or work on the transform itself.
You can think of a quaternion as an orientation in space. In contrary to what have been said, I do recommend learning how to use them if you can. However, I don't recommend using euler angles at all... as they're suject to different writing conventions, and will fail sometimes. You can look at 'gimbal lock' if you want details about that.
Simply a slerp or lerp (standing for spherical linear interpolation, or linear interpolation respectively) is a way to interpolate (go from one orientation to another, by increasing t from 0 to 1, in a coroutine or anywhere else) between orientation A and B. The difference between the two is that the slerp is giving you the shortest path from A to B.
In the end, when t = 1, lerp(A,B,t) and slerp(A,B,t) will give you B.
In your case, if you want to instantly rotate an object in space to a specific orientation, I suggest you use Quaternion.AngleAxis which is the most forward way to describe mathematically a quaternion.
If you want to add a rotation, say 90° to you actual orientation (without animation between the two), you can do something like this :
transform.rotation *= Quaternion.AngleAxis(axis_of_rotation, angle)
or use transform.rotate (depending on the parameters, it can be a right multiply, or left : local, or world transform).
Programmers' answer is detailling how to animate your transform. But I do suggest you to investigate quaternion themselves, as it will give you global understanding of space transforms.

How to calculate angle for transform.RotateAround so the end result has a y==0

So I need to solve the following puzzle:
There is a 3D cube with rotation (34,90,23) (or some other random values). I want to rotate around the local y-axis until transform.position.y == 0. In the editor I can just drag-rotate on the y axis until I see 0 as a value in the transform, but how do I do that with code? (You cannot just set y=0, because all the values change when you rotate around the local y-axis)
I am thinking about using transform.RotateAround(transform.position, transform.up, angle), but I don't know how to correctly calculate angle so that after RotateAround() the transform.rotation.y == 0.
And just to specify, I don't want to code the dragging itself, just the result. The rotation should be instant.
Can anyone write a working code for this?
I guess what you are looking for is an "animation" that will affect the transform.localEulerAngles.y value. To achieve this I'd recommend using a coroutine (you can find Unity tutorial here).
It would look like this:
private IEnumerator RotateAroundY(float rotationTime)
{
float timer = 0.0f;
Vector3 startLocalEulerAngles = transform.localEulerAngles;
Vector3 deltaLocalEulerAngles = new Vector3(0.0f, Mathf.DeltaAngle(startLocalEulerAngles.y, 0.0f), 0.0f);
while (timer < rotationTime)
{
timer += Time.deltaTime;
transform.localEulerAngles = startLocalEulerAngles + deltaLocalEulerAngles * (timer / rotationTime);
yield return new WaitForEndOfFrame();
}
transform.localEulerAngles = startLocalEulerAngles + deltaLocalEulerAngles;
}
And you can call it using StartCoroutine(RotateAroundY(4.0f));.
If you're getting into Unity I highly recommend you to familiarize with coroutines :)
EDIT :
Sorry about not noticing you didn't needed an animation: if you simply want to rotate around the local transform.up vector, you can change the transform.localEulerAngles.y value.
You can do it this way transform.localEulerAngles.y = new Vector3(transform.localEulerAngles.x, 0.0f, transform.localEulerAngles.z);
Hope this helps,

Categories