Tank Tutorial: Ходовая (Урок 1) - Форум
  • Страница 1 из 1
  • 1
Модератор форума: terex  
Tank Tutorial: Ходовая (Урок 1)
terexДата: Воскресенье, 27.03.2011, 03:26 | Сообщение # 1
Zzz
Группа: Администраторы
Сообщений: 71
Награды: 1
Репутация: 32767
Статус: Offline

В первой части этого урока вы узнаете что такое Wheel Collider, как с помощью него управлять транспортным средством и создать реалистичное поведение подвески автомобиля, во второй части урока, на основе всего этого я покажу как сделать ходовую часть танка с соответствующей физикой и контроллерами.
Собственно то что вы видите на изображении сверху (это будет результат данного урока) вы можете опробовать «вживую» прямо в браузере. Хотите сделать это сами? Тогда добро пожаловать.

1. Создаем «Автомобиль»

Для начала проделайте стандартные процедуры, создайте сцену, создайте terrain, либо используйте уже готовые, затем создайте пустой Game Object(GO), (GameObject->Create Empty), назовем его какнибудь оригинально, например Car.

Затем создайте куб (GameObject->Create Other -> Cube) примените к нему какой нибудь материал из стандартных, чтобы его было лучше видно. Представим себе что это кузов нашего автомобиля, и растянем его с помощью Scale по ширине и длине так чтобы передняя часть этого кузова смотрела вдоль оси Z (синяя ось, именно она официально считается передом в мире Unity) затем перетянем наш Cube на объект Car во вкладке Hierarchy, чтобы он стал дочерним объекту Car. Перейдем на сам объект Car и добавим ему Rigidbody (Component -> Physics -> Rigidbody). Назначим ему соответствующий вес, примерно 1500 кг., кузов автомобиля готов.

Помимо кузова у автомобиля должны быть колеса, теперь создайте цилиндр (GameObject->Create Other -> Cylinder), используйте все тот же Scale, придайте ему вид колеса, затем разверните его по оси Z на 90 градусов (важно! Только по оси Z!).

Переименуем наш только что созданный цилиндр в Wheel Forward Left, установим колесо на свое место, тоесть спереди и слева от нашего куба, (повторяю, передом в Unity считается положительная чать оси Z).

Перетянем Wheel Forward Left на объект Car. Назначим колесу текстуру по детальнее чтобы лучше видеть вращение колеса. Теперь скопируем наше колесо (Правой кнопкой по Wheel Forward Left и Duplicate), назовем Wheel Back Left и поставим на законное место, по аналогии поступите с остальными двумя колесами.


Итак, наш суперкар почти готов! Поднимите его немного над поверхностью terrain’а и нажмите Play, автомобиль упадет на землю и встанет колом на колеса, о мягкой подвеске ему еще только мечтать, выходите из Play mode. Как видите у наших колес имеется компонент под названием Capsule Collider, а у кузова Box Collider, выберите каждое из наших колес и удалите у них Capsule Collider (Правой кнопкой по нему и Remove Component). Нажмите Play, теперь автомобиль больше не стоит на колесах, они прошли сквозь террейн и взаимодействует с поверхностью у нас теперь только кузов.

2. Добавляем Wheel Colliders
Создайте пустой GameObject назовите его WCollider Forward Left, теперь добавьте к нему компонент Wheel Collider (Component -> Physics -> Wheel Collider), он выглядит как окружность с отрезком внутри, он может быть слишком большим или слишком маленьким по сравнению с вашим колесом, все зависит от того насколько вы увлеклись использованием scale. К счастью у него есть параметр Radius, изменяя данный параметр подгоните его под радиус своего колеса, затем создайте новый префаб (Assets -> Create -> Prefab) назовите его WCollider затем перетяните WCollider Forward Left на префаб. Мы создали префаб для удобства, так как коллайдеров у нас будет столько же сколько колес, и чтобы не изменять параметры каждого, будем изменять параметры префаба, остальные коллайдеры унаследуют его свойства. Теперь делаем стандартную процедуру, перетаскиваем WCollider Forward Left на объект Car и помещаем в центр переднего левого колеса, дублируйте его 3 раза переименуйте и переместите в соответствии с другими колесами.


Нажмите Play, автомобиль упадет и опять встанет на колеса (кстати если колеса все равно проходят сквозь террейн, это значит что автомобиль падает на землю с очень большой скоростью, и вам просто напросто нужно уменьшить расстояние до земли, либо увеличите параметр Drag у Rigidbody). Как видите результат не сильно отличается от того где у нас на колёсах стояли Capsule Colliders, все правильно, потому, что мы не задали нашим Wheel Collider’ам никаких параметров.

Итак, выйдите из Game Mode, выберите наш префаб WCollider, поехали меняем свойства компонента Wheel Collider:
Suspension Distanse – по сути это длина пружины нашей подвески, ставим 0.15
Suspension Spring -> Spring – жесткость подвески, чем больше вес нашего Rigidbody, тем больше должна быть жесткость, ставим 9000
Suspension Spring -> Damper – Смягчитель подвески, большее значение заставляет пружину двигаться медленнее, ставим 100
Mass – масса колеса, оставляем 1
Forward Friction – «передняя» сила трения колеса, оставляем как есть
Sideways Friction – «боковая» сила трения колеса, полезно если хотим реализовать дрифт автомобиля, оставляем как есть
Итого у нас получилось:
(Не смотрите на радиус, у вас он может быть другой, главное чтобы окружность совпадала с вашим колесом)

Итак, нажмите Play сейчас, посмотрите, наш автомобиль ведет себя теперь совсем по другому, теперь он пружинит от поверхности террейна, а если вы поставите его на склон какой нибудь горки, он покатится с неё, вот так у нас работают Wheel Collider’ы.

Но это далеко не все на что они способны, давайте выясним как с помощью них управлять автомобилем и сделать так чтобы наши колеса крутились и реагировали на неровности ландшафта. Начинаем скриптовать!

3. Управляем Wheel Collider’ами

В этом уроке я буду писать скрипты на C#, простите меня JavaScript’еры, он мне привычнее, я думаю вы разберетесь.
Создаем новый С# скрипт (Assets -> Create -> C Sharp Script), назовем его CarController, открываем и напишем следующее:

Code
using UnityEngine;
using System.Collections;

public class CarController : MonoBehaviour { //1 Главный класс скрипта должен называться точно так же как и файл скрипта, иначе компилятор выдаст ошибку.
         
     public WheelCollider[] WColForward; //2 Массив передних коллайдеров.
     public WheelCollider[] WColBack;  //3 Массив задних коллайдеров.
         
     // Use this for initialization
     void Start () {  //4 Будем использовать функцию Start() для инициализации.
         
     }
         
         
     void FixedUpdate () {  //5 для физического взаимодействия с объектами лучше использовать функцию FixedUpdate(), нежели функцию Update().
          
         
     }
}

Главный класс скрипта должен называться точно так же как и файл скрипта, иначе компилятор выдаст ошибку.
Массив передних коллайдеров.
Массив задних коллайдеров.
Будем использовать функцию Start() для инициализации.
Как то в уроке "создание игрового меню" я говорил что для физического взаимодействия с объектами лучше использовать функцию FixedUpdate(), нежели функцию Update().
Затем перетяните скрипт на объект Car, как вы можете видеть в инспекторе, у нас в скрипте есть два поля, собственно это массивы которые мы объявили, в них надо перетянуть наши коллайдеры следующим образом.

Далее работаем с нашими коллайдерами, дополняем скрипт:

Code
using UnityEngine;
using System.Collections;

public class CarController : MonoBehaviour {
       
    public WheelCollider[] WColForward;
    public WheelCollider[] WColBack;
       
    public float maxSteer = 30; //Максимальный угол поворота колес.
    public float maxAccel = 25; //Максимальный крутящий момент передающийся на колесо.
    public float maxBrake = 50; //Максимальный тормозной момент.
       
       
    // Use this for initialization
    void Start () {
       
    }
       
       
    void FixedUpdate () {
        
     float accel = 0;
     float steer = 0;
          
     accel = Input.GetAxis("Vertical");  //функция принимает направление виртуальной оси
     steer = Input.GetAxis("Horizontal");  //функция принимает направление виртуальной оси
        
     CarMove(accel,steer); //Передаем результаты полученные от виртуальных осей в функцию CarMove()
        
    }
       
    private void CarMove(float accel,float steer){ //Передаем результаты полученные от виртуальных осей в функцию CarMove()
        
     foreach(WheelCollider col in WColForward){ //(угол поворота колеса)
      col.steerAngle = steer*maxSteer; //(угол поворота колеса)
     }
        
     if(accel == 0){ //отвечает за силу торможения, это нужно для того чтобы автомобиль не двигался по инерции если мы не нажимаем на клавиши движения.
      foreach(WheelCollider col in WColBack){  //отвечает за силу торможения, это нужно для того чтобы автомобиль не двигался по инерции если мы не нажимаем на клавиши движения.
       col.brakeTorque = maxBrake; //отвечает за силу торможения, это нужно для того чтобы автомобиль не двигался по инерции если мы не нажимаем на клавиши движения.
      }    
         
     }else{ //(если мы нажимаем на клавиши W или S)
              
      foreach(WheelCollider col in WColBack){ //(крутящий момент колеса) умножая её на значение полученное с вертикальной оси, вследствие чего наш автомобиль начинает ускоряться.
       col.brakeTorque = 0; //(крутящий момент колеса) умножая её на значение полученное с вертикальной оси, вследствие чего наш автомобиль начинает ускоряться.
       col.motorTorque = accel*maxAccel; //(крутящий момент колеса) умножая её на значение полученное с вертикальной оси, вследствие чего наш автомобиль начинает ускоряться.
      }    
         
     }
        
          
        
    }
}

Теперь можете нажать Play, и используя кнопки W,S или стрелки вперед — назад, привести в движение нашу «формулу один», а кнопками A,D, или стрелками влево – вправо, поворачивать.
 
terexДата: Воскресенье, 27.03.2011, 04:40 | Сообщение # 2
Zzz
Группа: Администраторы
Сообщений: 71
Награды: 1
Репутация: 32767
Статус: Offline
Вот и все, просто не правда ли? Осталось исправить мелкие недочеты, во – первых, если вы попытаетесь развернуться на большой скорости автомобиль завалится на бок, это происходит из-за того что rigidbody вычисляет центр тяжести нашего автомобиля на основе всех прикрепленных к нему коллайдеров, во – вторых наши колеса все еще не крутятся и не реагируют на неровности ландшафта.

Давайте обо всем по порядку.
4. Центр тяжести

Задается он удивительно просто, во первых нам надо создать пустой GO, назовем его Center of mass, затем перетянуть его на объект Car, и разместить его приблизительно там где вы считаете он должен быть, например чтобы моё авто не переворачивалось при повороте на высокой скорости мне пришлось сделать его вот так:


У вас он может располагаться по другому, все зависит от геометрических размеров Box Collider’а нашего кузова, так что по экспериментируйте с центром тяжести, но для начала дополните наш скрипт:

Объявите переменную: public Transform COM.
Перетащите GO Center of mass на поле COM в скрипте.
Внутри функции Start() напишите следующую строчку: rigidbody.centerOfMass = COM.localPosition

Теперь можете нажать Play и проверить результат.

5. «Оживляем» колеса

Ну вот мы и добрались до финальной, на мой взгляд самой интересной в этом уроке и в то же время довольно сложной темы.

Для того чтобы оживить колесо, нам необходимо вычислять позицию и угол его вращения в каждом фиксированном кадре, в этом нам опять поможет чудесный WheelCollider, его метод GetGroundHit(), который способен вернуть структуру WheelHit в которой в свою очередь содержится точка соприкосновения коллайдера и террейна (переменная point). Благодаря методу GetGroundHit() мы можем вычислить позицию колеса на основе движения пружины подвески. Ну а что касается угла поворота – это совсем просто, в WheelCollider’е есть float переменная rpm, это аббревиатура от rotation per minute, на её основе мы и можем определить угол вращения колеса.

Переходим собственно к скрипту, он вырос, потолстел и теперь выглядит так:

Code
using UnityEngine;
using System.Collections;

public class CarController : MonoBehaviour {
       
    public WheelCollider[] WColForward;
    public WheelCollider[] WColBack;
       
    public Transform[] wheelsF; // 1 Создаем массивы которые будут хранить Transform наших колес.
    public Transform[] wheelsB; // 1
       
    public float wheelOffset = 0.1f; // 2 Данные переменные нам пригодятся, ниже объясню для чего.
    public float wheelRadius = 0.13f; //2   
       
    public float maxSteer = 30;
    public float maxAccel = 25;
    public float maxBrake = 50;
       
    public Transform COM;
       
    public class WheelData{ //3 Создадим класс который будет содержать нужную нам информацию о каждом нашем колесе, а именно:
     public Transform wheelTransform; //4 Transform колеса;
     public WheelCollider col; //5 Wheel Collider Колеса;
     public Vector3 wheelStartPos; //6 Стартовую позицию колеса;
     public float rotation = 0.0f;  //7 Угол вращения колеса.
    }
       
    protected WheelData[] wheels; //8 Объявляем массив wheels c типом WheelData.
       
    // Use this for initialization
       
        
    void Start () {
     rigidbody.centerOfMass = COM.localPosition;
        
     wheels = new WheelData[WColForward.Length+WColBack.Length]; //8
        
     for (int i = 0; i<WColForward.Length; i++){ //9 Передаем в массив wheels необходимые нам данные, для этого я написал функцию SetupWheels()
      wheels[i] = SetupWheels(wheelsF[i],WColForward[i]); //9
     }
        
     for (int i = 0; i<WColBack.Length; i++){ //9   
      wheels[i+WColForward.Length] = SetupWheels(wheelsB[i],WColBack[i]); //9   
     }
        
    }
       
       
    private WheelData SetupWheels(Transform wheel, WheelCollider col){ //10 Функция SetupWheels() принимает Transform колеса и его WheelCollider, передает в переменные содержащиеся в классе WheelData необходимые нам данные и возвращает его.
     WheelData result = new WheelData();    
        
     result.wheelTransform = wheel; //10
     result.col = col; //10
     result.wheelStartPos = wheel.transform.localPosition; //10
        
     return result; //10
        
    }
       
    void FixedUpdate () {
        
     float accel = 0;
     float steer = 0;
          
     accel = Input.GetAxis("Vertical");
     steer = Input.GetAxis("Horizontal");     
        
     CarMove(accel,steer);
     UpdateWheels(); //11 Напишем функцию UpdateWheels() в которой будет вычисляться позиция и угол поворота наших колес.
    }
       
       
    private void UpdateWheels(){ //11
     float delta = Time.fixedDeltaTime; //12 Запоминаем переменную fixedDeltaTime класса Time она нужна нам для того чтобы вращение колеса было равномерно растянуто по времени.
        
        
     foreach (WheelData w in wheels){ //13 Для каждого элемента массива wheels выполняем следующие операции:
      WheelHit hit; //14 Создаем переменную класса WheelHit (о ней я рассказывал выше);
              
      Vector3 lp = w.wheelTransform.localPosition; //15 Запоминаем локальную позицию колеса (локальная позиция – это позиция относительно родительских координат, глобальная – относительно мировых);
      if(w.col.GetGroundHit(out hit)){ //16 Если WheelCollider колеса сталкивается с поверхностью террейна (либо чего нибудь другого);
       lp.y -= Vector3.Dot(w.wheelTransform.position - hit.point, transform.up) - wheelRadius; //17 из всего этого вычитаем еще и переменную wheelRadius чтобы колесо заняло правильное место.
      }else{ //18 Если WheelCollider не касается поверхности террейна. То из координаты Y начальной локальной позиции колеса отнимаем wheelOffset, благодаря этому наши колеса не улетают в неизвестном направлении когда автомобиль падает с высоты или лежит на «спине»;
          
       lp.y = w.wheelStartPos.y - wheelOffset; //18
      }
      w.wheelTransform.localPosition = lp; //19 Применяем измененную позицию колеса к его текущей позиции;
         
         
      w.rotation = Mathf.Repeat(w.rotation + delta * w.col.rpm * 360.0f / 60.0f, 360.0f); //20 Вычисляем угол «вращения» колеса, используя функцию Repeat() класса Mathf,
      w.wheelTransform.localRotation = Quaternion.Euler(w.rotation, w.col.steerAngle, 90.0f); //21 Применяем к текущим локальным углам поворота получившийся результат
     }    
        
    }
       
    private void CarMove(float accel,float steer){
        
     foreach(WheelCollider col in WColForward){
      col.steerAngle = steer*maxSteer;
     }
        
     if(accel == 0){
      foreach(WheelCollider col in WColBack){
       col.brakeTorque = maxBrake;
      }    
         
     }else{
              
      foreach(WheelCollider col in WColBack){
       col.brakeTorque = 0;
       col.motorTorque    = accel*maxAccel;
      }    
         
     }
        
          
        
    }
       
}

Скопируйте его, вставьте в свой скрипт, затем во вкладке hierarchy выберите наш объект Car, как вы можете видеть в инспекторе у нас появились новые переменные и массивы в скрипте, в массивы wheelsF и wheelsB необходимо передать передние и задние колеса соответственно, (колеса а не Wheel Collier’ы!) как сделаете это нажмите Play. Если колеса уходят под террейн то поменяйте значение переменной wheelRadius, оно должно примерно совпадать с радиусом ваших коллайдеров.

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

Если же они крутятся у вас не по той оси, или в другую сторону, то вам следует перечитать п. 1 где мы создавали автомобиль, я там говорил о том что важно повернуть колеса именно по оси Z на 90 градусов
Урок 2>>

 
^DEmON^Дата: Суббота, 10.03.2012, 09:57 | Сообщение # 3
Рядовой
Группа: Пользователи
Сообщений: 1
Награды: 0
Репутация: 0
Статус: Offline
у меня возникла проблема с третьим уроком когда переносишь скрипт CarController на объект Car то unity выдаёт вот что "Can't add script behaviour CarController. the script file name does not match the name of the class defined in the script!" что мне сделать чтоб CarController переместился в объект Car?
 
  • Страница 1 из 1
  • 1
Поиск:
 Сайт временно находится в стадии разработки, приношу свои извинения за не удобства
 
 This site is temporarily under construction, I apologize for the convenience of not