En este tutorial vamos a estudiar la función Slerp de Quaternion en Unity, que nos permite pasar de una orientación inicial a una final en un período de tiempo determinado, realizando rotaciones suaves.
La versión en video de este tutorial no está disponible actualmente en este idioma.
Transcripción del video
¡Hola a todos!
En este tutorial vamos a estudiar la función Slerp de Quaternion en Unity, que nos permite pasar de una orientación inicial a una final en un período de tiempo determinado, realizando rotaciones suaves.
En este tutorial asumiré que ya estás familiarizado con Time.deltaTime, que he tratado en un videotutorial publicado anteriormente. Si no estás familiarizado con Time.deltaTime, te recomiendo encarecidamente ver ese video antes de continuar con este.
Este tutorial fue grabado utilizando Unity 2022; sin embargo, la función Slerp ha estado disponible en múltiples versiones y probablemente seguirá estando disponible en futuras versiones.
Una rotación desde la orientación A hasta la orientación B, y más en general una interpolación entre el valor A y el valor B en un período de tiempo determinado, puede ocurrir de distintas maneras: los valores pueden seguir una progresión lineal, una progresión esférica u otros tipos de progresiones, incluidas las personalizadas.
En el video muestro algunos ejemplos de interpolación, específicamente rotando un cubo 90 grados alrededor de su eje vertical. Es un ejemplo sencillo, pero transmite eficazmente el concepto.
El tiempo de rotación se mapea dentro del rango [0,1], donde en el instante 0 tenemos el valor inicial y en el instante 1 tenemos el valor final. Los valores intermedios del rango corresponden a los valores proporcionados por la interpolación elegida.
En los ejemplos, todos los cubos rotan 90 grados en 5 segundos; sin embargo, en el cuarto segundo de la animación, que corresponde al momento 0.8 en el rango [0,1], los distintos cubos tendrán orientaciones diferentes porque sus interpolaciones son distintas.

Al realizar rotaciones, como en nuestro caso, se prefiere la interpolación esférica frente a la interpolación lineal porque el resultado es más agradable.
En Unity, la interpolación esférica se calcula mediante la función Slerp, donde la S inicial significa Spherical, mientras que la interpolación lineal se calcula mediante la función Lerp, donde la L inicial significa Linear.
Como se mencionó anteriormente, Slerp forma parte de la clase Quaternion, que permite especificar orientaciones y rotaciones en el espacio 3D utilizando cuaterniones, un concepto matemático que no explicaré en este tutorial. Me limitaré a decir que se utilizan para expresar orientaciones y espero que los matemáticos me perdonen por esta simplificación, y a mostrarte cómo utilizar la función Slerp.
Slerp se utiliza para pasar de la orientación A a la orientación B.
Recibe tres parámetros: la orientación inicial, la orientación objetivo y el momento dentro del rango [0,1] en el que queremos conocer la orientación, es decir, el momento en el que queremos evaluar la interpolación.
Slerp devuelve un cuaternión que representa la orientación evaluada en el instante especificado.
Por lo tanto, cuando escribimos:
Quaternion.Slerp(A, B, 0.2);
estamos pidiendo a Unity que obtenga la orientación que el objeto debe tener en una quinta parte de la rotación necesaria para pasar de la orientación A a la orientación B, ya que 0.2 es una quinta parte de 1.
Si queremos que la animación dure 1 segundo, podemos usar Time.deltaTime en Update porque el rango de interpolación de 0 a 1 puede convertirse perfectamente en una animación de un segundo utilizando Time.deltaTime en Update, como mostré en el videotutorial sobre Time.deltaTime.
Por otro lado, para realizar la animación en un número arbitrario de segundos, debemos dividir Time.deltaTime por esa cantidad. Hablaré de esto con más detalle en breve con un ejemplo práctico.
La mayoría de las veces, la orientación inicial es la orientación del objeto, es decir, transform.rotation, ya que la rotación se expresa en cuaterniones.
Calcular la orientación objetivo, por ejemplo añadiendo un ángulo en grados a la orientación inicial, puede ser problemático; sin embargo, también abordaremos esto enseguida en el ejemplo práctico. Bien, finalmente pasemos a un ejemplo práctico.
En el video estoy mostrando una escena con un modelo 3D.

Específicamente, el modelo consta de 4 elementos: el marco de la puerta, las dos hojas de la puerta y una manija.
Aunque el punto pivote de la hoja de la puerta con la manija estaba inicialmente ubicado en las bisagras en mi modelo original, lo que permitía que la rotación ocurriera correctamente, por alguna razón Unity lo ha movido un poco hacia adelante.
Para resolver este problema, agrego un objeto Empty como hijo del marco y lo coloco en una de las bisagras de la hoja de la puerta que quiero animar.
Luego, hago que la hoja de la puerta sea hija del Empty.
Este nuevo objeto Empty necesitará un script de rotación, que crearemos de inmediato, llamándolo por ejemplo "doorOpening".
En particular, queremos que la hoja de la puerta se abra automáticamente al inicio del juego, rotando 90° alrededor de su eje vertical LOCAL, que en este caso es el eje Z.

La orientación inicial viene dada por transform.rotation.
Por comodidad, y para hacer el código más flexible, también declaro una variable de tipo Quaternion llamada startRotation y la inicializo en Start con:
startRotation = transform.rotation;
El cuaternión que representa la orientación final debe ser igual a la orientación inicial más una rotación de 90° alrededor del eje Z local; pero ¿cómo definimos esta rotación en Quaternions?
La función Quaternion.Euler viene en nuestra ayuda, ya que recibe tres parámetros: los ángulos de rotación en grados alrededor de los tres ejes XYZ.
Entonces definimos el cuaternión de la rotación a realizar como:
Quaternion rotationToBePerformed;
y lo inicializamos en Start con:
rotationToBePerformed = Quaternion.Euler(0f, 0f, 90f);
Este cuaternión representa la operación que se debe realizar, pero aún no es la orientación final.
Necesitamos calcularla sumando este valor a la orientación inicial.
Y aquí viene la parte delicada: para añadir la rotación a realizar a la orientación inicial, los dos cuaterniones deben MULTIPLICARSE entre sí.
Entonces definimos el cuaternión final como:
Quaternion targetRotation;
y lo inicializamos en Start con:
targetRotation = startRotation * rotationToBePerformed;
Pasemos a la función Update, que como recordatorio se invoca en cada frame del juego, y comencemos a escribir:
transform.rotation = Quaternion.Slerp(startRotation, targetRotation
y nos detenemos aquí porque todavía no hemos definido una variable para el tiempo de evaluación de la interpolación.
Esta variable debe comenzar en 0, así que podemos definirla e inicializarla fuera de Update con:
float evaluation = 0f;
luego también podemos escribirla como tercer parámetro de la función Quaternion.Slerp.
Además, dado que su valor debe aumentar en cada frame, escribimos en Update:
evaluation += Time.deltaTime;

Sabemos que el tiempo final de evaluación de la rotación será 1.0, por lo tanto esta animación durará exactamente un segundo, ya que estamos incrementando evaluation con Time.deltaTime en cada frame.
Ahora, al iniciar el juego, notaremos que el Empty rota alrededor de su eje vertical, pero la hoja de la puerta no se abre.
Esto se debe a que inicialmente configuré este objeto como Static, ya que no esperaba animarlo y había hecho que Unity calculase la iluminación global; sin embargo, ahora quiero animarlo, así que desmarco la casilla Static en su Inspector y confirmo la operación para los objetos hijos, es decir, la manija.
Al iniciar el juego nuevamente, esta vez veremos la puerta abriéndose con una interpolación suave en un segundo.

¿Cómo podemos ahora definir una duración de animación diferente?
La respuesta es bastante sencilla: el incremento de evaluation en cada frame debe ser igual a Time.deltaTime dividido por la duración de la animación.
Con solo Time.deltaTime, la animación dura un segundo, así que para hacer que la animación dure, por ejemplo, 5 segundos, necesitaremos aumentar evaluation en una quinta parte de Time.deltaTime en cada frame.
Entonces definimos una variable:
float openingDuration = 5f;
porque queremos que la animación dure 5 segundos. Luego actualizamos la instrucción relativa a evaluation en el método Update de la siguiente manera:
evaluation += (Time.deltaTime / openingDuration);

Guardamos el script, volvemos al editor de Unity e iniciamos el juego para observar el resultado. Esta vez, la animación se realizará en 5 segundos.
Con este ejemplo práctico puedes ver cómo utilizar Quaternion.Slerp para interpolar suavemente entre dos orientaciones en una duración específica.
Puedes adaptar este script a distintos escenarios u objetos para lograr rotaciones y animaciones suaves en tus proyectos de Unity.
En resumen, en este tutorial hemos visto cómo expresar la rotación a realizar en cuaterniones; cómo obtener el cuaternión objetivo, dado por el cuaternión inicial multiplicado por la rotación a realizar; cómo utilizar la función Slerp para realizar rotaciones con interpolación esférica, con duraciones variables y velocidad de ejecución constante independientemente del frame rate, gracias a Time.deltaTime.
Espero que este tutorial te haya sido útil. ¡Nos vemos pronto!