Кватернионы используются для представления вращений.
В Unity все вращения представлены в виде кватернионов. Их использование решает проблему "шарнирного замка" (gimbal lock).
Вникать во внутреннее устройство кватернионов нам нет нужды, мы можем просто их использовать :)
И для начала разберемся как получить кватернион из привычных векторов и углов.
Первый способ (углы указываются в градусах):
//Изменяем вращение объекта
transform.rotation.eulerAngles = new Vector3(0f, 90f, 0f);
Второй способ (значения вращения по каждой оси в градусах):
Quaternion.Euler(float x, float y, float z);
//или
Quaternion.Euler(Vector3 euler);
И в случае с методом и в случае со свойством вращение применяется в порядке: Z, X, Y. Стоит это учитывать, чтобы не было неожиданных поворотов
Третий способ (через ось и угл вращения вокруг неё в градусах):
//По оси и углу поворота
Quaternion.AngleAxis(Vector3 axis, float angle);
Получить углы Эйлера из кватерниона можно через свойство eulerAngles:
//Выводим вращение объекта в привычных значениях
Debug.Log(transform.rotation.eulerAngles);
Или можно получить ось и угол вращения вокруг нее:
float angle = 0f;
Vector3 axis;
//Получение оси и угла поворота
transform.rotation.ToAngleAxis(out angle, out axis);
Debug.Log("Angle: " + angle + " Axis: " + axis);
Теперь разберемся, как с их помощью вращать объекты.
Для этого нужно кватернион, из которого мы хотим повернуть (текущее положение), умножить на кватернион, на который хотим повернуть.
Пример:
//Поворачиваем объект на 90 градусов
transform.rotation *= Quaternion.Euler(0f, 90f, 0f);
Но кватернионы - это тот случай, когда от перемены мест множетелей произведение меняется.
Поэтому такой код даст иной результат:
transform.rotation = Quaternion.Euler(0f, 90f, 0f) * transform.rotation;
Интерполяция
Есть несколько методов интерполяции для кватернионов, первый из них - Lerp:
Quaternion.Lerp(Quaternion a, Quaternion b, float t);
Данный метод возвращает промежуточное вращение между a и b на основе t. При этом значение t ограничивается диапазоном от 0 до 1.
Вернёт a, при t равное или меньше 0.
Вернёт b, при t равное или больше 1.
Второй метод интерполяции - LerpUnclamped:
Quaternion.LerpUnclamped(Quaternion a, Quaternion b, float t);
От предыдущего он отличается только отсутствием ограничений t. Т.е результат выходит за пределы a и b, если t < 0 или t > 1.
Картинка для наглядности:
Следующие два метода интерполяции - Slerp и SlerpUnclumped:
Quaternion.Slerp(Quaternion a, Quaternion b, float t);
Quaternion.SlerpUnclumped(Quaternion a, Quaternion b, float t);
Разница между ними такая же, как и между двумя предыдущими.
А отличие Lerp от Slerp поможет понять это видео (синее - lerp, белое - slerp):
Пример:
Имеется прожектор, который нужно вращать по кругу, чтобы освещать всю территорию вокруг него.
Используем для решения сей задачи LerpUnclamped:
public class ProjectorRotater : MonoBehaviour
{
public Transform projector = null;
private Quaternion start;
private Quaternion end;
void Start()
{
start = Quaternion.identity;
end = Quaternion.Euler (0f, 90f, 0f);
}
void Update()
{
projector.rotation = Quaternion.LerpUnclamped (start, end, Time.time);
}
}
В данном случае прожектор пройдёт полный круг за 4 сек.
Немного изменим задачу.
Прожектор должен освещать не всё вокруг, а только сектор в 90 градусов.
Для этого заменим LerpUnclamped на Lerp. А чтобы прожектор не стопорился в конце, обвернём значение времени функцией Mathf.PingPong:
projector.rotation = Quaternion.Lerp(start, end, Mathf.PingPong(Time.time, 1f));
Задаём направления
Функция FromToRotation (или ее аналог SetFromToRotation) создает такой кватернион, что если его применить к fromDirection, то направление этого вектора совпадёт с toDirection:
Quaternion.FromToRotation(Vector3 fromDirection, Vector3 toDirection);
Обычно используется, чтобы повернуть объект так, чтобы одна из его осей смотрела в нужном направлении.
Функция LookRotation (или SetLookRotation) создает вращение, при котором ось Z сонаправленна forward, а ось Y сонаправленна upwards.
Quaternion.LookRotation(Vector3 forward, Vector3 upwards = Vector3.up);
Можно использовать, например, чтобы поворачивать персонажа к целе:
public class LookExample : MonoBehaviour
{
public Transform target = null;
void Update()
{
Vector3 relativePos = target.position - transform.position;
transform.rotation = Quaternion.LookRotation(relativePos);
}
}
Добавим интерполяцию и регулятор скорости для плавности:
public class LookExample : MonoBehaviour
{
public Transform target = null;
public float speed = 10f;
void Update()
{
Vector3 relativePos = target.position - transform.position;
transform.rotation = Quaternion.Slerp(transform.rotation, Quaternion.LookRotation(relativePos), Time.deltaTime * speed);
}
}
На этом всё :)