Пилю физический движок и автосимулятор на нём.
Кажется, я уже перерос libgdx и он сейчас больше мешает, чем помогает. Кто-то писал что к godot легко прикрутить свой физический движок — жду в комментариях рассказа как это сделать и ссылок с примерами.
Штуки, которых не было из коробки и я писал их сам:
1. Поддержка руля с force feedback через SDL.
2. Привязка звука к объектам в 3д. Подключил miniaudio, теперь право-лево и ближе-дальше нормально слышно.
3. Нет быстрой ECS. (Тут больше претензии к особенностям JVM, чем к libgdx).
В чём проблема с ECS — в языках типа С++ ECS может разложить все компоненты одного типа просто в один массив, доступ к ним будет линейным чтением из памяти, а в случае java объекты раскиданы как попало и на это никак не повлиять.
В итоге у меня получился самописный франкенштейн, в котором почти всё удалось вынести в компоненты, но один тип пришлось оставить в виде поля в Entity. Потому что физический движок очень часто лезет в компонент для состояния физического тела, где хранятся положение, скорость, момент инерции и аккумулятор для сил. Причём доступ в случайном порядке. Например, для добавления силы от пружины надо посмотреть места двух точек крепления у двух разных нет.
Я сделал два эксперимента и в обоих производительность проседала в разы. В итоге я решил что скорость важнее красоты кода и оставил это поле в Entity.
Эксперимент 1: как и для остальных компоненов, просто использовал HashMap[Entity, PhysicsBody] и вместо обращеня к полю лазил туда. Ужасно медленно.
Эксперимент 2: Заменил тип Entity на Int, для PhysicsBody завёл массив, где по индексу лежал нужный объект.
Производительность просела раза в два-три, и кажется JIT стал хуже справляться, т.к. производительность была очень нестабильной и где-то через десяток секунд устаканаливалась на чём-то стабильном, но раза в два-три более медленном чем с доступом к полю в Entity.
В эту неделю попробовал ради эксперимента сделать в игре модель мотоцикла. И это офигенно получилось — почти сразу после начальных настроек он поехал. Причём, как и настоящий мотоцикл, на скорости он был устойчивый, а рулить надо было контррулением. Это достигается за счёт угла наклона вилки и трейла.
В целом это очень круто и значит, что мой физический движок вполне нормально работает и его можно использовать для чего-то серьёзного. Кажется, что он нормально обрабатывает штуки типа прецессии вращающихся тел, которую аналитически в случае с кучей тел фиг посчитаешь.
Тут могло бы быть видео, но форум не даёт его загрузить файлом.
Проблемы, которые я не решил (и почему хочу перейти к чему-то типа Godot/Unity/UE):
В libgdx нет редактора объектов. Я не могу просто взять сцену, накидать на неё компонентов колес, пружин, амортизаторов, двигателя, коробки передач, дифференциала и в редакторе движка собрать из них транспортное средство. И ассета из этой штуки я тоже не сделаю.
Вместо этого приходится описывать все взаимосвязи кодом, получается очень долго. Вдобавок приходится отвлекаться на всякие штуки типа звука, ввода-вывода, графики, логики движения камеры и т.п. Сейчас это всё самописное, хотелось бы использовать что-то готовое и не отвлекаться.
В качестве эксперимента накидал плоскую трассу из линий. Вообще мне понравился такой подход — не надо думать про текстуры и тратить время на детализацию. Можно просто накидать контуры в Blender и закинуть в движок. Этого достаточно, чтобы как-то оценить управляемость машины и сопоставить размеры моделей. Вся карта в obj весит около 50 килобайт и легко парсится самодельным парсером. Стандартный парсер obj от libgdx ожидает увидеть полигоны вместо линий и эти файлики не переваривает.
Попробовал загрузить ресурсы из Ассето Корсы. Формат хранения у них бинарный, но довольно простой и на гитхабе уже есть код, который его парсит: github.com/RaduMC/kn5-converter/blob/master/kn5%20converter/Program.cs
Осталось только под свой язык программирования переписать.
Если кратко по формату и организации сцены в формате kn5 — сцена состоит из дерева нодов. У каждой ноды есть имя и список дочерних. Видов нод три — нода с матрицей преобразования, нода с мешем и нода с анимированным мешем. В файлах попадаются пустые ноды — кажется, их ещё используют в качестве зашлушек, чтобы потом по этой позиции добавить колесо или ещё что-то.
Я пока в своём минималистичном стиле нарисовал только линии, без полигонов, каждую ноду своим рандомным цветом. Но кажется у меня какая-то ошибка в индексах — куски земли как-то очень плотно залиты линиями и треугольников не видно.
Так же прямо внутри хранятся текстуры в dds и какие-то параметры материалов для шейдеров. Но сами шейдеры хз где.
Поправил загрузку, добавил текстуры.
Много времени потратил на борьбу с libgdx. На мой взгляд в этом движке много оверинжиниринга и какие-то простые вещи делаются сложно, а вдобавок чтобы использовать libgdx, надо знать и как работает openGL, и как работают велосипеды в движке поверх него. А для чего-то сложного движок слабо подходит, например поддержки загрузки dds текстур в движке нет
. Я нашёл какую-то внешнюю библиотеку (gdx-dds), но она на попытке загрузки каких-то текстур кидается исключениями. В итоге я вместо вызова «загрузить текстуру» трачу время.
На мой взгляд пример оверинжиниринга: RenderContext.java — чтобы его использовать, надо целиком прочитать его код, и потом понять что под капотом он что-то запоминает и как-то вызывает openGL
Больше всего я сгорел с бага с текстурными координатами. Попробуйте найти ошибку:
Штуки, которых не было из коробки и я писал их сам:
1. Поддержка руля с force feedback через SDL.
2. Привязка звука к объектам в 3д. Подключил miniaudio, теперь право-лево и ближе-дальше нормально слышно.
3. Нет быстрой ECS. (Тут больше претензии к особенностям JVM, чем к libgdx).
В чём проблема с ECS — в языках типа С++ ECS может разложить все компоненты одного типа просто в один массив, доступ к ним будет линейным чтением из памяти, а в случае java объекты раскиданы как попало и на это никак не повлиять.
В итоге у меня получился самописный франкенштейн, в котором почти всё удалось вынести в компоненты, но один тип пришлось оставить в виде поля в Entity. Потому что физический движок очень часто лезет в компонент для состояния физического тела, где хранятся положение, скорость, момент инерции и аккумулятор для сил. Причём доступ в случайном порядке. Например, для добавления силы от пружины надо посмотреть места двух точек крепления у двух разных нет.
Я сделал два эксперимента и в обоих производительность проседала в разы. В итоге я решил что скорость важнее красоты кода и оставил это поле в Entity.
Эксперимент 1: как и для остальных компоненов, просто использовал HashMap[Entity, PhysicsBody] и вместо обращеня к полю лазил туда. Ужасно медленно.
Эксперимент 2: Заменил тип Entity на Int, для PhysicsBody завёл массив, где по индексу лежал нужный объект.
Производительность просела раза в два-три, и кажется JIT стал хуже справляться, т.к. производительность была очень нестабильной и где-то через десяток секунд устаканаливалась на чём-то стабильном, но раза в два-три более медленном чем с доступом к полю в Entity.
В эту неделю попробовал ради эксперимента сделать в игре модель мотоцикла. И это офигенно получилось — почти сразу после начальных настроек он поехал. Причём, как и настоящий мотоцикл, на скорости он был устойчивый, а рулить надо было контррулением. Это достигается за счёт угла наклона вилки и трейла.
В целом это очень круто и значит, что мой физический движок вполне нормально работает и его можно использовать для чего-то серьёзного. Кажется, что он нормально обрабатывает штуки типа прецессии вращающихся тел, которую аналитически в случае с кучей тел фиг посчитаешь.
Тут могло бы быть видео, но форум не даёт его загрузить файлом.
Проблемы, которые я не решил (и почему хочу перейти к чему-то типа Godot/Unity/UE):
В libgdx нет редактора объектов. Я не могу просто взять сцену, накидать на неё компонентов колес, пружин, амортизаторов, двигателя, коробки передач, дифференциала и в редакторе движка собрать из них транспортное средство. И ассета из этой штуки я тоже не сделаю.
Вместо этого приходится описывать все взаимосвязи кодом, получается очень долго. Вдобавок приходится отвлекаться на всякие штуки типа звука, ввода-вывода, графики, логики движения камеры и т.п. Сейчас это всё самописное, хотелось бы использовать что-то готовое и не отвлекаться.
UPD 2024-12-03
В качестве эксперимента накидал плоскую трассу из линий. Вообще мне понравился такой подход — не надо думать про текстуры и тратить время на детализацию. Можно просто накидать контуры в Blender и закинуть в движок. Этого достаточно, чтобы как-то оценить управляемость машины и сопоставить размеры моделей. Вся карта в obj весит около 50 килобайт и легко парсится самодельным парсером. Стандартный парсер obj от libgdx ожидает увидеть полигоны вместо линий и эти файлики не переваривает.
UPD 2024-12-04
Попробовал загрузить ресурсы из Ассето Корсы. Формат хранения у них бинарный, но довольно простой и на гитхабе уже есть код, который его парсит: github.com/RaduMC/kn5-converter/blob/master/kn5%20converter/Program.cs
Осталось только под свой язык программирования переписать.
Если кратко по формату и организации сцены в формате kn5 — сцена состоит из дерева нодов. У каждой ноды есть имя и список дочерних. Видов нод три — нода с матрицей преобразования, нода с мешем и нода с анимированным мешем. В файлах попадаются пустые ноды — кажется, их ещё используют в качестве зашлушек, чтобы потом по этой позиции добавить колесо или ещё что-то.
Я пока в своём минималистичном стиле нарисовал только линии, без полигонов, каждую ноду своим рандомным цветом. Но кажется у меня какая-то ошибка в индексах — куски земли как-то очень плотно залиты линиями и треугольников не видно.
Так же прямо внутри хранятся текстуры в dds и какие-то параметры материалов для шейдеров. Но сами шейдеры хз где.
UPD 2024-12-13
Поправил загрузку, добавил текстуры.
Много времени потратил на борьбу с libgdx. На мой взгляд в этом движке много оверинжиниринга и какие-то простые вещи делаются сложно, а вдобавок чтобы использовать libgdx, надо знать и как работает openGL, и как работают велосипеды в движке поверх него. А для чего-то сложного движок слабо подходит, например поддержки загрузки dds текстур в движке нет
. Я нашёл какую-то внешнюю библиотеку (gdx-dds), но она на попытке загрузки каких-то текстур кидается исключениями. В итоге я вместо вызова «загрузить текстуру» трачу время.
На мой взгляд пример оверинжиниринга: RenderContext.java — чтобы его использовать, надо целиком прочитать его код, и потом понять что под капотом он что-то запоминает и как-то вызывает openGL
Больше всего я сгорел с бага с текстурными координатами. Попробуйте найти ошибку:
val mesh = new Mesh(
true,
node.verticesData.length,
node.indices.length,
new VertexAttribute(VertexAttributes.Usage.Position, 3, ShaderProgram.POSITION_ATTRIBUTE),
new VertexAttribute(VertexAttributes.Usage.Normal, 3, ShaderProgram.NORMAL_ATTRIBUTE),
new VertexAttribute(VertexAttributes.Usage.TextureCoordinates, 2, ShaderProgram.TEXCOORD_ATTRIBUTE),
new VertexAttribute(VertexAttributes.Usage.Tangent, 3, ShaderProgram.TANGENT_ATTRIBUTE),
)
Ошибка в том, что для текстурный хоординат должно быть такShaderProgram.TEXCOORD_ATTRIBUTE + "0"
В движке ни намёка на то, что текстурные координаты должны быть с нулём или ещё какой-то цифрой в конце.
Потом это через виртуализацию разруливается на калсайте. Да, сдеградируешь на вызовах функций, но зато выигрываешь по плотности укладки в памяти.
Если совсем с ума сойти, можно ваще сделать «value types у нас дома» через VarHandle + byte[]. Производительность должна быть на уровне, тк JIT потом это всё вклеивает нормально.
конечно, это крайне костыльно, потому что JVM:(
Кстати насчёт кодогенерации и прочих ухищрений — в скале есть макросы и inline функции, можно с их помощью что-то навертеть.
Интересен именно момент, когда один мотоциклист пинает другого, влияя на стабильность аппарата на высокой скорости
Вообще тема мотоциклов мне интересна, но без вестибулярного аппарата и на гоночном круглом руле управлемость как у пьяного самокатчика. Это совсем не то, что я чувствовал на реальном мотоциле.
Может быть, что-то такое можно сделать, если как в игральных автоматах поставить модель мотоцикла, сделать мощный и точный большой руль с force feedback и ещё какие-нибудь приводы для наклона мотоцикла влево-вправо (тоже с force feedback, чтобы чувствовать баланс веса игрока). Потому что мотоцикл рулится контррулением и это очень требовательно к точности и скорости реакции руля.