Делаю Unreal штуки. Бассейн. Часть вторая.

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

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

Принцип физики основан на силах выталкивания вверх и силе толкания в сторону (чтоб плыло). К этому я добавил дампинги, подавляющие избыточные вращения и придающие эффект сопротивления воды.

Первым, что я попытался сделать - это вычислять пересечение объёма меша с объёмом бассейна. И дальше работать с этим пересечением:

FBox ObjectBox = RootComponent->Bounds.GetBox(); FBox Intersection = ObjectBox.Overlap(PoolVolumeBox);
Желтым отмечен объём пересечения с бассейном, зеленым - объём предмета.
Желтым отмечен объём пересечения с бассейном, зеленым - объём предмета.

Хоть это и простой способ, но у него есть недостаток - предметы несимметричные. И пересечение было зачастую больше, чем объём, погруженный под воду. Это приводило к неправильным расчётам.

Тогда пришлось пойти на крайние меры. Я решил считать этот объём вручную, перебирая все вершины меша. Звучит, конечно, ужасно, но на деле всё более оптимистично. Берем вершины меша:

Positions = MeshComponent->GetStaticMesh()->GetRenderData()->LODResources[0].VertexBuffers.PositionVertexBuffer;

И дальше в цикле проверяем каждую, находится ли она в объёме басика. Если да, то учитываем её при расчёте объёма.

Объём с учётом только тех вершин, которые попали под воду.
Объём с учётом только тех вершин, которые попали под воду.

С этим уже можно работать. Хочу заметить, что для упрощения вычислений всей этой физики, используется не тот меш, который мы видим. А низкополигональный, но повторяющий форму. У него меньше вершин - быстрее считать.

Лоу-поли меш для физики воды
Лоу-поли меш для физики воды

Идея с лоу-поли мешем была отличная, но к сожалению её нигде не применяли, и в расчётах использовались почти всегда обычные хай-поли меши (есть только у уточки и надувного круга, и всё) :(

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

После того как объём найден я применяю к его центру стандартную физоидную силу:

Pushout = PushoutForce * PercentInside * ComponentMass; PrimitiveComponent->AddForceAtLocation(FVector(0, 0, Pushout), PushLocation);

PercentInside я вычисляю как соотношение объёма под водой к объёму меша. Правда, в этом случае я использую первый вариант объёма под водой (который чуть больше). Это приводит к некоторым малозаметным артефактам, но упрощение было продиктовано сроками. PushoutForce - это, по сути, плавучесть предмета.

Делаю Unreal штуки. Бассейн. Часть вторая.

Также я применяю боковую силу на основе вершин, которые НАД водой и с учётом нормалей поверхности воды под этими вершинами.

Инвертированная сумма нормалей поверхности воды под вершинами, умноженная на настройку силы.
Инвертированная сумма нормалей поверхности воды под вершинами, умноженная на настройку силы.

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

Дампинг - это такой параметр затухания движения. Он задается у меша в разделе физики.

Дампинги
Дампинги

Этот параметр замедления применяется постоянно, в каждом кадре. Но для воды так не подходит. Поэтому дампинг надо пересчитывать в каждом тике в соответствии с PercentInside (объём под водой). То есть чем глубже объект, тем больше сопротивления он испытывает.

Кроме того я разделяю вертикальный и XY дампинги. Вертикальный у меня зависит от объёма под водой, а XY нет. Это делает объект приятно плавающим на волнах, но тормозящим при плоском движении по воде.

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

Ну и пару слов о проблемах:

Самая большая проблема в рассинхроне физики и момента применения сил. Если FPS падает ниже 20, то это прям явно видно. Лечить такое надо согласованием тика физики и тика басика (настройка группы тика). Можно попробовать лечить таймером вместо тика.

Выталкивание не дружит с гравитацией

При большом количестве неоптимизированных предметов FPS плачет и рыдает, но сделать ничего не может.

А ведь можно было вставить лоу поли меши и избежатиь проблемы, но кому это надо?

Напоследок скажу, что параметры плавучести, а также коэффициенты дампингов я задавал для каждого объекта свои. Сначала хотел рассчитывать как-то универсально, но понял, что потрачу на это много времени и это того не стоит. Проще было указать, что уточка плавает хорошо, а ящик плохо. И в продакшн.

На этом про басик всё. Удачи!

5
1
2 комментария