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


Gõ tìm kiếm nhanh...