Аэродинамика в Unity ч.1

Немного расскажу о личном опыте разработки в Unity.

В данный момент я работаю над своим первым проектом. Это будет аркадный авиасимулятор в стиле low poly. А что самое главное в авиасимуляторе? Полёт, разумеется!

Работу я начал со знакомства с азами аэродинамики. Четыре силы удерживают тело в пространстве — звучит достаточно просто!

Аэродинамика в Unity ч.1

Сила тяжести в Unity уже есть. Сообщить телу силу тяги - вопрос двух строчек кода. Сила сопротивления и подъемная сила уже выглядят посерьезнее, но вряд ли там будет что-то сложное.

Аэродинамика в Unity ч.1

Подготовка

Чтобы самолёт полетел, нужен для начала сам самолёт. Можно было, конечно, заставить летать обычный куб. Но я решил немного разукрасить процесс разработки лично для себя.

В Вlender я собрал такое вот чудо.
В Вlender я собрал такое вот чудо.

Самолет имеется, но пока это просто моделька. Нужно, чтобы он обрел физические характеристики. Для этого в Unity есть компонент Rigidbody— его необходимо добавить к игровому объекту самолета.

Также необходимо, чтобы столкновения с другими объектами выполнялись согласно геометрии самолёта. В этом нам поможет компонент Mesh Collider — это коллайдер, который состоит из сетки, то есть чистой 3D модели.

Тут есть нюанс. Если игровой объект обладает Rigidbody, то Mesh Collider у него обязательно должен быть выпуклый — это значит, что отрезок, соединяющий любые две вершины модели, должен полностью лежать внутри него.

Я об этом вначале не знал, и в качестве мэша добавил модель корпуса самолета, вместе со стабилизаторами и крыльями. Unity умеет преобразовать такую сетку к выпуклой, но в результате получится нечто невразумительное:

Если оставить такую сетку, то самолёт будет биться о препятствия невидимыми гранями, а это испортит ощущение игрового процесса.
Если оставить такую сетку, то самолёт будет биться о препятствия невидимыми гранями, а это испортит ощущение игрового процесса.

Поэтому я сделал простенькую модель физического каркаса самолета, которая удовлетворяет условиям выпуклости.

Сетка в Blender
Сетка в Blender
Отображение Mesh Collider объекта в сцене
Отображение Mesh Collider объекта в сцене

Самолёт не взлетит, пока не наберёт скорость. Для этого у него должна быть возможность передвигаться по плоскости. Иными словами — нужно приделать ему функциональное шасси. Здесь пригодится компонент Wheel Collider. Я создал внутри объекта самолёта три пустых объекта, и каждому из них назначил этот компонент.

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

Реализация

Осталось самое интересное. Заставить объект, который тяжелее воздуха парить в небе! Функции, описанные мной здесь, — это мой первый подход. С того момента я сильно доработал код, но для соблюдения логики повествования от простого к сложному я привожу именно первую итерацию.

В коде ниже, используются параметры state и data

AircraftControlState state — хранит информацию о состоянии элементов управления. Например, текущая мощность двигателя.

AircraftData data — хранит информацию о характеристиках самолета. Таких как площадь крыла или площадь лобового сопротивления.

Тяга

Это сила, которую телу передаёт двигатель, она толкает самолёт вперед

private void ThrustForce_calculate(AircraftControlState controlState, AircraftData data) { //Рассчитываем вектор силы thrustForce = transform.forward * controlState.currentThrust; //Применяем вектор силы rigidBody.AddForce(thrustForce); }
Жёлтый вектор — сила тяги. Синий вектор — скорость.

Сила сопротивления

Сила сопротивления растёт со скоростью и в упрощённом варианте зависит только от площади профиля самолёта.

private void DragForce_calculate(AircraftControlState state, AircraftData data) { // Получаем направление вектора скорости тела velocity = rigidBody.linearVelocity; // Вектор силы сопротивления - обратный к вектору скорости Vector3 dragForceDir = velocity.normalized * -1; //Функция airDragMagnitude приведена ниже. dragForce = dragForceDir * airDragMagnitude(velocity.magnitude, data.dragArea); //Применяем вектор силы сопротивления rigidBody.AddForce(dragForce); }
//Упрощенная формула силы сопротивления //D = 1/2 * [Плотность среды] * [Квадрат скорости] * [Площадь сопротивления] private float airDragMagnitude(float velocity, float dragArea) { float result = 0.5f * airDensity * velocity * velocity * dragArea; return result; }
Красный вектор — сила сопротивления

Подъемная сила

Последнее, что осталось, — это применить подъемную силу.

private void LiftForce_calculate(AircraftControlState state, AircraftData data) { //Vector3.Cross(velocity, transform.right) рассчитывает вектор //перпендикулярный к плоскости, которая образована //направлением скорости и крылом Vector3 liftForceDir = Vector3.Cross(velocity, transform.right).normalized; //Подъемную силу мы получаем почти также как силу сопротивления, //но добавляем коэффициент крыла. //Это абстрактный коэффициент, который отвечает за эффективность крыла //(Сейчас эта штука считается иначе. Но сперва было так) liftForce = liftForceDir * airDragMagnitude(velocity.magnitude, data.wingArea) * data.CL; rigidBody.AddForce(liftForce); }

Итак, я применил поочередно эти три силы к телу (силу тяжести специально не нужно применять, она работает из коробки), настроил площадь крыла и площадь лобового сопротивления так, чтобы самолёт взлетал плюс/минус реалистично.

Самолёт уже летит. Полёт, конечное, выглядит неестественно, но уже есть с чем работать.

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

А в своём телеграм канале я регулярно делюсь прогрессом.

5
1
Начать дискуссию