First error: Slerp misuse
First, you should not have read the function documentation carefully Quaternion.Slerp and so did not understand correctly how the third parameter works. This function makes a interpolation between two values a
and b
(the first two parameters) according to the progress t
(the third parameter). This progress needs to be necessarily a value between 0 and 1, so much so that in the documentation itself it is written that it is truncated (clamped) for this range if different from it (bold is mine):
Spherically interpolates between a and b by t. The Parameter t is clamped to the range [0, 1].
In free translation:
Spherically interpolate between a and b by a rate t. Parameter t is truncated/maintained in the range [0, 1].
In your code, the value a
is the rotation current of the object (note the bold - I’ll come back to that in the next section of this answer), the value b
is the desired 120 degree rotation on the Z axis, and the value t
is a low value (closer to 0, maybe something like 0.4), because you multiplied 2.0 by the duration of the last frame of the game when your code started.
Illustrating: an interpolation between a=1
and b=10
, for example, it would result in 1 if you used t=0
and would result in 10 if you used t=1
. And it would result in intermediate values (2, 2.5, 4, 7.358, 9, 9.99, 9.999, ...) interpolated if you used any value of any value of t
between 0 and 1. Works the same way for rotations. If you use t=0
it will return the value of inicial.rotation
, and if you use t=1
it will return the value of Quaternion.Euler(0, 0, angle)
, and for any t
between 0 and 1 it will return an intermediate value between these two rotations.
In your code you used a value of t
more or less like 0.4, which was calculated once there at the beginning of the game. Soon its rotation will always be the interpolated value between the two rotations, there a 40% from the first value (of course, considering that it is a spherical and non-linear interpolation). You change that same value to each frame, but the t
is always the same, so your object should rotate once (jumping immediately to that rotation) and not rotate anymore. He still spins a little because of his second mistake...
Second error: use the current rotation as the initial rotation
Every frame you interpolate using a=inicial.rotation
. This is not the initial rotation of your object when you start the game (which is what I suppose you wanted, since it is animating with interpolation), but yes the current rotation after the execution of the previous table! Therefore, even with you using a value of t
fixed to each table, the result will vary somewhat because the interpolation of the following table will no longer be between a
and b
original, but between a+<resultado anterior>
and b
.
Solution
The solution is to solve the two problems described above.
Starting with the last one, it’s simple: save the initial rotation in a class attribute, and always use it in the Slerp call. So you ensure that your a
will always be the same.
Moving to the first problem: calculate a value of t
which is a de facto progression, ie a value between 0 and 1. As you want to use time, set a class attribute to count time yourself (adding the team.Deltatime in it to each frame) and weigh the value of t
calculating as the percentage of elapsed time (simple math, only making it elapsed time divided by total time, this already gives you a value between 0 and 1).
From a performance point of view, you no longer need to calculate the interpolation once the animation is finished (i.e., the value of t
has reached 1). So put this inside a if
.
Code example:
using UnityEngine;
public class Cube : MonoBehaviour {
Transform inicial;
float tempo;
float angle;
float decorrido;
Quaternion start;
Quaternion end;
void Start () {
inicial = GetComponent<Transform>();
tempo = 2.0f; // 2 segundos
decorrido = 0.0f;
angle = inicial.rotation.z + 180;
start = inicial.rotation;
end = Quaternion.Euler(0, 0, angle);
}
void Update () {
if(decorrido <= tempo) {
transform.rotation = Quaternion.Slerp(start, end, decorrido / tempo);
decorrido += Time.deltaTime;
}
}
}
P.S.: I left it like this so you’d realize just what really needed to change to work. But note that you do not need to capture and save the transformation of the current object in inicial
because he’s already in transform
. That is, in the context of this code, inicial.rotation
is the same thing as transform.rotation
.