03/10/26 · 6 min de lectura

Quaternions

Cómo utilizar el sistema de cuaternión para gestionar la rotación de los objetos del juego.

Imagen por Izzy Park .

En Unity, la rotación de un Trasform se guarda como un Quaternion (cuaternio). Funcionan de manera similar a los vectores pero tienen cuatro componentes: x, y, z y w. Estos componentes son interdependientes y trabajan juntos para definir cualquier rotación que un objeto pueda necesitar.

Una de las cosas más importantes que debemos recordar cuando estamos usando cuaternios es que desde que los componentes x, y, z y w funcionan juntos, nunca deberías ajustarlos individualmente.

Afortunadamente, Unity tiene muchas funciones integradas para hacer la administración de los cuaternios muy fácil.

El sistema para administrar las rotaciones con el que probablemente estás familiarizado se llama Ángulos de Euler. Los valores rotacionales que se muestran en el Inspector usan este sistema, porque es un poco más fácil de entender.

La rotación de los Ángulos de Euler se basa en una rotación alrededor de cada uno de los ejes: horizontal, vertical y z. Quizás te preguntes por qué Unity utiliza cuaternios si los Ángulos de Euler son fáciles de entender, especialmente desde que Unity convierte los cuaternos a Ángulos de Euler para el Inspector.

Basta decir que los Ángulos de Euler están sujetos a algo llamado el bloqueo de cardán, el que puede prevenir que los incrementos rotacionales funcionen correctamente. Afortunadamente, desde que Unity guarda las rotaciones como cuaternios, los mismos que no están sujetos al bloqueo de cardán, no tenemos ese problema.

Vamos a echarle un vistazo a algunas de las funcionalidades que están disponibles a través de la clase Quaternion.

Vamos a empezar con la función LookRotation(). Aquí tenemos la configuración de una escena con un personaje y una esfera. El personaje tiene un script anexado llamado LookatScript.

La esfera tiene un script anexado llamado MotionScript. El MotionScript. simplemente mueve el objeto de un lado al otro. basado en el ingreso del eje horizontal.

public class MotionScript : MonoBehaviour 
{
    public float speed = 3f;

    void Update()
    {
        transform.Translate(Input.GetAxis("Horizontal") * speed * Time.deltaTime, 0, 0);
    }
}

LookAtScript tiene un campo público para un Transform target. y en la función Update() vamos a usar LookRotation() para hacer que el objeto rote y esté de cara al objetivo.

public class LookAtScript : MonoBehaviour 
{
    public Transform target;

    void Update()
    {
        Vector3 relativePos = target.position - transform.position;
        transform.localRotation = Quaternion.LookRotation(relativePos);
    }
}

La función LookRotation() toma un Vector3. Regresa una rotación cuaternio alineada con el Vector3 que se pasó.

Al calcular el vector relativo entre el objeto y el objetivo:

Vector3 relativePos = target.position - transform.position;

podemos hacer que el eje z del objeto rote hacia el objetivo.

transform.localRotation = Quaternion.LookRotation(relativePos);

Esto funciona de manera similar a transform.LookAt(), pero al usar cuaternos se configura la rotación explícitamente.

También vale la pena recalcar que podemos pasar un segundo Vector3 a la función. Este Vector3 le dice a la función hacia qué dirección se considera arriba.

transform.localRotation = Quaternion.LookRotation(relativePos /*forward*/, vector3.Up /*upwards*/);

En el Editor Unity, iniciamos nuestra escena. Podemos ver que no importa a dónde movamos la esfera, el personaje se torna hacia ella.

gif

La próxima función a la que queremos echarle un vistazo es la función Slerp(). Slerp es una abreviación de Spherical Interpolation (interpolación polar). Es muy similar a la función Lerp(), la misma que es una abreviación de Linear Interpolation (interpolación linear).

La gran diferencia entre las dos es que Lerp hará una interpolación pareja entre dos cuaternos mientras que es Slerp() hará una interpolación en una curva. El resultado es que Lerp() proveerá un cambio parejo a través del tiempo, mientras que Slerp() comenzará y terminará despacio y será más rápida a la mita

Aquí tenemos una escena con otro personaje y otra esfera. El personaje no tiene ningún script anexado. La esfera tiene un GravityScript anexado a ella. Esta escena usa Slerp() y un poco de arranque para obtener un efecto de gravedad orbital.

public class GravityScript : MonoBehaviour 
{
    public Transform target;

    void Update()
    {
        Vector3 relativePos = (target.position + new Vector3(0, 1.5f, 0)) - transform.position;
        Quaternion rotation = Quaternion.LookRotation(relativePos);

        Quaternion current = transform.localRotation;
        transform.localRotation = Quaternion.Slerp(current, rotation, Time.deltaTime);
        transform.Translate(0, 0, 3 * Time.deltaTime);
    }
}

En el script GravityScript hemos configurado algo similar al LookAtScript de antes. Otra vez, tenemos un campo público para nuestro Transform objetivo.

public Transform target;

También estamos calculando el vector relativo entre el objeto y el objetivo. Aunque esta vez estamos agregando un offset para representar la altura de la esfera.

Vector3 relativePos = (target.position + new Vector3(0, 1.5f, 0)) - transform.position;

También estamos calculando la LookRotation() como antes, excepto que esta vez no estamos guardándola en la rotación del Transform del objeto. En su lugar estamos guardándola en una variable tipo cuaternio llamada rotation.

Quaternion rotation = Quaternion.LookRotation(relativePos);

Luego, guardamos la rotación local del objeto en una variable cuaternio llamada current.

Quaternion current = transform.localRotation;

Después de eso usamos la función Slerp() para rotar el objeto despacio para que mire hacia el objetivo.

transform.localRotation = Quaternion.Slerp(current, rotation, Time.deltaTime);

La función Slerp() lee la rotación actual, la rotación resultado y la velocidad que deben interpolar o girar. El hecho de que la rotación no ocurra inmediatamente, sino que ocurra a medida que pase el tiempo, es la clave para hacer que este ejemplo funcione.

Después de que rotemos el objeto ligeramente hacia el objetivo, lo movemos hacia adelante un poco.

transform.Translate(0, 0, 3 * Time.deltaTime);

En el Editor Unity, iniciamos la escena y vemos el resultado. La esfera siempre está moviéndose hacia adelante y girando para estar de frente al objetivo, el mismo que es este caso es el personaje. Esto nos da un efecto orbital muy regular y agradable.

gif

Lo último que veremos es la propiedad identity (identidad) de la clase Quaternion. El configurar un cuaterno a quaternion.identity efectivamente configurará su rotación Euler a 0, 0, 0 o sin rotación.

public class SomeClass : MonoBehaviour 
{
    private void Start()
    {
        transform.localRotation = Quaternion.identity;
    }
}

Hay más funciones en la clase Quaternion que puedes encontrar en la documentación vinculada en la parte inferior.

Lo más importante que debes recordar de esta lección es que los Quaternion son la mejor manera de lidiar con la rotación y que nunca deberías alterar sus componentes independientemente. Siempre habrá algún tipo de funcionalidad que puedas usar en vez de eso.

Gist del proyecto

Apoya el contenido de mi blog a través de:

Fuente:

Copiado!