来自于:小陀螺_Coder
    最近要写汽车的控制,发现比人物控制难很多,涉及到汽车的很多知识,自己写了一点不忍直视,各种bug,然后被告知untiy官方资源包里有控制脚本,就去学习了一下,然后借鉴了网上的一些教程,在这里表示衷心感谢,为了方面自己和他人,免得自己弄丢,然后贴在这里:

    1. using System;
    2. using UnityEngine;
    3. namespace UnityStandardAssets.Vehicles.Car//此处可改成自己的car名字
    4. {
    5. internal enum CarDriveType
    6. {
    7. FrontWheelDrive,//前驱
    8. RearWheelDrive,//后驱
    9. FourWheelDrive//四驱
    10. }
    11. internal enum SpeedType
    12. {
    13. MPH,//英里
    14. KPH//千米/h
    15. }
    16. public class CarController : MonoBehaviour
    17. {
    18. //[SerializeField]是为了成员变量可以在检视面板上显示
    19. [SerializeField] private CarDriveType m_CarDriveType = CarDriveType.FourWheelDrive; //四驱
    20. [SerializeField] private WheelCollider[] m_WheelColliders = new WheelCollider[4]; //存放轮子的数组
    21. [SerializeField] private GameObject[] m_WheelMeshes = new GameObject[4]; //车轮模型
    22. [SerializeField] private WheelEffects[] m_WheelEffects = new WheelEffects[4];//存放特效数组,粒子,音效等
    23. [SerializeField] private Vector3 m_CentreOfMassOffset;//车的重心
    24. [SerializeField] private float m_MaximumSteerAngle;//最大可转角度
    25. [Range(0, 1)] [SerializeField] private float m_SteerHelper; // 0 is raw physics , 1 the car will grip in the direction it is facing//初始化为0,1是车被控制面对的方向
    26. [Range(0, 1)] [SerializeField] private float m_TractionControl; // 0 is no traction control, 1 is full interference///0没有牵引力控制系统,1是完整的干扰
    27. //所有车轮的扭矩 扭矩越大,加速性能越好;扭矩越小,加速性能越差
    28. //如某车的1挡齿比(齿轮的齿数比,本质就是齿轮的半径比)是3,尾牙为4,轮胎半径为0.3米,原扭矩是200Nm的话,最后在轮轴的扭矩就变成200×3×4=2400Nm,
    29. //再除以轮胎半径0.3米后,轮胎与地面摩擦的部分就有2400Nm/0.3m=8000N,即800公斤力的驱动力,这就足以驱动汽车了
    30. //驱动力公式:驱动力=扭矩×变速箱齿比×主减速器速比×机械效率÷轮胎半径
    31. [SerializeField] private float m_FullTorqueOverAllWheels;//所有轮胎的扭矩
    32. [SerializeField] private float m_ReverseTorque;//反向扭矩
    33. [SerializeField] private float m_MaxHandbrakeTorque;//最大刹车扭矩
    34. [SerializeField] private float m_Downforce = 100f;//下压力
    35. [SerializeField] private SpeedType m_SpeedType;//速度类型
    36. [SerializeField] private float m_Topspeed = 200;//最大速度
    37. [SerializeField] private static int NoOfGears = 5;//档位总数
    38. [SerializeField] private float m_RevRangeBoundary = 1f;//最大滑动距离
    39. [SerializeField] private float m_SlipLimit;//轮胎下滑给定的阈值
    40. [SerializeField] private float m_BrakeTorque;//刹车扭矩
    41. private Quaternion[] m_WheelMeshLocalRotations;//四元数组存储车轮本地转动数据
    42. private Vector3 m_Prevpos, m_Pos;
    43. private float m_SteerAngle; //转向角
    44. private int m_GearNum;//当前档位数
    45. private float m_GearFactor;//挡位因子
    46. private float m_OldRotation;//用于转动计算
    47. private float m_CurrentTorque;//当前扭矩
    48. private Rigidbody m_Rigidbody;//刚体
    49. private const float k_ReversingThreshold = 0.01f; //反转阈值
    50. //角色信息访问控制,对外接口
    51. public bool Skidding { get; private set; } //是否打滑
    52. public float BrakeInput { get; private set; } //制动输入
    53. public float CurrentSteerAngle{ get { return m_SteerAngle; }}//当前车轮角度
    54. public float CurrentSpeed{ get { return m_Rigidbody.velocity.magnitude*2.23693629f; }}//当前速度
    55. public float MaxSpeed{get { return m_Topspeed; }}//最大速度
    56. public float Revs { get; private set; }//转速属性
    57. public float AccelInput { get; private set; } //加速输入
    58. // Use this for initialization
    59. //初始化
    60. private void Start()
    61. {
    62. Transform wheelParent = transform.Find("Colliders/Wheels"); //获得当前车轮碰撞器
    63. GameObject wheelMesh = transform.Find("Car/wheel_mesh").gameObject; //获取车轮模型位置
    64. for (int i = 0; i < 4; i++)
    65. {
    66. m_WheelColliders[i] = wheelParent.GetChild(i).GetComponent<WheelCollider>();
    67. m_WheelMeshes[i] = wheelMesh;
    68. }
    69. m_WheelMeshLocalRotations = new Quaternion[4]; //获得车轮的四元数的角度信息
    70. for (int i = 0; i < 4; i++)
    71. {
    72. m_WheelMeshLocalRotations[i] = m_WheelMeshes[i].transform.localRotation;
    73. }
    74. m_WheelColliders[0].attachedRigidbody.centerOfMass = m_CentreOfMassOffset;
    75. m_MaxHandbrakeTorque = float.MaxValue;
    76. m_Rigidbody = GetComponent<Rigidbody>();
    77. //当前扭矩=全部扭矩-(牵引系数【0-1】*全部扭矩)
    78. //设置当前扭矩,初始化的扭矩值跟m_TractionControl大小有关,m_TractionControl决定是否有牵引力,如果m_TractionControl
    79. //值为0,则当前扭矩直接就是最大值,如果该值为1,则初始扭矩为0,然后汽车启动慢慢增加扭矩力。建议m_TractionControl数值为0.5
    80. m_CurrentTorque = m_FullTorqueOverAllWheels - (m_TractionControl*m_FullTorqueOverAllWheels);//
    81. }
    82. //变档函数
    83. private void GearChanging()
    84. {
    85. float f = Mathf.Abs(CurrentSpeed/MaxSpeed);
    86. float upgearlimit = (1/(float) NoOfGears)*(m_GearNum + 1);
    87. float downgearlimit = (1/(float) NoOfGears)*m_GearNum;
    88. if (m_GearNum > 0 && f < downgearlimit)
    89. {
    90. m_GearNum--;
    91. }
    92. if (f > upgearlimit && (m_GearNum < (NoOfGears - 1)))
    93. {
    94. m_GearNum++;
    95. }
    96. }
    97. //在0-1范围内为值添加一个曲线偏向1的简单函数
    98. // simple function to add a curved bias towards 1 for a value in the 0-1 range
    99. private static float CurveFactor(float factor)
    100. {
    101. return 1 - (1 - factor)*(1 - factor);
    102. }
    103. //松开插值的版本,允许值超出范围
    104. // unclamped version of Lerp, to allow value to exceed the from-to range
    105. private static float ULerp(float from, float to, float value)
    106. {
    107. return (1.0f - value)*from + value*to;
    108. }
    109. //计算齿轮因素/档位因子
    110. private void CalculateGearFactor()
    111. {
    112. float f = (1/(float) NoOfGears);
    113. // gear factor is a normalised representation of the current speed within the current gear's range of speeds.
    114. // We smooth towards the 'target' gear factor, so that revs don't instantly snap up or down when changing gear.
    115. //齿轮因素是在当前齿轮的速度范围的正常表示。我们顺利走向“目标”装备因素,所以转速时不要立即调节或向下改变齿轮。
    116. //目标齿轮数=(当前速度/最大速度)*最大齿轮数-当前齿轮数
    117. //我们要让值平滑地想着目标移动,以保证转速不会在变换档位时突然地上高或者降低
    118. //反向差值,通过当前速度的比例值,找当前速度在当前档位的比例位置,得到的值将是一个0~1范围内的值。
    119. var targetGearFactor = Mathf.InverseLerp(f*m_GearNum, f*(m_GearNum + 1), Mathf.Abs(CurrentSpeed/MaxSpeed));//反插值,计算比例值(5,10,8)=(8-5)/(10-5)=3/5
    120. m_GearFactor = Mathf.Lerp(m_GearFactor, targetGearFactor, Time.deltaTime*5f); //从当前档位因子向目标档位因子做平滑差值
    121. }
    122. //计算引擎转速(显示/声音)
    123. private void CalculateRevs()
    124. {
    125. // calculate engine revs (for display / sound)
    126. // (this is done in retrospect - revs are not used in force/power calculations)
    127. CalculateGearFactor();
    128. var gearNumFactor = m_GearNum/(float) NoOfGears;
    129. var revsRangeMin = ULerp(0f, m_RevRangeBoundary, CurveFactor(gearNumFactor));
    130. var revsRangeMax = ULerp(m_RevRangeBoundary, 1f, gearNumFactor);
    131. Revs = ULerp(revsRangeMin, revsRangeMax, m_GearFactor);
    132. }
    133. //运动函数,最重要
    134. public void Move(float steering, float accel, float footbrake, float handbrake)
    135. {
    136. //保持当前轮胎网格跟随wheelcolliders转动
    137. for (int i = 0; i < 4; i++)
    138. {
    139. Quaternion quat;//四元数quat,用于旋转
    140. Vector3 position;
    141. m_WheelColliders[i].GetWorldPose(out position, out quat);//获取wheelcolliders的姿势,位置和转向角
    142. //设置网格物体的位置和转向角
    143. m_WheelMeshes[i].transform.position = position;
    144. m_WheelMeshes[i].transform.rotation = quat;
    145. }
    146. //clamp input values
    147. steering = Mathf.Clamp(steering, -1, 1);//限制steering的值在-1和1之间,以下同上
    148. AccelInput = accel = Mathf.Clamp(accel, 0, 1);
    149. BrakeInput = footbrake = -1*Mathf.Clamp(footbrake, -1, 0);
    150. handbrake = Mathf.Clamp(handbrake, 0, 1);
    151. //Set the steer on the front wheels.
    152. //Assuming that wheels 0 and 1 are the front wheels.
    153. //设置前轮转向角,0,1为前轮,汽车行驶时也只是控制前轮转向
    154. m_SteerAngle = steering*m_MaximumSteerAngle;
    155. m_WheelColliders[0].steerAngle = m_SteerAngle;
    156. m_WheelColliders[1].steerAngle = m_SteerAngle;
    157. //调用角度辅助助手
    158. SteerHelper();
    159. //设置加速/刹车信息到WheelCollider
    160. ApplyDrive(accel, footbrake);
    161. //检查速度范围
    162. CapSpeed();
    163. //Set the handbrake.设置手刹
    164. //Assuming that wheels 2 and 3 are the rear wheels.2,3代表后轮
    165. if (handbrake > 0f)
    166. {
    167. //设置手刹值到后轮,达到减速目的
    168. var hbTorque = handbrake*m_MaxHandbrakeTorque;
    169. m_WheelColliders[2].brakeTorque = hbTorque;
    170. m_WheelColliders[3].brakeTorque = hbTorque;
    171. }
    172. //计算转速,用来供外部调用转速属性Revs来播放引擎声音等
    173. CalculateRevs();
    174. //改变档位
    175. GearChanging();
    176. //施加下压力
    177. AddDownForce();
    178. //检查轮胎
    179. CheckForWheelSpin();
    180. //牵引力控制系统
    181. TractionControl();
    182. }
    183. //检查速度范围
    184. private void CapSpeed()
    185. {
    186. float speed = m_Rigidbody.velocity.magnitude;//将标量速度赋予speed
    187. //判断速度类型,
    188. switch (m_SpeedType)
    189. {
    190. case SpeedType.MPH:
    191. speed *= 2.23693629f;
    192. if (speed > m_Topspeed)
    193. m_Rigidbody.velocity = (m_Topspeed/2.23693629f) * m_Rigidbody.velocity.normalized;//速度归一化
    194. break;
    195. case SpeedType.KPH:
    196. speed *= 3.6f;
    197. if (speed > m_Topspeed)
    198. m_Rigidbody.velocity = (m_Topspeed/3.6f) * m_Rigidbody.velocity.normalized;
    199. break;
    200. }
    201. }
    202. //设置加速/刹车信息到WheelCollider
    203. private void ApplyDrive(float accel, float footbrake)
    204. {
    205. float thrustTorque;
    206. switch (m_CarDriveType)
    207. {
    208. case CarDriveType.FourWheelDrive:
    209. thrustTorque = accel * (m_CurrentTorque / 4f);
    210. for (int i = 0; i < 4; i++)
    211. {
    212. m_WheelColliders[i].motorTorque = thrustTorque;
    213. }
    214. break;
    215. case CarDriveType.FrontWheelDrive:
    216. thrustTorque = accel * (m_CurrentTorque / 2f);
    217. m_WheelColliders[0].motorTorque = m_WheelColliders[1].motorTorque = thrustTorque;
    218. break;
    219. case CarDriveType.RearWheelDrive:
    220. thrustTorque = accel * (m_CurrentTorque / 2f);
    221. m_WheelColliders[2].motorTorque = m_WheelColliders[3].motorTorque = thrustTorque;
    222. break;
    223. }
    224. for (int i = 0; i < 4; i++)
    225. {
    226. if (CurrentSpeed > 5 && Vector3.Angle(transform.forward, m_Rigidbody.velocity) < 50f)
    227. {
    228. m_WheelColliders[i].brakeTorque = m_BrakeTorque*footbrake;
    229. }
    230. else if (footbrake > 0)
    231. {
    232. m_WheelColliders[i].brakeTorque = 0f;
    233. m_WheelColliders[i].motorTorque = -m_ReverseTorque*footbrake;
    234. }
    235. }
    236. }
    237. //此函数时move函数用于转向角
    238. private void SteerHelper()
    239. {
    240. for (int i = 0; i < 4; i++)
    241. {
    242. WheelHit wheelhit;
    243. m_WheelColliders[i].GetGroundHit(out wheelhit);
    244. if (wheelhit.normal == Vector3.zero)
    245. return; // wheels arent on the ground so dont realign the rigidbody velocity 车轮离地,则不用调整汽车角度了。
    246. }
    247. // this if is needed to avoid gimbal lock problems that will make the car suddenly shift direction
    248. //使用四元数避免万向锁的问题
    249. //假如上一次车体Y方向角度比这次小于十度,就根据相差的度数乘以系数m_SteerHelper,得出需要旋转的度数
    250. //根据这个度数算出四元数,然后将刚体速度直接旋转这个偏移度数
    251. if (Mathf.Abs(m_OldRotation - transform.eulerAngles.y) < 10f)
    252. {
    253. var turnadjust = (transform.eulerAngles.y - m_OldRotation) * m_SteerHelper;
    254. Quaternion velRotation = Quaternion.AngleAxis(turnadjust, Vector3.up);
    255. m_Rigidbody.velocity = velRotation * m_Rigidbody.velocity;
    256. }
    257. m_OldRotation = transform.eulerAngles.y;
    258. }
    259. // this is used to add more grip in relation to speed
    260. //这个是用来增加下压力,这是用来添加更多的速度控制
    261. private void AddDownForce()
    262. {
    263. m_WheelColliders[0].attachedRigidbody.AddForce(-transform.up*m_Downforce*
    264. m_WheelColliders[0].attachedRigidbody.velocity.magnitude);//Addforce 第一个车轮增加一个力,
    265. }
    266. // checks if the wheels are spinning and is so does three things 检查轮胎是否旋转
    267. // 1) emits particles 是否发射粒子
    268. // 2) plays tiure skidding sounds 播放滑行音
    269. // 3) leaves skidmarks on the ground 去掉刹车印
    270. // these effects are controlled through the WheelEffects class 这些特效都是通过类WheelEffects实现的
    271. private void CheckForWheelSpin()
    272. {
    273. // loop through all wheels 遍历所有车轮
    274. for (int i = 0; i < 4; i++)
    275. {
    276. WheelHit wheelHit;
    277. m_WheelColliders[i].GetGroundHit(out wheelHit);//获取碰撞信息
    278. // is the tire slipping above the given threshhold
    279. //轮胎下滑超过给定的阈值吗
    280. if (Mathf.Abs(wheelHit.forwardSlip) >= m_SlipLimit || Mathf.Abs(wheelHit.sidewaysSlip) >= m_SlipLimit)
    281. {
    282. //超出则发射烟雾粒子
    283. m_WheelEffects[i].EmitTyreSmoke();
    284. // avoiding all four tires screeching at the same time
    285. // if they do it can lead to some strange audio artefacts
    286. //避免四个轮胎都同时播放滑行声音
    287. //如果那样的话会导致某些奇怪的音效
    288. //函数AnySkidSoundPlaying()是遍历四个轮子,检查是否播放音效,是的返回ture.
    289. if (!AnySkidSoundPlaying())
    290. {
    291. m_WheelEffects[i].PlayAudio();
    292. }
    293. continue;
    294. }
    295. // if it wasnt slipping stop all the audio
    296. //假如没有超出阈值,还没有停止音效,则停止音效
    297. if (m_WheelEffects[i].PlayingAudio)
    298. {
    299. m_WheelEffects[i].StopAudio();
    300. }
    301. // end the trail generation
    302. //停止烟雾生成
    303. m_WheelEffects[i].EndSkidTrail();
    304. }
    305. }
    306. //如果汽车轮胎过度滑转,牵引力系统可以控制减少轮胎动力
    307. // crude traction control that reduces the power to wheel if the car is wheel spinning too much
    308. private void TractionControl()//牵引力控制函数
    309. {
    310. WheelHit wheelHit;
    311. //判断驱动类型,四轮,还是前后轮,然后调用AdjustTorque函数
    312. switch (m_CarDriveType)
    313. {
    314. case CarDriveType.FourWheelDrive:
    315. // loop through all wheels
    316. for (int i = 0; i < 4; i++)
    317. {
    318. m_WheelColliders[i].GetGroundHit(out wheelHit);
    319. AdjustTorque(wheelHit.forwardSlip);
    320. }
    321. break;
    322. case CarDriveType.RearWheelDrive:
    323. m_WheelColliders[2].GetGroundHit(out wheelHit);
    324. AdjustTorque(wheelHit.forwardSlip);
    325. m_WheelColliders[3].GetGroundHit(out wheelHit);
    326. AdjustTorque(wheelHit.forwardSlip);
    327. break;
    328. case CarDriveType.FrontWheelDrive:
    329. m_WheelColliders[0].GetGroundHit(out wheelHit);
    330. AdjustTorque(wheelHit.forwardSlip);
    331. m_WheelColliders[1].GetGroundHit(out wheelHit);
    332. AdjustTorque(wheelHit.forwardSlip);
    333. break;
    334. }
    335. }
    336. //当向前滑动距离超过阈值后,就说明轮胎过度滑转,则减少牵引力,以降低转速。上面函数调用
    337. private void AdjustTorque(float forwardSlip)
    338. {
    339. if (forwardSlip >= m_SlipLimit && m_CurrentTorque >= 0)
    340. {
    341. m_CurrentTorque -= 10 * m_TractionControl;
    342. }
    343. else
    344. {
    345. m_CurrentTorque += 10 * m_TractionControl;
    346. if (m_CurrentTorque > m_FullTorqueOverAllWheels)
    347. {
    348. m_CurrentTorque = m_FullTorqueOverAllWheels;
    349. }
    350. }
    351. }
    352. //函数AnySkidSoundPlaying()是遍历四个轮子,检查是否播放音效,是的返回ture.上面检查函数调用
    353. private bool AnySkidSoundPlaying()
    354. {
    355. for (int i = 0; i < 4; i++)
    356. {
    357. if (m_WheelEffects[i].PlayingAudio)
    358. {
    359. return true;
    360. }
    361. }
    362. return false;
    363. }
    364. }
    365. }