Как сделать машинку из бумаги
Оригами — это уникальное искусство складывать необычные фигурки из листов простой бумаги. В этой статье мы расскажем о том, как сложить из бумаги машинку, так что запаситесь набором цветной бумаги и смело подключайте к работе детей, чтобы вместе с ними создать целый парк бумажных машин!
Урок №1
Для начала мы сделаем спортивную машину. Вам потребуется прямоугольный лист бумаги. Соотношение его сторон должно равняться 1:7.
1: Сначала загните верхний правый угол, затем разогните и загните левый угол — таким образом Вы наметите сгибы.
Мусоровоз: 7 примеров, как сделать машину из подручных материалов
Хендмейд докатился до автопрома. Живешь ты в Китае, Америке, Вьетнаме или Казахстане, встретить на улице самодельный «Ламборгини» или «Бэтпод» можно в любой момент. Сегодня FURFUR собрал под одним капотом самые яркие и безумные авто из мусора, старых автозапчастей и даже ржавых кастрюль.
«ФЕРРАРИ» ДЖОША РЕДДИНГА
Недавно в штате Индиана, США, появился новый автомобиль «Феррари». Правда, от настоящего «Феррари» у него нет ни одной детали, зато ажиотаж автомобиль вызвал огромный. Каждая парковка превращалась в место стихийной фотосессии.
Машину со столь гордым названием собрал Джош Реддинг — фермер, а по совместительству автомеханик-любитель. В хозяйстве у Джоша скопилась целая куча самого разного мусора: несколько разномастных ведер, старый паровой котел, большой деревянный ящик и даже колеса от строительной тачки. Почему-то отнести все эти богатства на свалку Джош не решился. Неожиданно в куче хлама он увидел очертания будущего «болида».
Свою поделку Реддинг назвал «Феррари». Автомобиль, конечно, вышел неказистый, зато совершенно уникальный. Собрать копию настоящего «Феррари» значительно проще. Скопировать машину Джоша невозможно. Ну у кого еще найдется такое же облупившееся зеленое ведро под фару? А про декоративных коровок под лобовым стеклом и говорить нечего.
КАСТОМИЗИРОВАННЫЙ «ЖУК»
Мусорный автомобиль может стать произведением искусства, особенно если за дело возьмется художник. Индиец по имени Хари (для индийских властей — Haribaabu Haatesan) приобрел за копейки старый «Фольксваген Жук». Машинка была не на ходу, у нее недоставало больше половины деталей, краска почти слезла — «Жуку» явно было место на автосвалке… или в мастерской Хари.
Художники, даже в Индии, люди не самые богатые. Зато с кучей свободного времени. Хари это время не терял: он отправился на ближайшую свалку. Для мусорного тюнинга пригодились самые разные предметы: обломки разбитых машин, старый обогреватель, целая россыпь бутылочных крышек, поломанные микросхемы.
Часть новых «деталей» пошла на ремонт «Жука», остальные — на декор. Получившаяся машина получила название Think Blue. Художественно обклеенный мусором автомобиль Хари покрыл золотой краской — на такой колеснице не стыдно приехать в гости к радже!
Самое интересное, что представительство «Фольксваген» в Индии посчитало работу Хари лучшей рекламой своих авто. Теперь художник колесит по стране и демонстрирует мусорного «Жука» на выставках.
САМОДЕЛЬНЫЙ «ПОРШЕ»
«В моторном отсеке Porsche 997 GT3 RS находится 3,8-литровый двигатель мощностью 450 лошадиных сил. Машина разгоняется с нуля до сотни за 4 секунды и достигает максимальной скорости 310 км/ч», — примерно такой текст прочитал однажды в рекламном буклете австриец Фердинанд Йоханнес. Фердинанд понимал: машина ему не по карману. Как он не пытался заработать, не видать ему 450 лошадиных сил. Рассчитывать можно только на собственные.
Именно этот принцип герр Йоханнес положил в основу своего творения. «Порше» мощностью в одну человеческую силу — такую амбициозную задачу поставил перед собой австриец. Полгода он проводил вечера в гараже, собирая копию своей четырехколесной мечты. На металлическую раму Фердинанд закрепил пластиковые трубки и пластины, склеил их особо прочным скотчем. Тот же скотч пошел на обшивку кузова. Сверху австриец покрыл авто блестящей пленкой.
«Мотор» у этого самого легкого и медленного «Порше» в истории — как у велосипеда. Фердинанд садится за руль и крутит педали. Для автомобиля Ferdinand GT3 RS — а именно так назвал австриец свое творение — весит крайне мало, всего 96 кг. Но для велосипеда это вес рекордный. При попутном ветре гонщику удается разогнать свой спорткар до 10 км/ч.
В планах у Фердинанда построить еще несколько веломобилей. Конечно, прокатить девушек на нем не удастся — слишком тяжело. Разве что пристроить еще две педали — для пассажирки. Зато эффектно подкатить к ночному клубу герр Йоханнес точно может.
БОЛИД ИЗ КАСТРЮЛЬ И МУСОРА
Китайцы копируют абсолютно все. От технических решений и дизайнерских находок до киносценариев и гаджетов. Самодельную пятую версию известного телефона китайцы умудрились выпустить на несколько месяцев раньше официальной «яблочной» премьеры.
Неудивительно, что в этой стране автомобили копируют все кому не лень. А не лень многим. Братья Жао из Тяньшаня замахнулись на «Формулу-1» — китайского болида до сих пор не существовало. Трудолюбивые братья работали над проектом 20 лет — и создали себе нового «Брата». Именно так они назвали получившееся гоночное авто.
Братья Жао — китайские фермеры. Они никогда не видели болид вживую, но картинок из журналов им хватило. Нового «Брата» китайцы построили в память об отце, который работал водителем грузовика и 32 года назад погиб во время землетрясения. Теперь машина в его честь красуется на автовыставке Тяньшаня.
Китайцы уже успели провести испытания — болид разгоняется до 160 км/ч. Конечно, результат для «Формулы-1» слабоват, но ведь «Брат» сделан из обломков велосипедов, строительного мусора и старых кастрюль. А им такие скорости и не снились.
«РОЛЛС-РОЙС» ИЗ КАЗАХСТАНА
Еще один автолевша живет значительно ближе — в городе Шахтинске, что в Казахстане. 24-летний Руслан Муканов — единственный в городе обладатель «Роллс-Ройса Фантом». Во всяком случае, снаружи его автомобиль от «Роллс-Ройса» точно не отличить.
500 тысяч долларов — именно столько пришлось бы заплатить Руслану, чтобы приобрести настоящий «Роллс-Ройс». Старый битый «Мерседес 124» обошелся ему всего в 1500 долларов. Примерно столько же ушло на «запчасти» для тюнинга: пластилин, из которого Руслан лепил изгибы автомобиля, стекловолокно и штукатурку для покрытия кузова, низкопрофильную резину и колеса увеличенного радиуса. За фирменным значком «Роллс-Ройса» Руслан охотился по всему Казахстану и в итоге нашел… Думаете, где? Правильно, в Караганде.
Механику пришлось съездить и в столицу Алматы — посмотреть, как выглядит настоящий «Роллс-Ройс». Помогли и чертежи из интернета. В итоге машина получилась почти точной копией «королевского автомобиля». Только внутри Муканов ничего не поменял. «Говорить всем, что это настоящий Rolls-Royce Phantom, глупо. Думаю, главное, что мое творение визуально один в один совпадает с оригиналом. Гонять на роскошном авто, пусть и ненастоящем, это тоже круто!» — поясняет Руслан.
Он стал настоящей звездой Шахтинска. Люди сигналят ему на дороге, фотографируют, друзья и знакомые просят прокатить. При этом за 15-20 тысяч долларов Руслан готов расстаться со своим «Роллс-Ройсом». Просто он задумал собрать новый, на этот раз еще более представительный — черный «Фантом».
МАШИНА ИЗ ТРЕХ АВТО
Английская инженерная мысль превзошла самые смелые творения народных автомехаников. Как минимум три машины слились в сварочном экстазе, чтобы породить этого красавца. Особенно впечатляет «крылатый» капот.
Судя по всему, создатель английского «суперкара» часто наведывается на автосвалку. Кроме того, ему не чужда любовь к оригинальным формам и необычным сочетаниям цветов.
Имя владельца точно не установлено, но известно, что свою поделку он продает — за целых 4000 долларов! Этот автомобиль был выставлен на аукционе eBay в течение двух лет, но недавно хозяин снял его с торгов. То ли решил колесить на нем сам, то ли покупатель все-таки нашелся.
БЭТМОБИЛЬ
Отдельный пунктик автоумельцев — бэтмобили. Если купить тот же «Роллс-Ройс» при очень большом желании и продаже себя в рабство еще возможно, то бэтмобиль купить невозможно. Тот, что принадлежит киностудии, обычно не продается, а то и вообще бутафорский. А настоящий Бэтмен свою тачку не продаст — она ему самому нужна.
В итоге поклонники навороченных киномобилей выкручиваются как могут. Кто-то считает, что машину достаточно обтянуть темной тканью, сделать спойлер и нарисовать крылатый значок Бэтмена — и авто готово защищать добро и справедливость. Но американец Боб Даллем подошел к делу значительно основательнее.
Боб захотел себе автомобиль «Тамблер» из серии фильмов про Бэтмена режиссера Кристофера Нолана. Даллем не меньше сотни раз посмотрел фильм и сделал скриншоты всех кадров с брутальной тачкой Бэтмена. В интернете он нашел несколько фанатских чертежей «Тамблера», доработал их и принялся за дело. К счастью, у него был двухместный гараж.
На создание своего монстра Боб потратил не меньше 50 тысяч долларов. Новый двигатель V8 выдает здесь 355 л.с., а ведь надо было купить еще автоматическую коробку передач, громадные колеса для бигфута и карбоновое стекло для обшивки. Автомобиль Бэтмена дешевым быть не может! В итоге у Боба получился настоящий шедевр.
Со своим «Тамблером» Даллем никогда не расстанется, сколько бы денег ему не предложили. Он делал машину для себя! Видимо, тем же принципом руководствовался и Бэтмен.
Текст: Никита Пурыжинский
10 способов сделать машину быстрее или азы тюнинга
Сегодня коротко перечислим несколько способов как сделать машину динамичней. Некоторые из них не требует никаких навыков, к другим надо немного подготовиться и немного повозиться. И так, начинаем обратый отсчет с наиболее простых и примитивных способов:
10. Сбрасывайте лишний вес: уберите весь хлам и мусор из багажника и салона, а если пойти по хардкору, то можно также демонтировать ненужных сидений, ковриков, запаску, магнитолу, и салонных панелей, похудеть (съедая меньше, что оставляет вам больше денег на зпчасти!), стричься под-ноль и сходить в туалет по-чаще… короче, говоря — все лишнее из салона и багажника — убрать!
9. Обслуживайте машину! меняйте фильтра, свечи и технические жидкости почаще — грязные фильтра и отработанные технические жидкости всегда ограничивают нормальную работу двигателя и приводят к ухудшению его самочувствию и уменьшению его производительности…
8. Используйте подходящие для мощности вашего мотора резину и постоянно следите за давлением в шинах, для оптимальной передачи момента на асфальт: имеете ввиду, что чем больше диаметр колес, тем тяжелее будут они весить и тем больше потребуется крутящего момента, чтобы вращать их; также стоит упомянуть о том, что широкая резина обеспечивает хороший зацеп с дорогой; так что главная задача тут состоит в том, чтобы найти ту золотую середину, когда размерность колес будет максимально работать на вас.
7. Оптимальная температура работы двигателя — это наше все: электронные блоки управления двигателем заранее запрограммированы таким образом, чтобы выдавать максимальную заложенную в них
производительность конкретного мотора, только при определенных значениях температуры разных узлов, поэтому всегда обеспечивайте оптимальное охлаждение мотору и оптимаьлное проветривание подкапотного пространства.
6. Дайте двигателю новое дыхание: иногда просто недостаточно чистого стокового воздушного фильтра для длительной работы двигателя на высоких оборотах или быстрого его раскручивания, поэтому желательно ставить хороший фильтр-нулевик (который также имеет свои ньюансы) или просто пристроить более производительный фильтр от другого автомобиля. Это даст вам лучший разгон и гарантирует хорошие показатели работы мотора на высоких оборотах, также не забудем о необходимости непосредственного доступа более холодного воздуха во впуск.
5. Позаботившись о впуске, нужно позаботиться и о выпуске (выхлопе): больший, по диаметру, выхлоп — это не только хороший звук, но и меньшее сопротивление газам выхлопа, и опять же, более оптимальные условия работы мотора, также не забудем об удалении катализатора и лишних глушителей, другими словами — максимально возможный прямоток…
4. Электроника двигателя: помимо железа, есть еще и электроника, а тут важно наличие быстродоступной и более подробной информации о параметрах разных узлов автомобиля; поэтому установка улучшенных датчиков (как широкополосная лямбда и широкодиапозонный датчик массового расхода воздуха, к примеру) на ряду с присутствием программируемого блока управления, который будет переваривать всю эту информацию, является необходимой, если мы хотим говорить о каких-либо серьезных изменениях в динамике.
3. Жесткость ходовки— это лучшая передача момента на дорогу: разумное поднятие давления в шинах и использование более жесткой, адекватно настроенной подвески, более жестких усиленных полиуретановых подушек двигателя и трансмиссии, обеспечивает лучшую передачу кинетической энергии коленвала на колеса, что положительно сказывается на динамику…
2. Кузов, и рама если таковой имеется — последнее слово тут отдается жесткости и легкости.
1. САМООБУЧЕНИЕ: учите себя общению с машиной, чтобы вы с ней, после всех переделок и настроек, смогли общаться на ТЫ; начиная с быстрого переключения передач и синхронного своевременного нажатия на педали, заканчивая ювелирно отточенным управлением и улавливанием машины при любых условиях!
Но не забудем о том, что иногда надо остановиться! поэтому эффективная тормозная система, всегда актуальна!
Спасибо, что читайте мой блог! Благодарю за поддержку!
Всем удачи! С Богом!
Как сделать машинку на WheelCollider’ах?
WheelColider – это компонент Unity3D, базирующийся на функционале физического движка PhysX, созданный специально, чтобы симулировать поведение автомобилей.
Множество игр базируются на физике автомобилей, например серия игр GTA или NFS. Так же, во многих играх транспорт является важным добавлением игровой механики – серия игр Battlefield, например. И, естественно, многие разработчики хотят добавить физику автомобилей в свой проект. Как раз для этого созданы WheelCollider’ы. Казалось бы, что может пойти нет так?
С самых первых версий Unity3D разработчики ведут неравный бой с WheelCollider’ами. Изначально проблема состояла из двух факторов:
– С одной стороны, WheelCollider’ы устроены очень примитивно, базовый функционал в Unity3D доступен ограничено.
– С другой стороны, на деле, кроме колеса и подвески нужно еще много чего написать. Объем работ, нередко, невероятно огромен.
Базовые проблемы – машина обладает низкой стабильностью поведения. В первую очередь – постоянно переворачивается. В этой статье я собрал коллекцию базовых техник, необходимых, чтобы решить проблему переворотов.
Эта статья состоит из нескольких частей:
– Перевод оригинальной статьи с сайта Unity3d.com о создании автомобиля на WheelCollider’ах.
– Трабл-шутинг, устраняем проблемы, «чтобы поехало хоть как-то»
– Несколько способов\методов\лучших практик, позволяющих сделать поведение машины на WheelCollider’ах более-менее приемлемым.
– Сборка машинки на основе очень кривой модели.
Перевод: Создание простого скелета
- Сначала, добавьте GameObject, который будет служить базой автомобиля. Чтобы сделать это, нажмите GameObject >CreateEmpty. Смените имя GameObject’а на car_root.
- Добавьте компонент Rigidbody на car_root. Стандартное значение массы в 1 кг слишком легкое для стандартных настроек подвески; установите массу на 1500 кг.
- Далее, создаем Collider автомобиля. GameObject >3D Object >Cube. Расположите куб под car_root в иерархии. Сбросьте параметры компонента Transform на 0 чтобы идеально выровнять куб в локальном пространстве. Автомобиль сориентирован параллельно оси Z (Z+ это направление вперед), поэтому меняем параметр ZScale на значение 3.
- Добавьте колеса к базе автомобиля. Выберите car_root и кликните по нему правой клавишей, далее CreateEmptyChild. Поменяйте название на wheels. Сбросьте параметры Transform на этом объекте. Этот GameObject не обязателен, но позже он поможет для настройки и отладки.
- Чтобы создать первое колесо, выберите wheels GameObject, правая клавиша >CreateEmpty, назовите новый объект frontLeft. Сбросьте Transform, а потом установите координаты: X на –1, Y на 0, и Z to Чтобы добавить колайдер, в меню инспектора нажмите Addcomponent >Physics >WheelCollider.
- Продублируйте GameObject frontLeft (правая клавиша >Duplicate). Измените координатыX c -1 на 1. Смените имя объекта на frontRight.
- Выберите frontLeftand frontRight GameObject’ы. Продублируйте их. Измените координатыZ на обоих GameObject’ах на –1. Измените имена на rearLeft и rearRight.
- Наконец, выберите car_root приподнимите его над поверхностью земли.
У вас должно получится что-то типа этого:
Чтобы машиной можно было управлять, вам нужно написать контроллер для неё:
using UnityEngine; using System.Collections; using System.Collections.Generic; public class SimpleCarController : MonoBehaviour public List axleInfos; // информация о каждой оси public float maxMotorTorque; // максимальный крутящий момент public float maxSteeringAngle; // максимальный угол поворота колес public void FixedUpdate() float motor = maxMotorTorque * Input.GetAxis("Vertical"); float steering = maxSteeringAngle * Input.GetAxis("Horizontal"); foreach (AxleInfo axleInfo in axleInfos) if (axleInfo.steering) axleInfo.leftWheel.steerAngle = steering; axleInfo.rightWheel.steerAngle = steering; > if (axleInfo.motor) axleInfo.leftWheel.motorTorque = motor; axleInfo.rightWheel.motorTorque = motor; > > > > [System.Serializable] public class AxleInfo public WheelCollider leftWheel; public WheelCollider rightWheel; public bool motor; // присоединено ли колесо к мотору? public bool steering; // поворачивает ли это колесо? >
Создайте новый скрипт C# и назовите его SimpleCarController. Скопируйте в новый скрипт текст скрипта выше и сохраните его. Далее добавьте его на базу автомобиля (Add Component > New Script на car_root). Вы можете попробовать разные настройки и протестировать их. Такие настройки SimpleCarController очень эффективны:
У вас может быть до 20 колес на одной машине. Далее, добавим колеса. Как видите, Wheel Collider не добавляет визуальную часть колеса, поэтому нужно еще чуть-чуть кода.
Вам нужна геометрия колеса. Вы можете сделать простое колесо с помощью цилиндров. Есть несколько способов добавить визуальную часть колеса: добавить визуальную часть в параметры скрипта или написать скрипт, который будет автоматически находить и присваивать визуальную часть. Мы воспользуемся вторым вариантом. Добавьте визуальную часть колес под GameObject’ы Wheel Collider’ов.
Далее, изменяем скрипт SimpleCarController:
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
[System.Serializable]
public class AxleInfo <
public WheelCollider leftWheel;
public WheelCollider rightWheel;
public bool motor;
public bool steering;
>
public class SimpleCarController : MonoBehaviour <
public List axleInfos;
public float maxMotorTorque;
public float maxSteeringAngle;
// находит визуальную часть колес
// устанавливает новые координаты
public void ApplyLocalPositionToVisuals(WheelCollider collider)
<
if (collider.transform.childCount == 0) <
return;
>
Transform visualWheel = collider.transform.GetChild(0);
Vector3 position;
Quaternion rotation;
collider.GetWorldPose(out position, out rotation);
visualWheel.transform.position = position;
visualWheel.transform.rotation = rotation;
>
public void FixedUpdate()
<
float motor = maxMotorTorque * Input.GetAxis(“Vertical”);
float steering = maxSteeringAngle * Input.GetAxis(“Horizontal”);
foreach (AxleInfo axleInfo in axleInfos) <
if (axleInfo.steering) <
axleInfo.leftWheel.steerAngle = steering;
axleInfo.rightWheel.steerAngle = steering;
>
if (axleInfo.motor) <
axleInfo.leftWheel.motorTorque = motor;
axleInfo.rightWheel.motorTorque = motor;
>
ApplyLocalPositionToVisuals(axleInfo.leftWheel);
ApplyLocalPositionToVisuals(axleInfo.rightWheel);
>
>
>
- Нужно проверить настройки инпута. Есть небольшой, но реальный шанс, что инпут обнулился при создании проекта. Edit > Project Settings… > Input. Конкретно нас интересуют оси Horizontal и Vertical, должно быть, как на картинки снизу.
- Колеса слишком высоко, по итогу колеса не касаются земли > сдвинуть колеса по Y координатам на 0.5 юнита ниже (т.е. Y = -0.5)
- Не выставлены значения в SimpleCarController > значение maxMotorTorque = 400
Лучшие практики, устранение проблем
1. Колеса смотрят непонятно куда
В двух словах нужно использовать пустой GameObject как базу для колеса, а затем саму модель нужно повернуть на 90 градусов по Z. Не забудьте присвоить базу колеса в SimpleCarController. В реальной ситуации колесо всегда состоит из нескольких моделей, так что изменения иерархии не избежать. Далее эта часть будет усложнятся – будем добавлять тормозные диски и суппорта.
using UnityEngine; using System.Collections; using System.Collections.Generic; [System.Serializable] public class AxleInfo public WheelCollider leftWheel; public GameObject leftWheelVisuals; public WheelCollider rightWheel; public GameObject rightWheelVisuals; public bool motor; public bool steering; public void ApplyLocalPositionToVisuals() //left wheel if (leftWheelVisuals == null) return; > Vector3 position; Quaternion rotation; leftWheel.GetWorldPose(out position, out rotation); leftWheelVisuals.transform.position = position; leftWheelVisuals.transform.rotation = rotation; //right wheel if (rightWheelVisuals == null) return; > rightWheel.GetWorldPose(out position, out rotation); rightWheelVisuals.transform.position = position; rightWheelVisuals.transform.rotation = rotation; > > public class SimpleCarController : MonoBehaviour public List axleInfos; public float maxMotorTorque; public float maxSteeringAngle; public void FixedUpdate() float motor = maxMotorTorque * Input.GetAxis("Vertical"); float steering = maxSteeringAngle * Input.GetAxis("Horizontal"); foreach (AxleInfo axleInfo in axleInfos) if (axleInfo.steering) axleInfo.leftWheel.steerAngle = steering; axleInfo.rightWheel.steerAngle = steering; > if (axleInfo.motor) axleInfo.leftWheel.motorTorque = motor; axleInfo.rightWheel.motorTorque = motor; > axleInfo.ApplyLocalPositionToVisuals(); > > >
2. Машину трясет при наборе скорости, Машина переворачивается – как увеличить стабильность?
- Поднять точку приложения силы колеса до середины колеса – в нашем случае ForceAppPointDistance = 0.5 (в настройках WheelCollider’а)
- Правильное, корректное поведение пружины – под нагрузкой пружина пытается разжаться до длинны в состоянии «без нагрузки». Тем не менее, стандартные настройки почему-то выставлены на середину хода подвески. TargetPosition = 0
- Стабилизаторы. Что-то типа стабилизаторов поперечной устойчивости. Суть стабилизаторов поперечной устойчивости – когда машина кренится, колесо, с противоположной стороны от направления крена, приподнимается выше. Таким образом машина кренится меньше. В нашем случае, мы добавляем стабилизирующие внешние силы. Мощность стабилизаторов следует выставлять в диапазоне от 0 до жесткости пружин (WheelCollider > Suspencion Spring > Spring).
- Меньше мощность стабилизаторов – больше крен в поворотах. И еще момент: ситуация в которой автомобиль с ходом подвески в полметра и диаметром колес в один метр переворачивается на ровном месте – это нормальное, адекватное поведение автомобиля. Существует т.н. moose test, как раз, чтобы проверить подобное поведение.
Рассчитываем сжатие подвески в процентах слева и справа
-> сравниваем сжатия (левое сжатие минус правое)
-> знак результата сравнения дает направление приложения силы стабилизации (минус или плюс)
-> домнажаем то, что получилось на мощность стабилизатора
-> присваиваем в центр колеса.
Так же, есть смысл корректировать точку присваивания силы в соответствии с ForceAppPointDistance.
using UnityEngine; using System.Collections; using System.Collections.Generic; [System.Serializable] public class AxleInfo public WheelCollider leftWheel; public GameObject leftWheelVisuals; private bool leftGrounded = false; private float travelL = 0f; //дистанция от полного «разжатия» до нынешнего положения, проценты public WheelCollider rightWheel; public GameObject rightWheelVisuals; private bool rightGrounded = false; private float travelR = 0f; //дистанция от полного «разжатия» до нынешнего положения, проценты public bool motor; public bool steering; public float Antiroll = 10000; //жесткость стабилизатора private float AntrollForce = 0; public void ApplyLocalPositionToVisuals() //left wheel if (leftWheelVisuals == null) return; > Vector3 position; Quaternion rotation; leftWheel.GetWorldPose(out position, out rotation); leftWheelVisuals.transform.position = position; leftWheelVisuals.transform.rotation = rotation; //right wheel if (rightWheelVisuals == null) return; > rightWheel.GetWorldPose(out position, out rotation); rightWheelVisuals.transform.position = position; rightWheelVisuals.transform.rotation = rotation; > public void CalculateAndApplyAntiRollForce(Rigidbody theBody) //рассчитываем стабилизаторы WheelHit hit; // с начала процент сжатия подвески leftGrounded = leftWheel.GetGroundHit(out hit); if (leftGrounded) travelL = (-leftWheel.transform.InverseTransformPoint(hit.point).y - leftWheel.radius) / leftWheel.suspensionDistance; else travelL = 1f; rightGrounded = rightWheel.GetGroundHit(out hit); if (rightGrounded) travelR = (-rightWheel.transform.InverseTransformPoint(hit.point).y - rightWheel.radius) / rightWheel.suspensionDistance; else travelR = 1f; // Сила, которая будет отдана стабилизатором AntrollForce = (travelL - travelR) * Antiroll; // (travelL-travelR) даст нам знак для следующего действия //присваиваем силы if (leftGrounded) theBody.AddForceAtPosition(leftWheel.transform.up * -AntrollForce, leftWheel.transform.position); if (rightGrounded) theBody.AddForceAtPosition(rightWheel.transform.up * AntrollForce, rightWheel.transform.position > >
body body; private void Start() body = GetComponent(); > public void FixedUpdate() float motor = maxMotorTorque * Input.GetAxis("Vertical"); float steering = maxSteeringAngle * Input.GetAxis("Horizontal"); foreach (AxleInfo axleInfo in axleInfos) if (axleInfo.steering) axleInfo.leftWheel.steerAngle = steering; axleInfo.rightWheel.steerAngle = steering; > if (axleInfo.motor) axleInfo.leftWheel.motorTorque = motor; axleInfo.rightWheel.motorTorque = motor; > axleInfo.ApplyLocalPositionToVisuals(); axleInfo.CalculateAndApplyAntiRollForce(body); > > >
3. Когда автомобиль поворачивает, все колеса идут по разным траекториям.
Аккерман. (на русском нет статьи на вики, тем не менее). Для того, чтобы автомобиль не заносило (а в нашем случае – еще и не переворачивало), нужно, чтобы колеса рулевого управления поворачивали в соответствии с геометрией рулевого управления Аккермана. К сожалению, WheelCollider’ы устроены так, что при определенных углах проскальзывания колеса сила стремится к бесконечности – в этот момент машина пытается перевернутся. Чтобы снизить шанс переворота, нужно написать симуляцию Аккермана. ackermanSteering = 0 означает, что колеса поворачивают параллельно, 1 означает, что Аккерман присваивается в полной мере, в соответствии с положением колес\осей относительно друг друга.
using UnityEngine; using System.Collections; using System.Collections.Generic; [System.Serializable] public class AxleInfo public WheelCollider leftWheel; public GameObject leftWheelVisuals; private bool leftGrounded = false; private float travelL = 0f; private float leftAckermanCorrectionAngle = 0; public WheelCollider rightWheel; public GameObject rightWheelVisuals; private bool rightGrounded = false; private float travelR = 0f; private float rightAckermanCorrectionAngle = 0;
public bool motor; public bool steering; public float Antiroll = 10000; private float AntrollForce = 0; public float ackermanSteering = 1f; public void ApplyLocalPositionToVisuals() //left wheel if (leftWheelVisuals == null) return; > Vector3 position; Quaternion rotation; leftWheel.GetWorldPose(out position, out rotation); leftWheelVisuals.transform.position = position; leftWheelVisuals.transform.rotation = rotation; //right wheel if (rightWheelVisuals == null) return; > rightWheel.GetWorldPose(out position, out rotation); rightWheelVisuals.transform.position = position; rightWheelVisuals.transform.rotation = rotation; > public void CalculateAndApplyAntiRollForce(Rigidbody theBody) WheelHit hit; leftGrounded = leftWheel.GetGroundHit(out hit); if (leftGrounded) travelL = (-leftWheel.transform.InverseTransformPoint(hit.point).y - leftWheel.radius) / leftWheel.suspensionDistance; else travelL = 1f; rightGrounded = rightWheel.GetGroundHit(out hit); if (rightGrounded) travelR = (-rightWheel.transform.InverseTransformPoint(hit.point).y - rightWheel.radius) / rightWheel.suspensionDistance; else travelR = 1f; AntrollForce = (travelL - travelR) * Antiroll; if (leftGrounded) theBody.AddForceAtPosition(leftWheel.transform.up * -AntrollForce, leftWheel.transform.position); if (rightGrounded) theBody.AddForceAtPosition(rightWheel.transform.up * AntrollForce, rightWheel.transform.position); > public void CalculateAndApplySteering (float input, float maxSteerAngle, List allAxles) //first find farest axle, we got to apply default values AxleInfo farestAxle = allAxles[0]; //calculate start point for checking float farestAxleDistantion = ((allAxles[0].leftWheel.transform.localPosition - allAxles[0].rightWheel.transform.localPosition) / 2f).z; for (int a = 0; a < allAxles.Count; a++) float theDistance = ((allAxles[a].leftWheel.transform.localPosition - allAxles[a].rightWheel.transform.localPosition)/ 2f).z; // if we found axle that farer - save it if (theDistance < farestAxleDistantion) farestAxleDistantion = theDistance; farestAxle = allAxles[a]; > > float wheelBaseWidth = (Mathf.Abs( leftWheel.transform.localPosition.x) + Mathf.Abs( rightWheel.transform.localPosition.x))/2; float wheelBaseLength = Mathf.Abs( ((farestAxle.leftWheel.transform.localPosition + farestAxle.rightWheel.transform.localPosition)/ 2f).z) + Mathf.Abs(((leftWheel.transform.localPosition + rightWheel.transform.localPosition) / 2f).z); float angle = maxSteerAngle * input; //ackerman implementation float turnRadius = Mathf.Abs(wheelBaseLength * Mathf.Tan(Mathf.Deg2Rad * (90 - Mathf.Abs(angle)))); if (input != 0) //right wheel if (angle > 0) /turn right rightAckermanCorrectionAngle = Mathf.Rad2Deg * Mathf.Atan(wheelBaseLength / (turnRadius - wheelBaseWidth / 2f)); rightAckermanCorrectionAngle = (rightAckermanCorrectionAngle - Mathf.Abs(angle)) * ackermanSteering + (Mathf.Abs(angle)); rightAckermanCorrectionAngle = Mathf.Sign(angle) * rightAckermanCorrectionAngle; > else /turn left rightAckermanCorrectionAngle = Mathf.Rad2Deg * Mathf.Atan(wheelBaseLength / (turnRadius + wheelBaseWidth / 2f)); rightAckermanCorrectionAngle = (rightAckermanCorrectionAngle - Mathf.Abs(angle)) * ackermanSteering + (Mathf.Abs(angle)); rightAckermanCorrectionAngle = Mathf.Sign(angle) * rightAckermanCorrectionAngle; > //left wheel if (angle > 0) /turn right leftAckermanCorrectionAngle = Mathf.Rad2Deg * Mathf.Atan(wheelBaseLength / (turnRadius + wheelBaseWidth / 2f)); leftAckermanCorrectionAngle = (leftAckermanCorrectionAngle - Mathf.Abs(angle)) * ackermanSteering + (Mathf.Abs(angle)); leftAckermanCorrectionAngle = Mathf.Sign(angle) * leftAckermanCorrectionAngle; > else /turn left leftAckermanCorrectionAngle = Mathf.Rad2Deg * Mathf.Atan(wheelBaseLength / (turnRadius - wheelBaseWidth / 2f)); leftAckermanCorrectionAngle = (leftAckermanCorrectionAngle - Mathf.Abs(angle)) * ackermanSteering + (Mathf.Abs(angle)); leftAckermanCorrectionAngle = Mathf.Sign(angle) * leftAckermanCorrectionAngle; > > else rightAckermanCorrectionAngle = 0f; leftAckermanCorrectionAngle = 0f; > leftWheel.steerAngle = leftAckermanCorrectionAngle; rightWheel.steerAngle = rightAckermanCorrectionAngle; Debug.Log(leftAckermanCorrectionAngle + " " + rightAckermanCorrectionAngle); > > [RequireComponent(typeof(Rigidbody))] public class SimpleCarController : MonoBehaviour public List axleInfos; public float maxMotorTorque; public float maxSteeringAngle; private Rigidbody body; private void Start() body = GetComponent(); > public void FixedUpdate() float motor = maxMotorTorque * Input.GetAxis("Vertical"); foreach (AxleInfo axleInfo in axleInfos) if (axleInfo.steering) axleInfo.CalculateAndApplySteering(Input.GetAxis("Horizontal"), maxSteeringAngle, axleInfos); > if (axleInfo.motor) axleInfo.leftWheel.motorTorque = motor; axleInfo.rightWheel.motorTorque = motor; > axleInfo.ApplyLocalPositionToVisuals(); axleInfo.CalculateAndApplyAntiRollForce(body); > > >
4. Настройка частоты обновления колайдеров колеса
Значения могу быть разными, зависят от конкретной настройки fixedTimeStep. В общем случае уменьшение fixedTimeStep (т.е. увеличение количества раз обработки физики в секунду) улучшает ситуацию, НО, низкие значения fixedTimeStep драматично и жестко грузят процессор. Нужно найти «золотую середину» между адекватным поведением автомобиля и нагрузкой на процессор. В данном случае fixedTimeStep = 0.02. Значения меньше 0.033 считаются очень маленькими.
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
[System.Serializable]
public class AxleInfo
<
public WheelCollider leftWheel;
public GameObject leftWheelVisuals;
private bool leftGrounded = false;
private float travelL = 0f;
private float leftAckermanCorrectionAngle = 0;
public WheelCollider rightWheel;
public GameObject rightWheelVisuals;
private bool rightGrounded = false;
private float travelR = 0f;
private float rightAckermanCorrectionAngle = 0;
public bool motor;
public bool steering;
public float Antiroll = 10000;
private float AntrollForce = 0;
public float ackermanSteering = 1f;
public void ApplyLocalPositionToVisuals()
<
//left wheel
if (leftWheelVisuals == null)
<
return;
>
Vector3 position;
Quaternion rotation;
leftWheel.GetWorldPose(out position, out rotation);
leftWheelVisuals.transform.position = position;
leftWheelVisuals.transform.rotation = rotation;
//right wheel
if (rightWheelVisuals == null)
<
return;
>
rightWheel.GetWorldPose(out position, out rotation);
rightWheelVisuals.transform.position = position;
rightWheelVisuals.transform.rotation = rotation;
>
public void CalculateAndApplyAntiRollForce(Rigidbody theBody)
<
WheelHit hit;
leftGrounded = leftWheel.GetGroundHit(out hit);
if (leftGrounded)
travelL = (-leftWheel.transform.InverseTransformPoint(hit.point).y – leftWheel.radius) / leftWheel.suspensionDistance;
else
travelL = 1f;
rightGrounded = rightWheel.GetGroundHit(out hit);
if (rightGrounded)
travelR = (-rightWheel.transform.InverseTransformPoint(hit.point).y – rightWheel.radius) / rightWheel.suspensionDistance;
else
travelR = 1f;
AntrollForce = (travelL – travelR) * Antiroll;
if (leftGrounded)
theBody.AddForceAtPosition(leftWheel.transform.up * -AntrollForce, leftWheel.transform.position);
if (rightGrounded)
theBody.AddForceAtPosition(rightWheel.transform.up * AntrollForce, rightWheel.transform.position);
>
public void CalculateAndApplySteering (float input, float maxSteerAngle, List allAxles)
<
//first find farest axle, we got to apply default values
AxleInfo farestAxle = allAxles[0];
//calculate start point for checking
float farestAxleDistantion = ((allAxles[0].leftWheel.transform.localPosition – allAxles[0].rightWheel.transform.localPosition) / 2f).z;
for (int a = 0; a < allAxles.Count; a++)
<
float theDistance = ((allAxles[a].leftWheel.transform.localPosition – allAxles[a].rightWheel.transform.localPosition)/ 2f).z;
// if we found axle that farer – save it
if (theDistance < farestAxleDistantion)
<
farestAxleDistantion = theDistance;
farestAxle = allAxles[a];
>
>
float wheelBaseWidth = (Mathf.Abs( leftWheel.transform.localPosition.x) + Mathf.Abs( rightWheel.transform.localPosition.x))/2;
float wheelBaseLength = Mathf.Abs( ((farestAxle.leftWheel.transform.localPosition + farestAxle.rightWheel.transform.localPosition)/ 2f).z) +
Mathf.Abs(((leftWheel.transform.localPosition + rightWheel.transform.localPosition) / 2f).z);
float angle = maxSteerAngle * input;
//ackerman implementation
float turnRadius = Mathf.Abs(wheelBaseLength * Mathf.Tan(Mathf.Deg2Rad * (90 – Mathf.Abs(angle))));
//38.363
if (input != 0)
<
//right wheel
if (angle > 0)
/turn right
rightAckermanCorrectionAngle = Mathf.Rad2Deg * Mathf.Atan(wheelBaseLength / (turnRadius – wheelBaseWidth / 2f));
rightAckermanCorrectionAngle = (rightAckermanCorrectionAngle – Mathf.Abs(angle)) * ackermanSteering + (Mathf.Abs(angle));
rightAckermanCorrectionAngle = Mathf.Sign(angle) * rightAckermanCorrectionAngle;
>
else
/turn left
rightAckermanCorrectionAngle = Mathf.Rad2Deg * Mathf.Atan(wheelBaseLength / (turnRadius + wheelBaseWidth / 2f));
rightAckermanCorrectionAngle = (rightAckermanCorrectionAngle – Mathf.Abs(angle)) * ackermanSteering + (Mathf.Abs(angle));
rightAckermanCorrectionAngle = Mathf.Sign(angle) * rightAckermanCorrectionAngle;
>
//left wheel
if (angle > 0)
/turn right
leftAckermanCorrectionAngle = Mathf.Rad2Deg * Mathf.Atan(wheelBaseLength / (turnRadius + wheelBaseWidth / 2f));
leftAckermanCorrectionAngle = (leftAckermanCorrectionAngle – Mathf.Abs(angle)) * ackermanSteering + (Mathf.Abs(angle));
leftAckermanCorrectionAngle = Mathf.Sign(angle) * leftAckermanCorrectionAngle;
>
else
/turn left
leftAckermanCorrectionAngle = Mathf.Rad2Deg * Mathf.Atan(wheelBaseLength / (turnRadius – wheelBaseWidth / 2f));
leftAckermanCorrectionAngle = (leftAckermanCorrectionAngle – Mathf.Abs(angle)) * ackermanSteering + (Mathf.Abs(angle));
leftAckermanCorrectionAngle = Mathf.Sign(angle) * leftAckermanCorrectionAngle;
>
>
else
<
rightAckermanCorrectionAngle = 0f;
leftAckermanCorrectionAngle = 0f;
>
leftWheel.steerAngle = leftAckermanCorrectionAngle;
rightWheel.steerAngle = rightAckermanCorrectionAngle;
>
>
[RequireComponent(typeof(Rigidbody))]
public class SimpleCarController : MonoBehaviour
<
public List axleInfos;
public float maxMotorTorque;
public float maxSteeringAngle;
private Rigidbody body;
private void Start()
<
body = GetComponent();
for (int a = 0; a < axleInfos.Count;a++)
<
axleInfos[a].leftWheel.ConfigureVehicleSubsteps(5, 12, 15);
axleInfos[a].rightWheel.ConfigureVehicleSubsteps(5, 12, 15);
>
>
public void FixedUpdate()
<
float motor = maxMotorTorque * Input.GetAxis(“Vertical”);
foreach (AxleInfo axleInfo in axleInfos)
<
if (axleInfo.steering)
<
axleInfo.CalculateAndApplySteering(Input.GetAxis(“Horizontal”), maxSteeringAngle, axleInfos);
>
if (axleInfo.motor)
<
axleInfo.leftWheel.motorTorque = motor;
axleInfo.rightWheel.motorTorque = motor;
>
axleInfo.ApplyLocalPositionToVisuals();
axleInfo.CalculateAndApplyAntiRollForce(body);
>
>
>
Как правильно добавить графику и модели
1. Для начала нужна модель автомобиля. В общем случае, есть 3 варианта где её достать: заказать у специалиста по игровым 3D моделям, купить/скачать с Asset store, скачать с стороннего сайта.
Нужно иметь ввиду, что модели, «выдраные» из игр нельзя использовать в комерческих играх. Так же, стоит обратить внимание, что модели для рендеренга видео и модели из google sketchup не подходят принципиально из-за их внутренней структуры. В общем случае, при всех равных условия, подобные модели требуют в 2 раза больше ресурсов компьютера.
Для тестов мы воспользуемся сайтом gamemodels.ru. Это сайт, на котором модеры (люди, занимающиеся модифициями игр) выкладывают модели, «выдраные» из всевозможных игр. Ссылка на используемую модель
2. Создайте папку Models в ассетах игры. Распакуйте архив, и добавьте содержимое в папку Models
3. Зайдите в папку и перетащите файл “r34” на сцену. Должно получится так:
Данная модель «выдрана» через рендерер – т.е. спец программа просто копирует геометрию, которая сохранена в видеопамяти. Посему, в модели нет иерархии, нет названий.
4. В иерархии жмем правой клавишей на объект r34 и жмем «Unpack prefab». Это нужно, чтобы можно было корректировать иерархию, названия, добавлять скрипты.
5. Под объектом r34 нужно создать пустой объект, назвать его Wheels, убедится, что этот объект находится на координатах 0.
6. В Scene view кликаем на колеса и диски – перетаскиваем их под объект Wheels
7. Внимание! Кликаем на колесо, смотрим координаты. Колесо, вроде, находится на дальней стороне автомобиля. И, вроде, гизмо контроля положения в его центре. А вот координаты почему-то на нуле. Это нормально – реальный центр колеса находится в центре автомобиля. Чтобы решить эту проблему создаем 3 пустых объекта на каждое колесо – один для WheelCollider’а, второй для визуальной части колеса, третий для калиперов. Далее, чтобы быстрее\проще их разместить можно воспользоватся изометрическим видом. Для этого кликните на одну из осей в правом верхнем углу Scene View. Чтобы вернутся в вид перспективы – кликните на сам центральный белый кубик. Координаты центров колес: FR(0.78, 0.32399, 1.399); FL(-0.78, 0.32399, 1.399); RL(-0.78, 0.32399, -1.26), RR(0.78, 0.32399, -1.26).
8. Переименовываем колеса в соответствии с иерархией, использованной в начале статьи: корневой объект называем в соответствии с положением (например FR_Wheel это front-right wheel, переднее правое колесо). Копируем, добавляем Visual (например, FR_WheelVisual). Добавляем под базовый объект покрышки, диски и тормозные диски. Называем покрышки Tire, тормозные диски называем BrakeDisc. Для тормозных скоб (калиперов) создаем копии базового объекта, называем в соответствии с положение – FR_Caliper и оставляем их на том же уровне иерархии, что базовые объекты колеса, добавляем под них соответствующий калипер. Должно получится вот так:
9. Создаем пустой объект под r34, назваем его Body. Перетаскиваем под него всю крупную геометрию автомобиля, в т.ч. стекла фар. На этом скриншоте я отключил отображение геометрии, которую нужно отправить под объект Body, т.е. видно только то, что трогать не надо.
10. Выделяем все объекты под Body. Добавляем компонент MeshCollider (на панеле инспектора AddComponent -> MeshCollider) и жмем галочку Convex. Должно получится так:
Суть: если нужно, чтобы сталкивались два колайдера произвольной геометрии, то один из них должен быть «Convex». Такие колайдеры генерируются используя точки, максимально удаленные от центра модели.
В идеале геометрия, используемая колайдерами\физикой должна быть создано отдельно, с помощью специального плагина. Но, в этом уроке мы используем то, что у нас есть.
11. На коренной объект (r34) добавляем скрипт, контролирующий автомобиль.
12. На пустые объекты XX_Wheel добавляем WheelCollider’ы
13. Если запустить игру, как только WheelColider’ы касаются земли, машина улетает за горизонт. Причина в том, что WheelColider’ы взаимодействуют с колайдерами кузова автомобиля И в Rigidbody не выставлен параметр массы. Для того, чтобы решить эту проблему, нужно настроить слои объектов и настроить Collision Matrix (матрицу столкновений), чтобы объекты разных слоев не сталкивались. Кликаем по корневому объекту автомобиля (r34). На инспекторе, сверху слева Layer – Add Layer… Называем новый слой Car. Опять выбираем корневой объект автомобиля (r34) и там же выбираем слой Car. В диалоговом окне спросят хотим ли мы присовить все объекты иерархии к этому слою. Выбираем «yes, change children».
Повторяем операцию с объектом Wheels, новый слой будет называться Wheel.
Далее Edit -> Project Settings… -> Physics
В матрице столкновений отключаем взаимодействие слоев Car и Wheel, должно выглядеть так:
И выставляем в Rigidbody Mass = 1500.
14. Выставляем параметры WheelCollider’ов: Radius = 0.315; ForceAppPointDistance = 0.315; TargetPosition = 0;
15. Корректируем управляющий скрипт, для работы с калиперами
using UnityEngine; using System.Collections; using System.Collections.Generic; [System.Serializable] public class AxleInfo public WheelCollider leftWheel; public GameObject leftWheelVisuals; public GameObject leftWheelVisualsCalliper; private bool leftGrounded = false; private float travelL = 0f; private float leftAckermanCorrectionAngle = 0; public WheelCollider rightWheel; public GameObject rightWheelVisuals; public GameObject rightWheelVisualsCalliper; private bool rightGrounded = false; private float travelR = 0f; private float rightAckermanCorrectionAngle = 0; public bool motor; public bool steering; public float Antiroll = 10000; private float AntrollForce = 0; public float ackermanSteering = 1f; public void ApplyLocalPositionToVisuals() //left wheel if (leftWheelVisuals == null) return; > Vector3 position; Quaternion rotation; leftWheel.GetWorldPose(out position, out rotation); leftWheelVisuals.transform.position = position; leftWheelVisuals.transform.rotation = rotation; leftWheelVisualsCalliper.transform.position = position; float angles = rotation.eulerAngles.y; leftWheelVisualsCalliper.transform.rotation = Quaternion.Euler(0f, angles, 0f); //right wheel if (rightWheelVisuals == null) return; > rightWheel.GetWorldPose(out position, out rotation); rightWheelVisuals.transform.position = position; rightWheelVisuals.transform.rotation = rotation; rightWheelVisualsCalliper.transform.position = position; angles = rotation.eulerAngles.y; rightWheelVisualsCalliper.transform.rotation = Quaternion.Euler(0f, angles, 0f); > public void CalculateAndApplyAntiRollForce(Rigidbody theBody) WheelHit hit; leftGrounded = leftWheel.GetGroundHit(out hit); if (leftGrounded) travelL = (-leftWheel.transform.InverseTransformPoint(hit.point).y - leftWheel.radius) / leftWheel.suspensionDistance; else travelL = 1f; rightGrounded = rightWheel.GetGroundHit(out hit); if (rightGrounded) travelR = (-rightWheel.transform.InverseTransformPoint(hit.point).y - rightWheel.radius) / rightWheel.suspensionDistance; else travelR = 1f; AntrollForce = (travelL - travelR) * Antiroll; if (leftGrounded) theBody.AddForceAtPosition(leftWheel.transform.up * -AntrollForce, leftWheel.transform.position); if (rightGrounded) theBody.AddForceAtPosition(rightWheel.transform.up * AntrollForce, rightWheel.transform.position); > public void CalculateAndApplySteering (float input, float maxSteerAngle, List allAxles) //first find farest axle, we got to apply default values AxleInfo farestAxle = allAxles[0]; //calculate start point for checking float farestAxleDistantion = ((allAxles[0].leftWheel.transform.localPosition - allAxles[0].rightWheel.transform.localPosition) / 2f).z; for (int a = 0; a < allAxles.Count; a++) float theDistance = ((allAxles[a].leftWheel.transform.localPosition - allAxles[a].rightWheel.transform.localPosition)/ 2f).z; // if we found axle that farer - save it if (theDistance < farestAxleDistantion) farestAxleDistantion = theDistance; farestAxle = allAxles[a]; > > float wheelBaseWidth = (Mathf.Abs( leftWheel.transform.localPosition.x) + Mathf.Abs( rightWheel.transform.localPosition.x))/2; float wheelBaseLength = Mathf.Abs( ((farestAxle.leftWheel.transform.localPosition + farestAxle.rightWheel.transform.localPosition)/ 2f).z) + Mathf.Abs(((leftWheel.transform.localPosition + rightWheel.transform.localPosition) / 2f).z); float angle = maxSteerAngle * input; //ackerman implementation float turnRadius = Mathf.Abs(wheelBaseLength * Mathf.Tan(Mathf.Deg2Rad * (90 - Mathf.Abs(angle)))); //38.363 if (input != 0) //right wheel if (angle > 0) /turn right rightAckermanCorrectionAngle = Mathf.Rad2Deg * Mathf.Atan(wheelBaseLength / (turnRadius - wheelBaseWidth / 2f)); rightAckermanCorrectionAngle = (rightAckermanCorrectionAngle - Mathf.Abs(angle)) * ackermanSteering + (Mathf.Abs(angle)); rightAckermanCorrectionAngle = Mathf.Sign(angle) * rightAckermanCorrectionAngle; > else /turn left rightAckermanCorrectionAngle = Mathf.Rad2Deg * Mathf.Atan(wheelBaseLength / (turnRadius + wheelBaseWidth / 2f)); rightAckermanCorrectionAngle = (rightAckermanCorrectionAngle - Mathf.Abs(angle)) * ackermanSteering + (Mathf.Abs(angle)); rightAckermanCorrectionAngle = Mathf.Sign(angle) * rightAckermanCorrectionAngle; > //left wheel if (angle > 0) /turn right leftAckermanCorrectionAngle = Mathf.Rad2Deg * Mathf.Atan(wheelBaseLength / (turnRadius + wheelBaseWidth / 2f)); leftAckermanCorrectionAngle = (leftAckermanCorrectionAngle - Mathf.Abs(angle)) * ackermanSteering + (Mathf.Abs(angle)); leftAckermanCorrectionAngle = Mathf.Sign(angle) * leftAckermanCorrectionAngle; > else /turn left leftAckermanCorrectionAngle = Mathf.Rad2Deg * Mathf.Atan(wheelBaseLength / (turnRadius - wheelBaseWidth / 2f)); leftAckermanCorrectionAngle = (leftAckermanCorrectionAngle - Mathf.Abs(angle)) * ackermanSteering + (Mathf.Abs(angle)); leftAckermanCorrectionAngle = Mathf.Sign(angle) * leftAckermanCorrectionAngle; > > else rightAckermanCorrectionAngle = 0f; leftAckermanCorrectionAngle = 0f; > leftWheel.steerAngle = leftAckermanCorrectionAngle; rightWheel.steerAngle = rightAckermanCorrectionAngle; > > [RequireComponent(typeof(Rigidbody))] public class SimpleCarController : MonoBehaviour public List axleInfos; public float maxMotorTorque; public float maxSteeringAngle; private Rigidbody body; private void Start() body = GetComponent(); for (int a = 0; a < axleInfos.Count;a++) axleInfos[a].leftWheel.ConfigureVehicleSubsteps(5, 12, 15); axleInfos[a].rightWheel.ConfigureVehicleSubsteps(5, 12, 15); > > public void FixedUpdate() float motor = maxMotorTorque * Input.GetAxis("Vertical"); foreach (AxleInfo axleInfo in axleInfos) if (axleInfo.steering) axleInfo.CalculateAndApplySteering(Input.GetAxis("Horizontal"), maxSteeringAngle, axleInfos); > if (axleInfo.motor) axleInfo.leftWheel.motorTorque = motor; axleInfo.rightWheel.motorTorque = motor; > axleInfo.ApplyLocalPositionToVisuals(); axleInfo.CalculateAndApplyAntiRollForce(body); > > >
16. Колеса ходят «восьмерками»
Причина в том, что изначальная модель вырезана с небольшим отрицательным развалом колес. Поправляем модельки калиперов и самого колеса, на левой стороне ставим RotationZ = 4.5; на правой RotationZ = -4.5;
Восьмерка хоть и пропала, но колесо дергает вверх-вниз. Чтобы как-то сгладить ситуацию нужно подвинуть визуальные части колеса немного ниже, до Y = -0.269.
Так же, хочу обратить ваше внимание, что подобного рода проблемы возникают всегда, вне зависимости от качества базовых моделей\ассетов. Если вы хотите, чтобы Ваша система физики автомобиля была жизнеспособна, нужно учитывать подобного рода проблемы.
17. Настройка посадки
Ставим на всех WheelCollider’ах позицию Y на 0.47, а SuspencionDistance на 0.2.
Да, много работы
Но, в общем, если пользоваться «нормальными» моделями автомобилей, процесс гораздо быстрее.