Pull to refresh

Comments 14

Это как дела обстоят в нашем мире.

А в Unity3D этот драг имеет линейный характер от скорости. Припомнить такой закон я не смог. Во всяком случае из школьной программы — динамическое сопротивление в среде от квадрата скорости, сопротивление «по асфальту» не зависит от скорости.

Недавно получил ответ от автора Tank Physics Kit, в котором буквально говорится:

I think that PhysX is just a stuff for video games, not a genuine physics simulator.
Even when we set the actual values into the physics components, they will not work realistically.


Курение мануалов по Wheel на сайте nVidia\PhysX к позитивным результатам не привело.

Посему я понял, что если динамика игры претендует слегка походить на наш мир, её надо писать самому. Что касается кинематики, я ещё не определился — я не в курсе верны ли в Unity3D вращательные движения.
_______________________

cbr2002hell огромное спасибо за раскрытую тему!
Расцениваю комментарий как предложение мне провести исследование на тему вращательных движений ))
Вопрос и правда интересный. Хотя бы — как они момент инерции рассчитывают с учетом точечного размера Rigidbody?
Сам спросил — сам ответил. Момент инерции считается по коллайдеру, предполагая, что тело однородно по плотности. Проверил на примитивах: сферах, кубах, цилиндрах.

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

AngularDrag используется идентично линейному:
rb.angularVelocity = rb.angularVelocity * Mathf.Clamp01(1f - myDrag*Time.deltaTime);

Я в древности на бумажке прикидывал. Выходило, что момент инерции описывается тензором
Ixx Ixy Ixz
Iyx Iyy Iyz
Izx Izy Izz
Однако тогда я заметил и то, что некоторые игроделы ограничивались только Ixx, Iyy, Izz — это компоненты, достаточные для вычисления вращения только по координатным осям, и, как я понимаю, не совсем недостаточны для случая произвольной оси.
В документах Unity3D действительно мелькала фраза, что они инерцию считают по равномерному распределению массы в пределах 3Д модели (и суммируют присоединённые меши, если таковые есть), но генерируют ли они полный тензор для вращения по произвольной оси…
Здесь разница в том, что в линейной кинематике a = F/m, это три скаляра (ну пусть даже два вектора и скаляр), и они не грузят процессор.
Во вращательной кинематике ε=M/I, формула «такая же». Но вектор момента силы делится на тензор инерции, что уже не так тривиально. А уж вычисление самого тензора потребует интегрирование по объёму 3D модели. У меня подозрение, что мало кто из игроделов серьёзно будет озадачиваться таким вопросом. Вероятно тут силён соблазн сурово сэкономить (=схалтурить) до уровня Ixx, Iyy, Izz.
Но как смоделировать контрольную ситуацию для сверки Unity3D и правильного вычисления я не сообразил, потому, что интегрировать по объёму мне трудно.

Опять же спасибо за выявление формулы «трения» вращения! :-)
Как выяснилось, типовая физика вращения в играх просчитывается в локальных координатах тела. А потому они хранят лишь одну (предпросчитанную) матрицу Ib, а для произвольного положения момент инерции находят как R*Ib*RT (ну, если точнее — они находят обратную).
Но, как вы заметили, проверить, что в Unity реализуется именно эта модель — проблематично. Остается принять это как наиболее правдоподобное положение.
Разница между I и I' только в ориентации тела, и есть операции, трансформирующие тензор согласно этому. Но сущности тензор не поменяет.

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

Тут и есть соблазн «библиотекаря», разложить «пользовательское» вращение по произвольной локальной оси на последовательность вращений по удобным «библиотекарю» Ox', Oy', Oz', и использовать только три ячейки, Ix'x', Iy'y', Iz'z' из 9-значного тензора. Этим создаётся видимость вращения по произвольной оси. Но, предполагаю, может возникнуть погрешность.

Представьте себе, что вы считаете длину гипотенузы через сумму длины и высоты ступенек. Как бы вы не уменьшали размер ступеньки, для равнобедренного прямоугольного треугольника вы получите гипотенузу, равную 1+1, а не корректное √2.

То есть суть эксперимента истины я представляю так: возьмём пусть даже простую известную фигуру параллелепипед, высчитаем для него верный тензор со всеми 9 ячейками через интеграл момента инерции всех его атомов; затем начнём вращать его процедурно εd=Md/I' по какой либо его диагонали, и рядом вращать такой же с использованием собственных технологий «библиотекаря». Если пойдёт погрешность, то можно через известные для параллелепипеда Ixx, Iyy, Izz попытаться выяснить, при как именно считает «библиотекарь».
Или с чистой совестью успокоиться на том, что «2=√2 точно» ;-)
Лично у меня цена этого вопроса (ярость выкурить том кинематики и первый курс матана) покуда не достигла необходимой для проверки.
Разлагать пользовательское вращение на составные — нет необходимости. Умножение на матрицу (точнее, в движке идет умножение на кватернион) решает все проблемы.
Новая угловая скорость выглядит так (в локальных координатах):
AngularVelocity = AngularVelocity + I_inv*torque*Time.deltaTime - I_inv*Vector3.Cross(AngularVelocity,(I*AngularVelocity))*Time.deltaTime;

здесь I и I_inv — заранее просчитанные прямая и обратная матрица момента инерции; torque — приведенный к локальной системе суммарный момент.
Все, что теперь нужно — получить новый кватернион ориентации:
deltaOrientation = 0.5f*(Orientation * AngularVelocity)*Time.deltaTime;
Orientation = Orientation + deltaOrientation;
NormalizeQuaternion(Orientation);


(здесь полагаем, что умножение матрицы на вектор, сложение кватернионов и их нормализация определены (что для Unity, вообще-то, не верно) )
Простите, а зачем вы умножаете на Time.deltaTime в методе FixedUpdate? Ведь в этом случае значения будут высчитываться в зависимости от частоты кадров, что не есть правильно. Может там должно быть фиксированное значение Time.fixedDeltaTime?
В общем-то, сами разработчики рекомендуют везде и всюду (что в Update(), что в FixedUpdate()) использовать на Time.deltaTime (например, тут https://docs.unity3d.com/ScriptReference/Time-fixedDeltaTime.html). Однако, я и правда где-то встречал мнение, что drag умножается именно на fixedDeltaTime. Единственное — я не помню, как это там обосновывалось.

В контексте выполнения FixedUpdate свойство Time.deltaTime возвращает значение равное Time.fixedDeltaTime, так что разницы нет. Использовать везде Time.deltaTime рекомендуется просто для однообразия.

В целом, меня эта разница не особо интересует. А вот буржуи на каком-то форуме обсуждали, и мелькало сообщение, что fixedDeltaTime возвращает предустановленную константу (ту, которую в настройках можно менять), а deltaTime — реальное значение времени. Лично у меня загрузить машину так, чтобы начала тормозить физика никогда не получалось, так что для меня эти переменные равнозначны.
Из справки к Time.deltaTime: «When called from inside MonoBehaviour's FixedUpdate, returns the fixed framerate delta time»
И вдогонку из справки к Time.fixedDeltaTime: «For reading the delta time it is recommended to use Time.deltaTime instead because it automatically returns the right delta time if you are inside a FixedUpdate function or Update function.»
Sign up to leave a comment.

Articles