В этом уроке мы изучим функцию Slerp класса Quaternion в Unity, которая позволяет переходить от начальной ориентации к конечной за заданный промежуток времени, выполняя плавные вращения.


Видеоверсия этого руководства в настоящее время недоступна на этом языке.


Текстовая расшифровка видео

Всем привет!

В этом уроке мы изучим функцию Slerp класса Quaternion в Unity, которая позволяет переходить от начальной ориентации к конечной за заданный промежуток времени, выполняя плавные вращения.

В этом уроке я предполагаю, что вы уже знакомы с Time.deltaTime, который я рассматривал в ранее опубликованном видеоуроке. Если вы не знакомы с Time.deltaTime, настоятельно рекомендую посмотреть это видео перед продолжением.

Этот урок был записан с использованием Unity 2022. Тем не менее функция Slerp доступна во многих версиях и, скорее всего, будет доступна и в будущих релизах.

Переход от ориентации A к ориентации B, а более общо интерполяция между значением A и значением B за заданный промежуток времени, может происходить разными способами: значения могут изменяться линейно, по сферической траектории или по другим типам прогрессии, включая пользовательские.

В видео я показываю несколько примеров интерполяции, в частности вращение куба на 90 градусов вокруг его вертикальной оси. Это простой пример, но он эффективно передает саму идею.

Время вращения отображается в диапазоне [0,1], где в момент 0 мы имеем начальное значение, а в момент 1 — конечное значение. Промежуточные значения диапазона соответствуют значениям, определяемым выбранной интерполяцией.

В примерах все кубы поворачиваются на 90 градусов за 5 секунд. Однако на четвертой секунде анимации, что соответствует моменту 0.8 в диапазоне [0,1], различные кубы будут иметь разную ориентацию, поскольку используются разные типы интерполяции.

При выполнении вращений, как в нашем случае, сферическая интерполяция предпочтительнее линейной, потому что результат выглядит более естественно.

В Unity сферическая интерполяция вычисляется с помощью функции Slerp, где начальная буква S означает Spherical, а линейная интерполяция вычисляется с помощью функции Lerp, где L означает Linear.

Как уже упоминалось, Slerp является частью класса Quaternion, который позволяет задавать ориентации и вращения в трехмерном пространстве с использованием кватернионов. Это математическое понятие я не буду объяснять в данном уроке.

Ограничусь тем, что они используются для выражения ориентаций и покажу, как применять функцию Slerp.

Slerp используется для перехода от ориентации A к ориентации B.

Она принимает три параметра: начальную ориентацию, целевую ориентацию и момент в диапазоне [0,1], в который мы хотим узнать ориентацию, то есть момент, в который нужно вычислить интерполяцию.

Slerp возвращает кватернион, представляющий ориентацию, вычисленную в заданный момент.

Итак, когда мы пишем:

Quaternion.Slerp(A, B, 0.2);

мы просим Unity определить ориентацию, которую объект должен иметь на одной пятой части пути вращения от ориентации A к ориентации B, поскольку 0.2 составляет одну пятую от 1.

Если мы хотим, чтобы анимация длилась 1 секунду, можно использовать Time.deltaTime в Update, потому что диапазон интерполяции от 0 до 1 можно точно преобразовать в односекундную анимацию с помощью Time.deltaTime в Update, как я показывал в видеоуроке о Time.deltaTime.

Если же необходимо выполнить анимацию за произвольное количество секунд, следует разделить Time.deltaTime на это количество. Чуть позже я подробнее покажу это на практическом примере.

В большинстве случаев начальная ориентация — это ориентация объекта, то есть transform.rotation, поскольку вращение выражается в кватернионах.

Вычисление целевой ориентации, например добавление угла в градусах к начальной ориентации, может быть не совсем очевидным. Однако мы также рассмотрим это в практическом примере. Итак, давайте перейдем к практике.

В видео я показываю сцену с 3D моделью.

В частности, модель состоит из четырех элементов: дверная рама, две створки двери и ручка.

Хотя точка pivot у створки с ручкой в исходной модели находилась на петлях, что позволяло вращению происходить корректно, по какой то причине Unity немного сместил ее вперед.

Чтобы решить эту проблему, я добавляю Empty объект как дочерний к раме и размещаю его на одной из петель той створки, которую хочу анимировать.

Затем я делаю створку дочерним объектом этого Empty.

Для этого нового Empty потребуется скрипт вращения, который мы сейчас создадим, назвав его, например, doorOpening.

В частности, нам нужно, чтобы створка автоматически открывалась в начале игры, поворачиваясь на 90 градусов вокруг своей локальной вертикальной оси, которая в данном случае является осью Z.

Начальная ориентация задается через transform.rotation.

Для удобства и чтобы сделать код более гибким, я также объявляю переменную типа Quaternion с именем startRotation и инициализирую ее в Start следующим образом:

startRotation = transform.rotation;

Кватернион, представляющий конечную ориентацию, должен быть равен начальной ориентации плюс вращение на 90 градусов вокруг локальной оси Z. Но как задать это вращение в кватернионах?

Здесь нам помогает функция Quaternion.Euler, которая принимает три параметра: углы поворота в градусах вокруг осей X, Y и Z.

Итак, определяем кватернион выполняемого вращения:

Quaternion rotationToBePerformed;

и инициализируем его в Start следующим образом:

rotationToBePerformed = Quaternion.Euler(0f, 0f, 90f);

Этот кватернион представляет операцию вращения, но это еще не конечная ориентация.

Ее нужно вычислить, добавив это значение к начальной ориентации.

И здесь важный момент: чтобы добавить выполняемое вращение к начальной ориентации, два кватерниона нужно перемножить.

Поэтому определяем конечный кватернион:

Quaternion targetRotation;

и инициализируем его в Start следующим образом:

targetRotation = startRotation * rotationToBePerformed;

Переходим к функции Update, которая вызывается каждый кадр игры, и начинаем писать:

transform.rotation = Quaternion.Slerp(startRotation, targetRotation

и останавливаемся, потому что пока не определили переменную времени вычисления интерполяции.

Эта переменная должна начинаться с 0, поэтому объявляем и инициализируем ее вне Update следующим образом:

float evaluation = 0f;

затем указываем ее третьим параметром в функции Quaternion.Slerp.

Кроме того, поскольку ее значение должно увеличиваться каждый кадр, в Update пишем:

evaluation += Time.deltaTime;

Мы знаем, что конечное значение evaluation будет равно 1.0, следовательно анимация продлится ровно одну секунду, так как на каждом кадре мы увеличиваем evaluation на Time.deltaTime.

Теперь, запустив игру, мы заметим, что Empty вращается вокруг своей вертикальной оси, но створка двери не открывается.

Это происходит потому, что изначально я отметил этот объект как Static, поскольку не планировал его анимировать и позволил Unity рассчитать глобальное освещение. Теперь же объект нужно анимировать, поэтому я снимаю флажок Static в Inspector и подтверждаю изменения для дочерних объектов, то есть для ручки.

Запустив игру снова, мы увидим, как дверь открывается с плавной интерполяцией за одну секунду.

Как теперь задать другую длительность анимации?

Ответ довольно прост: приращение evaluation на каждом кадре должно быть равно Time.deltaTime, деленному на длительность анимации.

Если использовать только Time.deltaTime, анимация длится одну секунду. Поэтому, чтобы анимация длилась, например, 5 секунд, нужно увеличивать evaluation на одну пятую Time.deltaTime на каждом кадре.

Итак, объявим переменную:

float openingDuration = 5f;

поскольку мы хотим, чтобы анимация длилась 5 секунд. Затем изменим строку обновления evaluation в методе Update следующим образом:

evaluation += (Time.deltaTime / openingDuration);

Сохраните скрипт, вернитесь в редактор Unity и запустите игру, чтобы увидеть результат. На этот раз анимация будет выполняться за 5 секунд.

В этом практическом примере вы увидели, как использовать Quaternion.Slerp для плавной интерполяции между двумя ориентациями за заданную длительность.

Вы можете адаптировать этот скрипт для различных сценариев и объектов, чтобы реализовать плавные вращения и анимации в своих проектах Unity.

Итак, подведем итог: в этом уроке мы рассмотрели, как выразить выполняемое вращение в кватернионах; как получить целевой кватернион как произведение начального кватерниона и кватерниона выполняемого вращения; как использовать функцию Slerp для выполнения вращений со сферической интерполяцией с переменной длительностью и постоянной скоростью выполнения независимо от частоты кадров благодаря Time.deltaTime.

Надеюсь, этот урок был для вас полезен. До скорой встречи!

Этот сайт предназначен исключительно для демонстрации некоторых моих работ и не имеет рекламных целей. Обратите внимание, что в настоящее время я не ищу и не рассматриваю запросы на индивидуальные заказы, консультации или любые другие формы профессионального сотрудничества.


РАСШИРЕННАЯ ИНФОРМАЦИЯ О ПОЛИТИКЕ КОНФИДЕНЦИАЛЬНОСТИ И ИСПОЛЬЗОВАНИИ ФАЙЛОВ COOKIE