{"id":4012,"url":"\/distributions\/4012\/click?bit=1&hash=5b9cad3f989520ad358a2237d28d1f12ecdc50cb8452456f27fcbce716b2c8f0","title":"\u041f\u043e\u044f\u0432\u0438\u043b\u0441\u044f \u0438\u043d\u0441\u0442\u0440\u0443\u043c\u0435\u043d\u0442 \u0434\u043b\u044f \u0441\u0442\u0435\u0441\u043d\u0438\u0442\u0435\u043b\u044c\u043d\u044b\u0445 \u0440\u0438\u0435\u043b\u0442\u043e\u0440\u043e\u0432","buttonText":"","imageUuid":"","isPaidAndBannersEnabled":false}

Пространственные преобразования в Unreal Engine

Для самых маленьких

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

Вся пространственная математика строится на матрицах и операциях с ними. К счастью, напрямую перемножать матрицы при разработке игр вам не придется.

В UE4 положение объекта в пространстве задается структурой Transform (FTransform), в основе которой лежит матрица трансформации, представляющая собой перемноженные матрицы масштабирования/ориентации/трансляции.

Для работы с ориентацией используется структура Rotator (FRotator), а в C++ есть и кватернион (FQuat). Rotator строит матрицу ориентации по эйлеровым координатам Roll, Pitch, Yaw (aka вращение, тангаж, рысканье). Если углубляться в детали, то стоит отметить, что при вычислении ориентации в UE4 сначала применяется вращение вокруг оси Z (Yaw), затем Y (Pitch) и в конце X (Roll). При изменении порядка вы получите другую ориентацию. Это стоит иметь в виду при копировании координат объектов в движок или из движка в другое ПО.

Вектор

Vector (FVector) – не матрица, а структура для хранения координат в трехмерном пространстве. На основе вектора могут строиться как матрицы трансляции, так и матрицы масштабирования.

Самые-самые начинающие не всегда могут понять, задает ли вектор координаты в пространстве или направление. На самом деле, это зависит от того, как вы его используете и какой смысл вкладываете. Так, вектор (3, 0, 5) может означать координаты объекта, который сдвинут на 3 см вперед и на пять вверх от центра координат, а может – направление вперед-вверх (т. е. направление из центра координат).

Векторы можно умножать и делить, а еще их можно нормализовать, т. е. свести к единичной длине. Последняя операция имеет смысл, только когда мы говорим о направлениях. После нормализации вектор (5, 0, 0) – направление «вперед» – превратится в (1, 0, 0) – все еще «вперед», но длина вектора теперь равна единице.

Помимо сложения и умножения, для векторов можно находить векторное произведение (Cross Product – по моему опыту, оно используется редко) и скалярное произведение (Dot Product). Если вы заглянете в определение, то увидите, что это скалярное произведение – это произведение длин векторов на косинус угла между ними. На практике это означает, что скалярное произведение двух векторов будет больше нуля, когда векторы хотя бы чуть-чуть направлены в одну сторону, и меньше нуля, когда они направлены в противоположные стороны.

Если векторы были нормализованы, то скалярное произведение всегда будет находиться в пределах от -1 (диаметрально противоположные направления) до 1 (направления идеально совпадают).

Как понять, с какой стороны стены находится кубик?

Надо найти скалярное произведение направления стена->кубик и нормали стены.

Трансформации

Трансформации – это матрицы, а значит, их можно умножать, причем при умножении порядок множителей имеет значение. С точки зрения не-математической логики, в результате умножения трансформаций мы получаем то, что скорее напоминает их сумму. На первом месте (верхний пин) находится значение, которое мы прибавляем, а на втором (нижний пин) – то, к чему прибавляем.

Вернемся к кубикам.

Умножая локальную трансформацию объекта А относительно объекта Б на трансформацию Б относительно центра координат, мы получаем трансформацию А относительно центра координат. Ничего сложного.

Обратная операция несколько сложнее с точки зрения математики, но разработчики UE4 позаботились о том, чтобы вам не пришлось с ней возиться.

Функция Make Relative Transform находит локальную трансформацию объекта А относительно Б. В более ранних версиях движка вместо нее присутствовала функция Convert Transform to Relative, но у нее были перепутаны названия параметров, и разработчики Epic Games решили, что проще будет создать новую функцию, чтобы никого не шокировать переименованием.

Ориентация

Объекты Rotator можно умножать точно так же, как и трансформации, но соответствующая операция спрятана под функцией Combine Rotators.

Внутри этой функции происходит старое доброе умножение кватернионов:

FRotator UKismetMathLibrary::ComposeRotators(FRotator A, FRotator B) { FQuat AQuat = FQuat(A); FQuat BQuat = FQuat(B); return FRotator(BQuat*AQuat); }

Rotator имеет полезную функцию Rotate Vector (и противоположную по смыслу Unrotate Vector), которая делает ровно то, что описано в названии: поворачивает вектор в соответствии с той ориентацией, которая установлена в Rotator.

Если мы повернем относительные координаты объекта А относительно объекта Б в ротаторе объекта Б, мы получим смещение А относительно Б в мировых координатах.

А еще можно использовать операцию вычитания (Delta Rotator).

Если вам нужно найти смещение ориентации одного объекта относительно другого, не вычитайте напрямую координаты Эйлера (Yaw2 – Yaw1), поскольку они всегда находятся в диапазоне от -180 до 180. Если Yaw1 = -179, а Yaw2 = 179, то разница между ними составляет два градуса, а простое вычитание даст 358. Функция вычитания ротаторов нормализует координаты и выдаст корректные два градуса.

И последняя весьма полезная операция – вращение относительно локальных осей координат.

Предположим, стоит задача повернуть кубик относительно его собственной вертикальной оси Z. Сделать это очень легко, ведь в движке уже реализована функция Add Relative Rotation. Для экторов она называется Add Actor Relative Rotation. Тут имеет смысл напомнить, что эктор не обладает каким-то самостоятельным положением в пространстве. Его положение полностью определяется положением корневого компонента. Т.е. Root Component -> Set World Rotation – это то же самое, что и Set Actor Rotation.

Однако что делать, если возникла необходимость повернуть относительно локальных осей не объект, а трансформацию, которая хранится в простой переменной, и функции Add Relative Rotation у нее нет? Это очень легко. Вам всего лишь надо…

Тех же результатов можно достичь, перемножив трансформации, однако в этом случае вы нагружаете процессор ненужными вычислениями трансляции и масштаба.

0
28 комментариев
Написать комментарий...
A3zazel

а) Такие материалов на сайте должно быть больше.
б) А мемов от Гуся меньше.
в) Да и активности оно заслуживает тоже куда больше.
г) А мемы от Гуся меньше.

ИМХО.

Ответить
Развернуть ветку
Антон Романов

Полностью согласен!

Ответить
Развернуть ветку
Иванка Сегодина

   UE в России в 3,5 или в 4 раза менее популярен чем Unity, это можно посмотреть по активностям на тематических форумах, вакансиях hh или соц.сетях. Люди которые интересуются движком сами ищут новости на Euro порталах, следят за тенденциями и, при желании, могут для себя или своих знакомых что-то перевести.
   Почти все youtube каналы находятся в состоянии "на грани развала", видео ролики с переводами не набирают много просмотров (пару тысяч за неделю это уже классно), каких-то ярких и потрясающих обучалок практически нет. Каналы интересных людей, например такого как Алексей Савченко (менеджер по лицензированию Epic Games) вообще имеют несколько сотен просмотров и чуть больше 1 000 подписчиков, хотя человек на регулярной основе выпускает интересные ролики, отвечает на стримах, пишут статьи и дает интервью. 
*получается многие думают, зачем мне утруждать себя, создавать тему, оформлять ее, тратить время и получить за это 500-700 просмотров в неделю на DTF.
**возможно ситуация с донатной темой пользователями и сможет немного сдвинуть в лучшую сторону эту ситуацию. 

Ответить
Развернуть ветку
A3zazel

Да, я наблюдаю туже картину...

Вероятно, это напрямую связанно с тем, что в ру-сегменте и игры, в основном, делают мобильные, где, очевидно, Unity и рулит. А жаль..,(

Ответить
Развернуть ветку
Евгений Торопчин

Это напрямую связано с простотой и понятностью unity и документации к нему. Куче простых и хороших обучалок основам. С# более удобен чем C++

Ответить
Развернуть ветку
A3zazel

Писать логику плюсами в U4!? о_О
Фу, моветон!,)

Ответить
Развернуть ветку
Антон Антонов
Автор

Математика точно так же работает и в Unity. Там отличается только реализация, а суть — та же.

Ответить
Развернуть ветку
Антон Антонов
Автор

Прост сайт после перезапуска — не про геймдев, а про игры.

Ответить
Развернуть ветку
A3zazel

К величайшему сожалению,(

Ответить
Развернуть ветку
Anton Tananuka

Антон Антонов из всех миров, где есть Антоны, самый Антон! (а куда делись нейронные комменты?)

Ответить
Развернуть ветку
Алексей Иванов

Буквально сегодня страдал сидел с локалыми и глобальными ротациями...
Ох настрадался. 
Добавлю себе в закладки сей труд. 
Большая человеческая благодарность автору! 

Щас, когда ИИ делать буду, ох чую, пригодится мне этот пост.

Ответить
Развернуть ветку
Victor T

Видимо, я самый самый самый маленький, потому что я нихрена не понял)

Ответить
Развернуть ветку
Alex Twofaced
 Если вам нужно найти смещение ориентации одного объекта относительно другого, не вычитайте напрямую координаты Эйлера (Yaw2 – Yaw1), поскольку они всегда находятся в диапазоне от -180 до 180. Если Yaw1 = -179, а Yaw2 = 179, то разница между ними составляет два градуса, а простое вычитание даст 358. Функция вычитания ротаторов нормализует координаты и выдаст корректные два градуса.

Вот где ты был 3 года назад. Это же самая жесть, сколько говен пришлось съесть тестируя вонючие ротаторы :)

Ответить
Развернуть ветку
Евгений Иванов

Подскажите как использовать FQuat что бы сделать relative rotation. Все попытки сделать умножения FQuat приводят только к world или local rotation.

Ответить
Развернуть ветку
Антон Антонов
Автор

Не совсем понял вопрос. Надо добавить вращение по локальным осям?

Ответить
Развернуть ветку
Антон Антонов
Автор

Наверное, имеется в виду вот это:

FQuat AddLocalRotation(const FQuat& RotationToAdd, const FQuat& BaseRotation)
{
return (BaseRotation.Inverse() * RotationToAdd.Inverse()).GetInverse();
}

Ответить
Развернуть ветку
Евгений Иванов

надо сделать вращение вокруг родительского объекта, то что делает ноде в BP SetRelativeRotation.  Если делаю

FRotator DeltaRotator(0, 25, 0); 
FQuat Current(GetRelativeRotation());
AngleRotateQuat = Current * DeltaRotator.Quaternion();
SetRelativeRotation(AngleRotateQuat);

Результат неправильный, если ось Y больше 0, то происходит вращение по двум осям сразу.
DeltaRotator в действительности может быть любой, но для одной оси задаётся вращение.

Ответить
Развернуть ветку
Антон Антонов
Автор

Тогда смотри мой ответ выше.
AngleRotateQuat = AddLocalRotation(DeltaRotator.Quaternion(), Current);
SetRelativeRotation(AngleRotateQuat);

Ответить
Развернуть ветку
Евгений Иванов

это тоже же самое, если Y больше нуля и добавить ещё по X вращение то сдвиг по двум осям происходит , я так думаю FQuat только для local и world rotation предназначен. SetRelativeRotation на обычном FRotator даёт другой результат, но там ограничение в 90 градусов.

Ответить
Развернуть ветку
Антон Антонов
Автор

Нет, это не то же самое. Надо умножать инвертированные кватернионы. В статье об этом в самом конце.

Ответить
Развернуть ветку
Евгений Иванов

ты и на с++ это написал, возможно это работает для актора в локальных поворотах, но не для компонента для относительного поворота.

Ответить
Развернуть ветку
Антон Антонов
Автор

Математика работает одинаково для чего угодно, ты просто что-то сделал неправильно.

Ответить
Развернуть ветку
Евгений Иванов

все таки это даёт локальный поворот как функция AddLocalRotation но не подобие Add Relative Rotation . Даже повторив на БП вариант из статьи я получаю локалный поворот.

Ответить
Развернуть ветку
Антон Антонов
Автор

Я не понял. А тебе-то какой поворот нужен, если не локальный? Нужно посчитать ориентацию А относительно Б что ли?

Ответить
Развернуть ветку
Евгений Иванов

нужен результат который даёт Add Relative Rotation на компоненте, точнее такой же результат используя Set Relative Rotation.
Вот сравнивал локальный поворот и относительный это разный результат https://i.imgur.com/pLScTKh.png
Локальный поворот это подобие локального из любого 3Д пакета, если объ повернули на 90 по Х например то Z ось смотрит вниз. Но мне так не надо мне надо что оси поворота от родителя брались. В таком случа если родитель имеет поворот 0, 0, 0 (и рут компонент так же) то Z ось всегда оставалась на месте, как и другие оси.

Ответить
Развернуть ветку
Евгений Иванов

или такой поворот незнаю как он называется: что бы одни и те же оси всегда поворачивали объект в одном направлении. Что то типа глобальный осей поворота. Функция Add World Rotation это не такой. При World Rotation если объект имеет поворот 0, 0, 0 то Y ось поворачивает вверх, т.е. саму ось Y, но если Z имеет угол 90, то добавив Y ось, поворачивается X ось, а не Y. Не понимаю как это работает. Я бы хотел что бы Y поворачивал всегда наверх вне зависимости какой текущий поворот у других осей. И другие оси так же.  Я такое делал с помощью функций FVector::RotateAngleAxis но там я поворачивал вектор на объ который сам зафиксирован. А вот как такое сделать с компонентом который сам вращается я хз.

Ответить
Развернуть ветку
Иван Гайдуков

Добрый день. Собрал логику( пазл). Нажимаем на кнопку поворачиваем статик мешь. Повтор сделан на логике ADD actor rotator. С каждым нажатием имеем заданный угол в 30 градусов. Далее логика проверяет в каком положении находится объект. И далее вызываем логику по перемещению другого актора. Вопрос в чем все работает. Но после сборки данной логики крашится проект??  Возможно ли что данная нода так нагружает проект или вызывает ошибку в вычислении? 

Ответить
Развернуть ветку
Антон Антонов
Автор

Если крашится полностью с вылетом в Crash Reporter, то проблема в коде, не связанном с пространственными перемещениями. Неинициализированный указатель, например.

Если движок обрабатывает ошибку (может подвиснуть секунд на 30, и потом пишет о проблеме в логе), то проблема где-то в логике вычислений. Т. е. при операциях с матрицами (например, ненормализованным кватернионом, или попытка нормализовать нулевой вектор) происходит деление на ноль или что-нибудь подобное. Нужно внимательно изучать код.

Ответить
Развернуть ветку
Читать все 28 комментариев
null