当前位置:   article > 正文

Unity中的车辆控制_unity wheelcollider 汽车滑移率

unity wheelcollider 汽车滑移率


        WheelCollider(车轮碰撞器)是一种特殊的地面车辆碰撞器,它具有内置的碰撞检测、车轮物理引擎和一个基于滑移的轮胎摩擦模型。WheelCollider是专门为有轮子的车辆所做的设计。此处把坦克作为车辆来实现坦克的车辆碰撞和车辆运动。

车轮碰撞器

      在添加轮子时,给车辆(此处是坦克整体tank)添加碰撞器Rigidbody,且坦克较重,需要在Rigidbody中调整坦克的质量(Mass属性,这里调整为300),如下图:

在这里插入图片描述
       在坦克模型下建立名为PhysicalBody的空物体,用于存放坦克的碰撞器组件。再在PhysicalBody里添加wheelL1、wheelL2、wheelR1、wheelR2这4个空物体代表坦克的四个轮子。在PhysicalBody里添加2个名为collider的空物体,用于给坦克添加碰撞器(坦克车身有上下两部分,故添加2个collider),此时坦克层次模型如下图:
在这里插入图片描述
       给2个collider添加BoxCollider组件(Component --> Physics --> Box Collider),然后点击collider的属性面板的Edit Collider调整位置和大小。再给4个轮子分别添加WheelCollider(Component --> Physics --> WheelCollider),调整位置和大小,注意轮子的位置、大小、角度的细微差别会影响物理性能,所以要根据Transform的数值进行调整。L1表示左前轮,L2表示左后轮,R1表示右前轮,R2表示右后轮。最后调整结果见下图:

在这里插入图片描述

WheelCollider的属性

       WheelCollider是用Unity3D制作汽车类型游戏的关键所在,它不仅可以模拟轮子的碰撞过程,还模拟了汽车的悬挂系统、引擎系统、轮胎摩擦等汽车的关键物理特性。WheelCollider的属性及说明见下表:

属性说明
mass车轮的质量
radius轮子半径
suspensionDistance车轮悬挂的最大延长距离
center轮子的中心位置(相对于本地坐标系)
suspensionSpring车轮悬挂的参数,通过添加弹簧和阻尼力,悬挂试图达到的目标位置
forwardFriction在车轮指向方向上的摩擦力的属性
sidewaysFriction轮胎侧面方向上的摩擦力的属性

       车轮是由motorTorque、brakeTorque和steerAngle属性控制的,它们的含义见下表:

属性或方法说明
motorTorque在轮轴上的电机力矩
brakeTorque刹车的力矩
steerAngle车轮转向角度

       一般来说,重心、悬挂系统和轮胎(摩擦力属性)对汽车的性能有着很大的影响。

  1. 重心:由于车辆的重心通常不是车的中心位置,若要得到更真实的效果,可以通过代码设置Rigidbody.centerOfMass来调整重心位置。
  2. 悬挂系统:车的悬挂系统可以增强轮胎和路面的摩擦。下面是与悬挂系统有关的参数及说明:
属性或方法说明
spring悬挂弹簧。该值决定了 悬挂弹簧的刚性,把它设得很高可以使弹簧很软,所以车轮将有更大的震动范围,把它设得很小,将使悬挂很硬
damper悬挂阻尼器,可以使弹簧震动变得平滑
targetPosition目标位置。静止状态下悬挂的距离,0表示充分伸展弹簧,1表示充分压缩弹簧
  1. 轮胎摩擦力:Forward Friction是前后方向的摩擦力属性,影响motorTorque和brakeTorque的效果。Sideways Friction是左右方向的摩擦力属性,影响steerAngle的效果。下面是与摩擦力相关的参数及说明:
参数说明
extremumSlip滑动极值点(默认为1)
exyremumValue滑动极值的力(默认为20000)
asymptoteSlip渐近线滑动点(默认为2)
asymptoteValue渐近线滑动上的力(默认为10000)
stiffness用于extremumValue和asymptoteValue值的倍数(默认为1)

控制车辆

       汽车的前轮和后轮分别悬挂在两条轴上,每条轴上两个轮子的步调是一致的。新建名为AxleInfo的Script脚本,添加代表车轴信息的AxleInfo类,(此处的炮管可参见前面的博客)代码如下:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
[System.Serializable] //代表串行化,添加该标记的目的在于,使类成员可以在属性面板中显示
public class AxleInfo 
{
    public WheelCollider leftWheel;
    public WheelCollider rightWheel;
    public bool motor;
    public bool steering;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

      编写名为tank的Script脚本拖到坦克上,代码如下:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Tank : MonoBehaviour
{
  //炮塔
  public Transform turret;
  //炮塔旋转速度
  private float turretRotSpeed = 0.5f;

  //炮管
  public Transform gun;
  //炮管的旋转范围
  private float maxRoll = 10f;
  private float minRoll = -4f;
  //炮塔炮管目标角度
  private float turretRotTarget = 0;
  private float turretRollTarget = 0;

  //轮轴
  public List<AxleInfo> axleInfos;
  //马力和最大马力
  private float motor = 0;
  public float maxMotorTorque;
  //制动和最大制动
  private float brakeTorque = 0;
  public float maxBrakeTorque = 100;
  //转向角和最大转向角
  private float steering = 0;
  public float maxSteeringAngle;

  //开始时执行
  void Start()
  {
      //获取炮塔
      turret = transform.Find("turret");
      //获取炮塔
      gun = turret.Find("gun");
  }

  //每帧执行一次
  void Update()
  {
     
      //玩家控制操作
      PlayerCtrl();
      //遍历车轴
      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;
          }
          //制动
          if (true)
          {
              axleInfo.leftWheel.brakeTorque = brakeTorque;
              axleInfo.rightWheel.brakeTorque = brakeTorque;
          }
      }
      //炮塔炮管旋转
      TurretRotation();
      TurretRoll();
  }

  //炮塔旋转
  public void TurretRotation()
  {
      if (Camera.main == null)
          return;
      if (turret == null)
          return;
      //归一化角度
      float angle = turret.eulerAngles.y - turretRotTarget;
      if (angle < 0)
          angle += 360;
      if (angle > turretRotSpeed && angle < 180)
          turret.Rotate(0f, -turretRotSpeed, 0f);
      else if (angle > 180 && angle < 360 - turretRotSpeed)
          turret.Rotate(0f, turretRotSpeed, 0f);
  }

  //旋转炮管
  public void TurretRoll()
  {
      if (Camera.main == null)
          return;
      if (turret == null)
          return;
      //获取角度
      Vector3 worldEuler = gun.eulerAngles;
      Vector3 localEuler = gun.localEulerAngles;
      //世界坐标系角度计算
      worldEuler.x = turretRollTarget;
      gun.eulerAngles = worldEuler;
      //本地坐标系角度限制
      Vector3 euler = gun.localEulerAngles;
      if (euler.x > 180)
          euler.x -= 360;
      if (euler.x > maxRoll)
          euler.x = maxRoll;
      if (euler.x < minRoll)
          euler.x = minRoll;
      gun.localEulerAngles = new Vector3(euler.x, localEuler.y, localEuler.z);
  }

  //玩家控制
  public void PlayerCtrl()
  {
      //马力和转向角
      motor = maxMotorTorque * Input.GetAxis("Vertical");
      steering = maxSteeringAngle * Input.GetAxis("Horizontal");
      //炮管炮塔角度
      turretRotTarget = Camera.main.transform.eulerAngles.y;
      turretRollTarget = Camera.main.transform.eulerAngles.x;
  }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125

     最后设置tank模型的AxleInfos的size设置为2(前后两条车轴),并将4个WheelCollider拖入其中,设置为后驱,如下图:

在这里插入图片描述
     选择坦克的4个WheelCollider,调整参数,可以得到更好的体验,见下图:

在这里插入图片描述

刹车

     玩家按下后退键时,是刹车还是后退。可以使用车轮碰撞器的rpm(转速)属性判断。如果转速大于某个值(这里取5),可以视为坦克前进,小于某个值(这里取-5)为后退,转速为0则表示静止,而不单单根据后退键来看。修改PlayerCtrl,代码如下:

 public void PlayerCtrl()
    {
        //马力和转向角
        motor = maxMotorTorque * Input.GetAxis("Vertical");
        steering = maxSteeringAngle * Input.GetAxis("Horizontal");
        //制动
        brakeTorque = 0;
        foreach(AxleInfo axlenInfo in axleInfos)
        {
            //前进时,按下“下”键
            if (axlenInfo.leftWheel.rpm > 5 && motor < 0)
                brakeTorque = maxBrakeTorque;
            //后退时,按下“上”键
            else if(axlenInfo.leftWheel.rpm <-5 && motor > 0)
                brakeTorque = maxBrakeTorque;
            continue;
        }
        //炮管炮塔角度
        turretRotTarget = Camera.main.transform.eulerAngles.y;
        turretRollTarget = Camera.main.transform.eulerAngles.x;
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21

     运行游戏,可以体验到刹车效果。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/花生_TL007/article/detail/109170
推荐阅读
相关标签
  

闽ICP备14008679号