CarController.cs 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368
  1. using System;
  2. using UnityEngine;
  3. #pragma warning disable 649
  4. namespace UnityStandardAssets.Vehicles.Car
  5. {
  6. internal enum CarDriveType
  7. {
  8. FrontWheelDrive,
  9. RearWheelDrive,
  10. FourWheelDrive
  11. }
  12. internal enum SpeedType
  13. {
  14. MPH,
  15. KPH
  16. }
  17. public class CarController : MonoBehaviour
  18. {
  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
  26. [Range(0, 1)] [SerializeField] private float m_TractionControl; // 0 is no traction control, 1 is full interference
  27. [SerializeField] private float m_FullTorqueOverAllWheels;
  28. [SerializeField] private float m_ReverseTorque;
  29. [SerializeField] private float m_MaxHandbrakeTorque;
  30. [SerializeField] private float m_Downforce = 100f;
  31. [SerializeField] private SpeedType m_SpeedType;
  32. [SerializeField] private float m_Topspeed = 200;
  33. [SerializeField] private static int NoOfGears = 5;
  34. [SerializeField] private float m_RevRangeBoundary = 1f;
  35. [SerializeField] private float m_SlipLimit;
  36. [SerializeField] private float m_BrakeTorque;
  37. private Quaternion[] m_WheelMeshLocalRotations;
  38. private Vector3 m_Prevpos, m_Pos;
  39. private float m_SteerAngle;
  40. private int m_GearNum;
  41. private float m_GearFactor;
  42. private float m_OldRotation;
  43. private float m_CurrentTorque;
  44. private Rigidbody m_Rigidbody;
  45. private const float k_ReversingThreshold = 0.01f;
  46. public bool Skidding { get; private set; }
  47. public float BrakeInput { get; private set; }
  48. public float CurrentSteerAngle{ get { return m_SteerAngle; }}
  49. public float CurrentSpeed{ get { return m_Rigidbody.velocity.magnitude*2.23693629f; }}
  50. public float MaxSpeed{get { return m_Topspeed; }}
  51. public float Revs { get; private set; }
  52. public float AccelInput { get; private set; }
  53. // Use this for initialization
  54. private void Start()
  55. {
  56. m_WheelMeshLocalRotations = new Quaternion[4];
  57. for (int i = 0; i < 4; i++)
  58. {
  59. m_WheelMeshLocalRotations[i] = m_WheelMeshes[i].transform.localRotation;
  60. }
  61. m_WheelColliders[0].attachedRigidbody.centerOfMass = m_CentreOfMassOffset;
  62. m_MaxHandbrakeTorque = float.MaxValue;
  63. m_Rigidbody = GetComponent<Rigidbody>();
  64. m_CurrentTorque = m_FullTorqueOverAllWheels - (m_TractionControl*m_FullTorqueOverAllWheels);
  65. }
  66. private void GearChanging()
  67. {
  68. float f = Mathf.Abs(CurrentSpeed/MaxSpeed);
  69. float upgearlimit = (1/(float) NoOfGears)*(m_GearNum + 1);
  70. float downgearlimit = (1/(float) NoOfGears)*m_GearNum;
  71. if (m_GearNum > 0 && f < downgearlimit)
  72. {
  73. m_GearNum--;
  74. }
  75. if (f > upgearlimit && (m_GearNum < (NoOfGears - 1)))
  76. {
  77. m_GearNum++;
  78. }
  79. }
  80. // simple function to add a curved bias towards 1 for a value in the 0-1 range
  81. private static float CurveFactor(float factor)
  82. {
  83. return 1 - (1 - factor)*(1 - factor);
  84. }
  85. // unclamped version of Lerp, to allow value to exceed the from-to range
  86. private static float ULerp(float from, float to, float value)
  87. {
  88. return (1.0f - value)*from + value*to;
  89. }
  90. private void CalculateGearFactor()
  91. {
  92. float f = (1/(float) NoOfGears);
  93. // gear factor is a normalised representation of the current speed within the current gear's range of speeds.
  94. // We smooth towards the 'target' gear factor, so that revs don't instantly snap up or down when changing gear.
  95. var targetGearFactor = Mathf.InverseLerp(f*m_GearNum, f*(m_GearNum + 1), Mathf.Abs(CurrentSpeed/MaxSpeed));
  96. m_GearFactor = Mathf.Lerp(m_GearFactor, targetGearFactor, Time.deltaTime*5f);
  97. }
  98. private void CalculateRevs()
  99. {
  100. // calculate engine revs (for display / sound)
  101. // (this is done in retrospect - revs are not used in force/power calculations)
  102. CalculateGearFactor();
  103. var gearNumFactor = m_GearNum/(float) NoOfGears;
  104. var revsRangeMin = ULerp(0f, m_RevRangeBoundary, CurveFactor(gearNumFactor));
  105. var revsRangeMax = ULerp(m_RevRangeBoundary, 1f, gearNumFactor);
  106. Revs = ULerp(revsRangeMin, revsRangeMax, m_GearFactor);
  107. }
  108. public void Move(float steering, float accel, float footbrake, float handbrake)
  109. {
  110. for (int i = 0; i < 4; i++)
  111. {
  112. Quaternion quat;
  113. Vector3 position;
  114. m_WheelColliders[i].GetWorldPose(out position, out quat);
  115. m_WheelMeshes[i].transform.position = position;
  116. m_WheelMeshes[i].transform.rotation = quat;
  117. }
  118. //clamp input values
  119. steering = Mathf.Clamp(steering, -1, 1);
  120. AccelInput = accel = Mathf.Clamp(accel, 0, 1);
  121. BrakeInput = footbrake = -1*Mathf.Clamp(footbrake, -1, 0);
  122. handbrake = Mathf.Clamp(handbrake, 0, 1);
  123. //Set the steer on the front wheels.
  124. //Assuming that wheels 0 and 1 are the front wheels.
  125. m_SteerAngle = steering*m_MaximumSteerAngle;
  126. m_WheelColliders[0].steerAngle = m_SteerAngle;
  127. m_WheelColliders[1].steerAngle = m_SteerAngle;
  128. SteerHelper();
  129. ApplyDrive(accel, footbrake);
  130. CapSpeed();
  131. //Set the handbrake.
  132. //Assuming that wheels 2 and 3 are the rear wheels.
  133. if (handbrake > 0f)
  134. {
  135. var hbTorque = handbrake*m_MaxHandbrakeTorque;
  136. m_WheelColliders[2].brakeTorque = hbTorque;
  137. m_WheelColliders[3].brakeTorque = hbTorque;
  138. }
  139. CalculateRevs();
  140. GearChanging();
  141. AddDownForce();
  142. CheckForWheelSpin();
  143. TractionControl();
  144. }
  145. private void CapSpeed()
  146. {
  147. float speed = m_Rigidbody.velocity.magnitude;
  148. switch (m_SpeedType)
  149. {
  150. case SpeedType.MPH:
  151. speed *= 2.23693629f;
  152. if (speed > m_Topspeed)
  153. m_Rigidbody.velocity = (m_Topspeed/2.23693629f) * m_Rigidbody.velocity.normalized;
  154. break;
  155. case SpeedType.KPH:
  156. speed *= 3.6f;
  157. if (speed > m_Topspeed)
  158. m_Rigidbody.velocity = (m_Topspeed/3.6f) * m_Rigidbody.velocity.normalized;
  159. break;
  160. }
  161. }
  162. private void ApplyDrive(float accel, float footbrake)
  163. {
  164. float thrustTorque;
  165. switch (m_CarDriveType)
  166. {
  167. case CarDriveType.FourWheelDrive:
  168. thrustTorque = accel * (m_CurrentTorque / 4f);
  169. for (int i = 0; i < 4; i++)
  170. {
  171. m_WheelColliders[i].motorTorque = thrustTorque;
  172. }
  173. break;
  174. case CarDriveType.FrontWheelDrive:
  175. thrustTorque = accel * (m_CurrentTorque / 2f);
  176. m_WheelColliders[0].motorTorque = m_WheelColliders[1].motorTorque = thrustTorque;
  177. break;
  178. case CarDriveType.RearWheelDrive:
  179. thrustTorque = accel * (m_CurrentTorque / 2f);
  180. m_WheelColliders[2].motorTorque = m_WheelColliders[3].motorTorque = thrustTorque;
  181. break;
  182. }
  183. for (int i = 0; i < 4; i++)
  184. {
  185. if (CurrentSpeed > 5 && Vector3.Angle(transform.forward, m_Rigidbody.velocity) < 50f)
  186. {
  187. m_WheelColliders[i].brakeTorque = m_BrakeTorque*footbrake;
  188. }
  189. else if (footbrake > 0)
  190. {
  191. m_WheelColliders[i].brakeTorque = 0f;
  192. m_WheelColliders[i].motorTorque = -m_ReverseTorque*footbrake;
  193. }
  194. }
  195. }
  196. private void SteerHelper()
  197. {
  198. for (int i = 0; i < 4; i++)
  199. {
  200. WheelHit wheelhit;
  201. m_WheelColliders[i].GetGroundHit(out wheelhit);
  202. if (wheelhit.normal == Vector3.zero)
  203. return; // wheels arent on the ground so dont realign the rigidbody velocity
  204. }
  205. // this if is needed to avoid gimbal lock problems that will make the car suddenly shift direction
  206. if (Mathf.Abs(m_OldRotation - transform.eulerAngles.y) < 10f)
  207. {
  208. var turnadjust = (transform.eulerAngles.y - m_OldRotation) * m_SteerHelper;
  209. Quaternion velRotation = Quaternion.AngleAxis(turnadjust, Vector3.up);
  210. m_Rigidbody.velocity = velRotation * m_Rigidbody.velocity;
  211. }
  212. m_OldRotation = transform.eulerAngles.y;
  213. }
  214. // this is used to add more grip in relation to speed
  215. private void AddDownForce()
  216. {
  217. m_WheelColliders[0].attachedRigidbody.AddForce(-transform.up*m_Downforce*
  218. m_WheelColliders[0].attachedRigidbody.velocity.magnitude);
  219. }
  220. // checks if the wheels are spinning and is so does three things
  221. // 1) emits particles
  222. // 2) plays tiure skidding sounds
  223. // 3) leaves skidmarks on the ground
  224. // these effects are controlled through the WheelEffects class
  225. private void CheckForWheelSpin()
  226. {
  227. // loop through all wheels
  228. for (int i = 0; i < 4; i++)
  229. {
  230. WheelHit wheelHit;
  231. m_WheelColliders[i].GetGroundHit(out wheelHit);
  232. // is the tire slipping above the given threshhold
  233. if (Mathf.Abs(wheelHit.forwardSlip) >= m_SlipLimit || Mathf.Abs(wheelHit.sidewaysSlip) >= m_SlipLimit)
  234. {
  235. m_WheelEffects[i].EmitTyreSmoke();
  236. // avoiding all four tires screeching at the same time
  237. // if they do it can lead to some strange audio artefacts
  238. if (!AnySkidSoundPlaying())
  239. {
  240. m_WheelEffects[i].PlayAudio();
  241. }
  242. continue;
  243. }
  244. // if it wasnt slipping stop all the audio
  245. if (m_WheelEffects[i].PlayingAudio)
  246. {
  247. m_WheelEffects[i].StopAudio();
  248. }
  249. // end the trail generation
  250. m_WheelEffects[i].EndSkidTrail();
  251. }
  252. }
  253. // crude traction control that reduces the power to wheel if the car is wheel spinning too much
  254. private void TractionControl()
  255. {
  256. WheelHit wheelHit;
  257. switch (m_CarDriveType)
  258. {
  259. case CarDriveType.FourWheelDrive:
  260. // loop through all wheels
  261. for (int i = 0; i < 4; i++)
  262. {
  263. m_WheelColliders[i].GetGroundHit(out wheelHit);
  264. AdjustTorque(wheelHit.forwardSlip);
  265. }
  266. break;
  267. case CarDriveType.RearWheelDrive:
  268. m_WheelColliders[2].GetGroundHit(out wheelHit);
  269. AdjustTorque(wheelHit.forwardSlip);
  270. m_WheelColliders[3].GetGroundHit(out wheelHit);
  271. AdjustTorque(wheelHit.forwardSlip);
  272. break;
  273. case CarDriveType.FrontWheelDrive:
  274. m_WheelColliders[0].GetGroundHit(out wheelHit);
  275. AdjustTorque(wheelHit.forwardSlip);
  276. m_WheelColliders[1].GetGroundHit(out wheelHit);
  277. AdjustTorque(wheelHit.forwardSlip);
  278. break;
  279. }
  280. }
  281. private void AdjustTorque(float forwardSlip)
  282. {
  283. if (forwardSlip >= m_SlipLimit && m_CurrentTorque >= 0)
  284. {
  285. m_CurrentTorque -= 10 * m_TractionControl;
  286. }
  287. else
  288. {
  289. m_CurrentTorque += 10 * m_TractionControl;
  290. if (m_CurrentTorque > m_FullTorqueOverAllWheels)
  291. {
  292. m_CurrentTorque = m_FullTorqueOverAllWheels;
  293. }
  294. }
  295. }
  296. private bool AnySkidSoundPlaying()
  297. {
  298. for (int i = 0; i < 4; i++)
  299. {
  300. if (m_WheelEffects[i].PlayingAudio)
  301. {
  302. return true;
  303. }
  304. }
  305. return false;
  306. }
  307. }
  308. }