Тюнинг машины для достижения лучших результатов
Привет всем :) В этой статье мы попытаемся разобраться, как можно изменять параметры существующих моделей в диптауне, для достижения нужного нам эффекта. Делать мы это будем, как вы уже наверное догадались, на примере демосцены с машиной. Те кто уже имел удовольствие поиграть с текущей версией демки, могли заментить, что машина ведет себя как то странно, если не сказать плохо. Она плохо управляется и сильно скользит по поверхности (язык не поворачивается сказать "по дороге"). Мы попробуем разобраться в чем дело, и настроить параметры таким образом, чтобы наша модель была больше похожа на настоящую машину, а не игрушечную машинку, ведущую себя как корова на льду.
Для работы нам понадобится устновленная версия Deeptown SDK, а так же любой текстовый редактор, позволяющий изменять текстовые файлы в UNIX формате. В windows такими редакторами являются wordpad и, конечно, встроенный редактор в FAR Manager-е.
Содержание |
Постановка задачи
Приведем здесь список проблем, которые были выяснены в ходе "эксплуатации" модели:
- Машина медленно разгоняется и очень плохо тормозит
- На больших скоростях заметно хуже управляется
- Опять же, очень сильно заносит и при больших скоростях
- При резком развороте задним ходом, колеса выворачиваются в противоположную сторону
- Резкий разворот при движении вперед практически невозможно сделать; машина просто поворачивает по большой дуге
Если присмотреться к передним колесам машины в момент старта с места, то можно заметить, что почти сразу начинают проскальзывать, и сцепления с поверхностью практически нет. То же самое происходит и при попытке торможения движением в обратную сторону — колеса проворачиваются и не оказывают заметного эффекта.
Настройка трения
Для начала, рекомендую вам обратиться к соответствующей документации, для того чтобы лучше понимать что мы будем делать.
Собственно, решение проблемы кроется в настройке коэффициента трения между колесами и поверхностью. Для этого, заходим в файл описания модели машины, расположенный по адресу Media/storage/media/models/test.model.xml. Нас интересуют секции описания колес: <source lang="xml"> <body name="wheel-fl">
<friction value="1000" /> <mass_data> <mass value="25" /> </mass_data> <geometry> <geom type="cylinder" radius="0.337575" length="0.228778"> <rotation w="0.707106781186548" x="0" y="0.707106781186547" z="0" /> </geom> </geometry> <surfaces> <mesh name="wheel-fl" node="/media/meshes/t01.mesh" /> </surfaces> <position x="0.724081" y="-0.467315" z="1.28295" />
</body> </source>
Находим параметр friction и увеличиваем его где-то до 1600. Не забывайте, что менять этот параметр надо для всех четырех колес! Это значение было определено мной экспериментальным путем. Меньшие значения приводят к тому что машина сильно скользит как по льду, бо'льшие значения улучшают управляемость до определенного уровня, а затем колеса просто перестают вращаться :) Связано это с тем, что двигатель уже не может провернуть колеса с большим трением. Поскольку поведение по умолчанию нас не очень устраивает, придется так же настроить мощность двигателя. Тюнить так тюнить! :)
Настройка двигателя
Двигатель описан в том же файле, чуть ниже. Ищем секцию motor и смотрим: <source lang="xml"> <motor name="engine" url="joint_speed:j-fl/1,j-fr/1">
<limit param="speed" lo="-180" hi="180" /> <limit param="force" lo="0" hi="600" />
</motor> </source>
Любой двигатель описывается несколькими параметрами, которые непосредственно влияют на его поведение. Первый параметр, это url связанных с двигателем суставов, на которые он воздействует (в данном случае — крутит; бывают и другие типы двигателей). В нашем случае, двигатель с именем engine воздействует на 2 сустава: "j-fl/1" "j-fr/1". Суставы описаны выше по тексту. Единички в идентификаторе сустава обозначают оси, которые нам нужны. У суставов передних колес по две оси: одна является собственно осью колеса (оно вращается по этой оси), а другая ось проходит перпендикулярно первой — это уже рулевое управление. В случае основного мотора, нам конечно нужна основная ось колеса.
Еще у моторов есть ограничительные параметры: диапазоны изменения скоростей и прикладываемой силы — speed и force соответственно. Примером высокоскоростного, но слабого мотора может послужить двигатель бор машинки (брр); примером медленного, но сильного — двигатель в лифте или эскалаторе. А может быть и то и другое, зависит от места применения конкретного мотора. Эти параметры вводятся для того, чтобы исключить возможность "силового мошенничества" в основном пространстве Диптауна (чтобы не плодить "суперменов").
В общем, нам нужно увеличить верхнюю границу силы нашего мотора, до величины порядка 1200. Важно понимать, что ставить заоблачные значения силы бесполезно — колеса просто будут вращаться на месте, не оказывая воздействия.
Настройка рулевого управления
Теперь попробуем решить проблему с рулевым управлением. Напомню, она проявляется на больших скоростях, либо при попытке сделать "полицейский разворот", это когда машина разворачивается на 180 градусов практически на месте. Для этого нужно начать двигаться задним ходом, а затем резко вывернуть руль. Проблема в том, что при попытке совершить такой разворот, колеса выворачиваются в обратную сторону и иногда даже застревают в таком положении. Иногда бывает даже, что колеса машины не получается повернуть до тех пор, пока мы не начнем движение (те кто ездил на советских машинах, меня поймут ;)
Все упирается опять же, в силу мотора, но в этот раз уже поворачивающего колеса. Проблемы управляемости на больших скоростях являются следствием возникающего гироскопического эффекта в колесах (да да, физика тут на уровне :). Задумайтесь: колесо весит 25 кг. При вращении с большими скоростями, оно начинает вести себя как настоящий гироскоп (инертность то большая!), в результате оно препятствует изменениям оси своего вращения (что происходит при повоторах). Чем резче поворот, тем больше колесо сопротивляется.
Решить эту проблему можно либо уменьшив вес колес, либо увеличив силу мотора поворачивающего колеса. Я пошел по второму пути. Принцип тот же, что и в предыдущем случае. Ищем описание мотора с именем turn (повотор) и увеличиваем его силу. <source lang="xml"> <motor name="turn" url="joint_speed:j-fl/0,j-fr/0">
<limit param="speed" lo="-1" hi="1" /> <limit param="force" lo="0" hi="300" /> <limit param="position" lo="-0.6" hi="0.6" />
</motor> </source>
Не долго думая, меняем значение с 300 на 3000. Ничего страшного, что наш "гидроусилитель" получился сильнее основного мотора машины :) Рычаг то тут больше.
В принципе, уже можно попробовать применить наши изменения и посмотреть что получилось. Для этого нам нужно сконвертировать XML описание модели в понятный движку диптауна бинарный формат BXL. Это делает утилита bxd, входящая в стандартный набор утилит Deeptown SDK. Для ее использования надо открыть консоль вашей ОС (в windows это Пуск->Выполнить->cmd.exe, хотя лучше использовать FAR), затем перейти в директорию models и ввести команду:
bxd test.model.xml -f test.model
Если все пройдет успешно, то программа закончит работу и завершится без дополнительных сообщений (это традиционное поведение UNIX). Выводиться будут только сообщения об ошибках.
Примечание: Утилита умеет выполнять и обратную операцию. Более подробно это описано в краткой справке к программе, которую можно вызвать, введя команду:
bxd --help
Теперь можно попробовать запустить дип и покататься на обновленной машине :) Разница должна быть заметна сразу. Если что-то не так, перепроверьте ваши изменения, возможно где-то ошибка. Проверьте так же, действительно ли файл модели сконвертировался успешно.
Тормоза и ручник
Наконец, попробуем пойти еще дальше и приделать к нашему болиду нормальные тормоза. Для начала, ограничимся режимом экстренного торможения, когда все колеса машины блокируются полностью. Эта операция будет несколько сложнее чем предыдущие, потому что мы будем добавлять новые секции в файл описания модели и править ее скрипт.
В настоящий момент, все скрипты пишутся на языке K++. В принципе, этот язык довольно понятный, особенно людям, знакомым с Си подобными языками, вроде JavaScript или PHP. При возникновении вопросов, обращайтесь к документации, либо на форум.
Для начала, рассмотрим существующий скрипт и прокомментируем основные части. Для экономии места и вашего внимания, здесь я привожу только интересующие нас места: <source lang="kpp" line=1> class Bonnet extends WorldObject {
var Motor m_engine; var Motor m_turn; var int m_engine_start_count; var int m_turn_start_count;
public:
function void State_init() { //... m_engine = this.getMotor('engine'); m_turn = this.getMotor('turn'); //... } function void startEngine(const real speed, const real force) { if(!m_engine.isEnabled()) { m_engine.setParam('speed', speed); m_engine.setParam('force', force); m_engine.enable(); } ++m_engine_start_count; }
function void stopEngine() { if(--m_engine_start_count == 0) m_engine.disable(); }
function void startTurn(const real speed, const real force) { if(!m_turn.isEnabled()) { m_turn.setParam('speed', speed); m_turn.setParam('force', force); m_turn.enable(); } ++m_turn_start_count; }
function void stopTurn() { if(--m_turn_start_count == 0) m_turn.disable(); } function void Control_stop_engine(const bytea arg) { stopEngine(); } function void Control_forward (const bytea arg) { startEngine(180, 600); } function void Control_backward (const bytea arg) { startEngine(-180, 600); } function void Control_break (const bytea arg) { startEngine(0, 600); } function void Control_stop_turn (const bytea arg) { stopTurn(); } function void Control_left (const bytea arg) { startTurn(-1, 300); } function void Control_right (const bytea arg) { startTurn(1, 300); }
</source>
- 2-5
- В теле класса Bonnet объявляются четыре поля (переменных): два мотора и два счетчика. Счетчики можно понимать просто как числа, в то время как моторы являются объектами. Ниже, в них мы положим реальные объекты, соответствующие двум моторам нашей модели.
- 7-12
- Метод State_init() вызывается однократно, при загрузке модели и служит для выполнения различных операций инициализации. В нашем случае мы присваиваем переменным m_engine и m_turn объекты моторов, которые получаем по имени. Это те самые имена, которые были описаны в файле модели.
- 14-23
- Объявляются два метода startEngine() и stopEngine() которые устанавливают желаемый режим работы, а так же включают и выключают двигатель. Когда двигатель "включен", то он воздействует на ассоциированные с ним объекты; когда выключен — нет. Ну это я думаю и так понятно :)
- 25-43
- В этих строках делается по сути то же самое, но уже для мотора рулевого управления.
- 36-42
- А здесь объявляется кучка методов, которые обрабатывают ввод пользователя (нажатия клавиш) и вызывают соответствующие методы скрипта. Чтобы понять как это работает, надо открыть файл Media/storage/etc/world/input.conf:
# Input context for local physics tests context local_control # Send event on key press on key_press check(key=LEFT) do send_event:left on key_press check(key=RIGHT) do send_event:right on key_press check(key=UP) do send_event:forward on key_press check(key=DOWN) do send_event:backward on key_press check(key=SPACE) do send_event:break on key_release check(key=LEFT) do send_event:stop_turn on key_release check(key=RIGHT) do send_event:stop_turn on key_release check(key=UP) do send_event:stop_engine on key_release check(key=DOWN) do send_event:stop_engine on key_release check(key=SPACE) do send_event:stop_engine
- Здесь объявлены привязки конкретных клавиш к событиям ввода. Работает это так: когда движок ввода обнаруживает, что пользователь нажал клавишу, например кнопку ВВЕРХ, то он смотрит у себя в таблицах и находит действие "send_event:forward", что в конечном счете приведет к тому, что в скрипте управляемой модели будет вызван метод Control_forward(). Префикс "Control_" приписывается автоматически.
Однако, ближе к делу. Для того чтобы сделать полноценные тормоза, там надо добавить в модель еще один мотор, который будет противодействовать движению машины, а так же реализовать соответствующий интерфейс в скрипте, управляющий этми мотором.
Опять же, открываем файл Media/storage/media/models/test.model.xml и добавляем в него следующую секцию, рядом с существующими моторами: <source lang="kpp"> <motor name="breaks" url="joint_speed:j-fl/1,j-fr/1,j-bl/0,j-br/0">
<limit param="speed" lo="-1" hi="1" /> <limit param="force" lo="0" hi="100000" />
</motor> </source>
Обратите внимание, что этот мотор действует на все четыре колеса; соответственно, в его параметре url прописаны все 4 сустава колес. В случае суставов задних колес, наличествует только одна ось, поэтому указан иднекс 0. При экстренном торможении, на колеса приходится очень большая нагрузка, поэтому сила противодействия тоже должна быть приличной. Соответственно указывается большое значение силы.
Принцип работы такой: когда требуется затормозить, мы включаем мотор "breaks", с помощью метода enable(), установив желаемую скорость в 0. Таким образом, мотор будет очень сильно стараться остановить вращение оси, независимо от направления ее вращения.
Осталось только реализовать управление этим мотором из скрипта, поскольку все привязки в конфигурации уже есть. Параллельно с этим, мы уберем счетчики, поскольку как оказалось, они не влияют на работу модели.
Для этого мы должны объявить еще одну переменную m_breaks, рядом с существующими: <source lang=kpp> class Bonnet extends WorldObject {
var Motor m_engine; var Motor m_turn; var Motor m_breaks; //добавляем переменную var int m_engine_start_count; //а это убираем var int m_turn_start_count; //и это тоже
</source>
Далее, в метод State_init() прописываем строчку для инициализации нашей переменной, а так же задаем параметры: <source lang=kpp> function void State_init() {
//... m_engine = this.getMotor('engine'); m_turn = this.getMotor('turn'); m_breaks = this.getMotor('breaks'); //получаем мотор по имени
m_breaks.setParam('speed', 0); //желаемая скорость всегда 0 m_breaks.setParam('force', 100000); //силу по максимуму //...
} </source>
Меняем управляющие методы: <source lang=kpp> function void Control_stop_engine(const bytea arg) { stopEngine(); } function void Control_forward (const bytea arg) { m_breaks.disable(); startEngine(180, 1200); } function void Control_backward (const bytea arg) { m_breaks.disable(); startEngine(-180, 1200); } function void Control_break (const bytea arg) { m_breaks.enable(); } function void Control_stop_turn (const bytea arg) { stopTurn(); } function void Control_left (const bytea arg) { startTurn(-1, 3000); } function void Control_right (const bytea arg) { startTurn(1, 3000); } </source>
Обратите внимание, что мы поменяли величины силы для всех моторов в соответствии с нашими изменениями. Ну и подправили код методов таким образом, что при нажатии на тормоз, будет включен "двигатель" тормозов, а при нажатии на кнопки движения он будет автоматически выключен (чтобы нельзя было поехать на ручнике :).
Дополнительно, надо поправить оставшиеся методы, убрав из них упоминания о счетчиках: <source lang=kpp> function void startEngine(const real speed, const real force) {
if(!m_engine.isEnabled()) { m_engine.setParam('speed', speed); m_engine.setParam('force', force); m_engine.enable(); } //++m_engine_start_count;
}
function void stopEngine() { /*if(--m_engine_start_count == 0) */m_engine.disable(); }
function void startTurn(const real speed, const real force) {
if(!m_turn.isEnabled()) { m_turn.setParam('speed', speed); m_turn.setParam('force', force); m_turn.enable(); } //++m_turn_start_count;
}
function void stopTurn() { /*if(--m_turn_start_count == 0) */m_turn.disable(); } </source>
С кодом мы вроде как разобрались, осталось только его скомпилировать. Для этого, нужно опять же взять консоль, но уже перейти в директорию scripts. И выполнить следующую команду:
kpp bonnet.kpp
Если мы все сделали правильно, то программа скомпилируется без ошибок. Соответственно, компилятор выйдет, не сказав ни слова. В противном случае, он напишет отчет об ошибках, где будет указаны сообщения об ошибке и номер строки, на которой они возникают. Посмотрите на эти строки и сравните с тем что написано здесь. Возможно, вы пропустили какой то символ, либо написали слово с ошибкой.
Ура, работает!
Если это действительно так, то мои вам поздравления :) Теперь можно выписывать круги на нашем виртуальном автодроме, дрифтить и разворачиваться на ручнике :) Если же что-то не получилось, — не отчаивайтесь. Пишите свои вопросы на форум и делитесь своими мыслями с другими участниками.
Домашнее задание
Если вы горите желанием что-нибудь еще эдакое сделать, то вот вам список идей которые вы можете поробовать реализовать самостоятельно :) Не забывайте только перекомпилировать скрипт (утилита kpp) и конвертировать файл модели после изменения (утилита bxd).
- Поиграйте со значениями; попробуйте подобрать лучшие соотношения мощностей двигателя и коэффициентов трения. Удачный опыт, можно опять же описать на форуме.
- Попробуйте поставить разные коэффициенты трения для передних и задних колес (а так же установить трение в самих осях).
- Можно переделать машину из переднеприводной в заднеприводную. Для этого надо поправить определение двигателя engine в файле описания модели. Как именно — решать вам ;) Не забывайте, что поведение машины сильно изменится при этом. Все таки задний привод.
- А можно сделать и полный привод! :)
- А еще можно попробовать сделать и заднюю пару колес управляемой (а то и независимо!). Тогда получится машина с огромной маневренностью.
- Попробуйте сделать нормальные тормоза, а не только ручник. То есть, чтобы машина тормозила плавно, так чтобы колеса не блокировались. Подсказка: это можно сделать с помощью имеющегося двигателя m_engine, просто надо менять режимы его работы. При торможении ставить желаемую скорость в 0 и подбирать параметры.
- Еще можно сделать настоящую подвеску (амортизаторы). Тогда машина сможет лучше ездить по пересеченной местности. Только для этого надо использовать уже линейный мотор.
- Наконец, можно упростить все что мы сделали тут и обойтись без введения дополнительного двигателя, а все сделать через основной.
- Если совсем нечего делать, то можно добавить еще одну пару колес где нибудь =)
В целом, пробуйте и экспериментируйте :) Все в ваших руках. Диапазон возможностей ограничивается только вашей фантазией :)
В следующем туториале я постараюсь рассказать, как можно добавлять в систему собственные объекты. Это немного сложнее, но только чуть чуть. До встречи! :)
С уважением,
Korvin