Делаю демо с автомобилем и корректной физикой #3

TLDR: Я подключил руль и разобрался с тем, как использовать force feedback из своего кода.

В реальности этот путь включал в себя целую кучу приключений.

Сначала я заказал руль Thrustmaster T300 и дождался когда его привезут. Потом сел играть в Dirt Rally 2.0 и Assetto Corsa и пожалел, что не купил руль раньше. Играть определённо понравилось. У меня был такой же руль раньше, но полтора года назад он сгорел и я почему-то откладывал покупку на потом.

Делаю демо с автомобилем и корректной физикой #3

Позвал в гости знакомого с Oculus Quest 2 и мы попробовали играть в VR. Впечатления неоднозначные.

Плюсы:

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

  2. Кажется, я так лучше чувствовал заносы, потому что ледяной участок трассы в Dirt Rally мне удалось проехать заметно быстрее, чем без VR. Но тут я не могу с уверенностью утверждать, потому что я после этого попробовал без VR нажимать на газ аккуратнее и вроде как тоже начал ехать быстрее.

  3. В AC я сел в миату и был очень приятно впечатлён. Приборная панель, рычаг переключения передач, чёрные кожанные сиденья с красной строчкой - всё как в настоящем авто. Если хочется посидеть в красивой машине - нужен VR :)

  4. C настройкой VR проблем почти не было. Наверно, потому что разработчики игр заранее об этом позаботились. Тормозов я тоже не заметил.

  5. Видеопоток жмётся на видеокарте и отправляется на очки либо по usb 3.0, либо через wifi. Видеоразъём не нужен. Я не знал что так можно, подозреваю что из-за этого сильно страдало качество картинки, но с точки зрения меня как пользователя подключение очень простое и удобное.

Минусы

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

  2. Очень не хватает разрешения. Когда 1600 пикселей растягиваются на всю ширину обзора, оказывается что даже текст на кнопке прочитать сложно, не говоря уж о повороте вдали на трассе. Я ехал как ёжик в тумане, и если на медленных поворотах со льдом это не было проблемой, то на быстрых асфальтовых участках было плохо.

  3. Почему-то места с тенями выглядели как совсем тёмные и без деталей, а светлые - как пересвеченные пятна. Возможно, надо было ещё сильнее покрутить настройки гаммы и яркости, но я не смог добиться хорошей картинки, хотя на обычном мониторе всё выглядит шикарно. Возможно, тут ещё виновато сжатие видео.

  4. В играх иногда сбивается положение "центра", в итоге можно оказаться на заднем сидении авто или в VR видеть руль не там, где он в реальности.

  5. Если VR очки хоть чуть-чуть сдвигались на голове, картинка становилась очень нечёткой. Приходилось постоянно их поправлять или придерживать рукой

В целом - попробовать стоит, но постоянно играть я бы вряд ли так захотел. В идеале бы разрешение картинки поднять до честных 4к на глаз (как у Эппловских очков, например).

Вернёмся к разработке игры.

В линуксе force feedback сам по себе не заработал. Руль видно как джойстик с осями, но без фидбека. Есть драйвер на гитхабе, с ним всё заработало.

Для общения с рулём есть библиотека SDL (Simple DirectMedia Layer) с интерфейсом на Си. Я так понимаю, все её и используют, находил какие-то плагины для Unity и годота.

В SDL есть три отдельных подсистемы - Joystick, Controller и Haptic.

Joystick - низкоуровневый интерфейс джойстика, по нему можно узнать сколько кнопок, сколько осей, какие у них положения и т.п. Руль c педалями или коробка передач - тоже джойстики.

Controller - высокоуровневый интерфейс для более-менее стандартных контроллеров с множеством всяких консольных кнопок типа A, B, курков и т.п. Чтобы узнать, какой номер кнопки чему соответствует, используется описание из файлика типа gamecontrollerdb.txt

Рулей в этом списке нет. При желании можно руль туда добавить, но не нужно. И в целом подсистема с контроллерами для руля не пригодится, не тратье на неё время.

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

Причём Haptic-девайс, это отдельный указатель, который можно получить из указателя на джойстик.

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

Поскольку моё демо написано на Scala и работает на JVM, понадобилась обёртка для SDL. В libgdx есть такая - но она работает только с контроллерами. Там стоит проверка, что если джойстика нет в списке для контроллеров, то его вообще за девайс не считают и даже не показывают. При желании руль можно добавить в список контроллеров и его увидеть, но haptic подсистемы там в принципе не будет.

Ещё есть альтернативная имплементация и там есть джойстики, но по-факту её четыре года не обновляли и haptic там тоже нет.

Я уже отчаялся и хотел доделывать одну из этих библиотек под себя, но потом наткнулся на просто java-обёртку для всего SDL, попробовал и она заработала. libsdl4j

Единственный неочевидный момент - в SDL для эффекта используется union, в котором могут лежать разные "структуры" и есть поле type, которое говорит что же из union надо доставать. В java-обёртке вылезают какие-то пограничные случаи, и надо этот тип указать два раза - в `setType(...)` и потом в `constant.type = ...`

val effect = SDL_HapticEffect() effect.setType(SDL_HapticEffectType.SDL_HAPTIC_CONSTANT) effect.constant.`type` = SDL_HapticEffectType.SDL_HAPTIC_CONSTANT effect.constant.direction.`type` = SDL_HapticDirectionEncoding.SDL_HAPTIC_STEERING_AXIS effect.constant.direction.dir(0) = 1 effect.constant.length = effectLengthMs effect.constant.attackLength = 0 effect.constant.fadeLength = 0 effect.constant.level = (force * 0x7FFF.toDouble).toShort val effectId = SdlHaptic.SDL_HapticNewEffect(haptic, effect) SdlHaptic.SDL_HapticRunEffect(haptic, effectId, iterations) ... SdlHaptic.SDL_HapticDestroyEffect(haptic, effectId)

Ещё в процессе написания этого кода мне сильно помог chatgpt - он сразу выдавал куда более подробные и понятные примеры кода для SDL, чем я находил в гугле. Но местами врал - например, level это signed short и максимальным значением является 0x7FFF, а gpt предлагал использовать 0xFFFF. И самой собой примеры были на Си.

Когда со всем этим разобрался, попробовал управлять рулём из кода игры. С помощью силы "привязал" руль пружиной к рулевой рейке внутри движка. И это прикольно, можно почувствовать, что происходит с колёсами. Например, на большой скорости руль тяжелеет, а при сносе передних колёс выпрямляющее усилие на руле уменьшается.

Не скажу что руль вёл себя как-то реалистично. Нужна ещё куча настроек и доделок. Но обратная связь на руле и возможность его поворачивать на любой угол и нажимать педали с произвольным усилием - это офигенно.

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

Ещё чисто ради эксперимента я попробовал использовать эффект с силой по синусоиде. (Примерно как игровые джойстики умеют жужжать в руках). В качестве частоты синусоиды передавал частоту вращения двигателя. И при нажатии на газ увеличивал силу колебаний. На низких оборотах (около 1000-2000 оборотов в минуту, 15-30 в секунду) руль вполне себе справляется и прикольно дрожит. На высоких руль пытается (я пробовал до 7500 оборотов), но видимо колебания становятся маленькими и приводные ремни в руле это всё демпфируют - с ростом частоты ощущения на руле пропадают, но начинает немножко дрожать стол и издавать звуки жужжащего моторчика сам руль.

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

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

Как я до этого догадался? Попробовал тронуться, плавно отпуская сцепление и добавляя газ. И заподозрил, что больно уж не похоже на реальность.

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

Ещё я ради интереса поэкспериментировал в разных играх и начал замечать кривые моменты.

  • Forza horizon 4 - руль на занос реагирует как будто с задержкой, если заехать левыми или правыми колёсами на обочину и нажать газ или затормозить - машину не развернёт, она просто едет прямо.
  • Dirt Rally 2.0 - если нажать на газ, обороты резко подскакивают на примерно тысячу оборотов, отпустить - резко падают. Как будто шины сильно мягче чем в реальности и при любом нажатии на газ начинают очень ощутимо проскальзывать. И реакции на руле очень мягкие, чтобы повернуть надо прям размахивать рулём даже на асфальте. Эти эффекты нормальные, но они должны быть сильно меньше.
  • Assetto corsa - я залез в ресурсы игры посмотреть, как у них считается ускорение при нажатии на педаль газа. У них есть две таблички - одна для максимального крутящего момента и вторая, которая чуть нелинейно отображает положение педали в крутящий момент. (Условно, там при 10% педали получается сразу 30% от крутящего момента, а дальше рост всё слабее). Если честно, я ожидал увидеть у них 2д табличку типа "момент в зависомости от оборотов и от положения педали газа". В такой табличке для атмосферного двигателя можно было бы закодировать кучу интересных эффектов, но её нет :( Наверно, это самая реалистичная игра из тех, до которых я добрался, было неожиданно увидеть такое упрощение внутри.

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

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

22
3 комментария

«Если хочется посидеть в красивой машине - нужен VR»

Может быть, нужна реальная красивая машина, а не VR? А то так сначала и до красивых женщин в VR можно дойти, а потом и до красивой еды 😊

1

о, привет водителям! в моей игре тоже есть поддержка рулей и т.д, но это все попроще оформлено (головой вертеть нельзя и вибрация зависит чисто от скорости)

По минусам.
1. Укачивание проходит тем быстрее, чем больше играешь в динамичные игры.
2. Разрешения хватает, если выставить сглаживание повыше. Запуская через СтимВР можно рендерить картинку в разы выше, чем разрешение шлема. Хз что там с окулусовским приложением. Но для симуляторов таки лучше стимвр нативные шлемы брать, потому что все равно сидишь - кабель не мешает.
3. Тут все от игры зависит.
4. Значит шлему недостаточно маркеров для нормального отслеживания. На шлемах с базовыми станциями таких проблем нет, там отслеживание абсолютное.
5. Решается нормальным креплением.