You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
327 lines
11 KiB
327 lines
11 KiB
using System; |
|
using System.Collections; |
|
using System.Collections.Generic; |
|
using UnityEngine; |
|
|
|
internal enum CarDriveType |
|
{ |
|
FrontWheelDrive, |
|
RearWheelDrive, |
|
FourWheelDrive |
|
} |
|
|
|
internal enum SpeedType |
|
{ |
|
MPH, |
|
KPH |
|
} |
|
|
|
public class CarController : MonoBehaviour |
|
{ |
|
[SerializeField] private CarDriveType m_CarDriveType = CarDriveType.FourWheelDrive; |
|
[SerializeField] private WheelCollider[] m_WheelColliders = new WheelCollider[4]; |
|
[SerializeField] private GameObject[] m_WheelMeshes = new GameObject[4]; |
|
[SerializeField] private Vector3 m_CentreOfMassOffset; |
|
[SerializeField] private float m_MaximumSteerAngle; |
|
[Range(0, 1)] [SerializeField] private float m_SteerHelper; // 0 is raw physics , 1 the car will grip in the direction it is facing |
|
[Range(0, 1)] [SerializeField] private float m_TractionControl; // 0 is no traction control, 1 is full interference |
|
[SerializeField] private float m_FullTorqueOverAllWheels; |
|
[SerializeField] private float m_ReverseTorque; |
|
[SerializeField] private float m_MaxHandbrakeTorque; |
|
[SerializeField] private float m_Downforce = 100f; |
|
[SerializeField] private SpeedType m_SpeedType; |
|
[SerializeField] private float m_Topspeed = 200; |
|
[SerializeField] private int NoOfGears = 5; |
|
[SerializeField] private float m_RevRangeBoundary = 1f; |
|
[SerializeField] private float m_SlipLimit; |
|
[SerializeField] private float m_BrakeTorque; |
|
|
|
private Quaternion[] m_WheelMeshLocalRotations; |
|
private Vector3 m_Prevpos, m_Pos; |
|
private float m_SteerAngle; |
|
private int m_GearNum; |
|
private float m_GearFactor; |
|
private float m_OldRotation; |
|
private float m_CurrentTorque; |
|
private Rigidbody m_Rigidbody; |
|
private const float k_ReversingThreshold = 0.01f; |
|
|
|
public bool Skidding { get; private set; } |
|
public float BrakeInput { get; private set; } |
|
public float CurrentSteerAngle { get { return m_SteerAngle; } } |
|
public float CurrentSpeed { get { return m_Rigidbody.velocity.magnitude * 2.23693629f; } } |
|
public float MaxSpeed { get { return m_Topspeed; } } |
|
public float Revs { get; private set; } |
|
public float AccelInput { get; private set; } |
|
|
|
// Use this for initialization |
|
private void Start() |
|
{ |
|
m_WheelMeshLocalRotations = new Quaternion[4]; |
|
for (int i = 0; i < 4; i++) |
|
{ |
|
m_WheelMeshLocalRotations[i] = m_WheelMeshes[i].transform.localRotation; |
|
} |
|
m_WheelColliders[0].attachedRigidbody.centerOfMass = m_CentreOfMassOffset; |
|
|
|
m_MaxHandbrakeTorque = float.MaxValue; |
|
|
|
m_Rigidbody = GetComponent<Rigidbody>(); |
|
m_CurrentTorque = m_FullTorqueOverAllWheels - (m_TractionControl * m_FullTorqueOverAllWheels); |
|
} |
|
|
|
|
|
private void GearChanging() |
|
{ |
|
float f = Mathf.Abs(CurrentSpeed / MaxSpeed); |
|
float upgearlimit = (1 / (float)NoOfGears) * (m_GearNum + 1); |
|
float downgearlimit = (1 / (float)NoOfGears) * m_GearNum; |
|
|
|
if (m_GearNum > 0 && f < downgearlimit) |
|
{ |
|
m_GearNum--; |
|
} |
|
|
|
if (f > upgearlimit && (m_GearNum < (NoOfGears - 1))) |
|
{ |
|
m_GearNum++; |
|
} |
|
} |
|
|
|
|
|
// simple function to add a curved bias towards 1 for a value in the 0-1 range |
|
private static float CurveFactor(float factor) |
|
{ |
|
return 1 - (1 - factor) * (1 - factor); |
|
} |
|
|
|
|
|
// unclamped version of Lerp, to allow value to exceed the from-to range |
|
private static float ULerp(float from, float to, float value) |
|
{ |
|
return (1.0f - value) * from + value * to; |
|
} |
|
|
|
|
|
private void CalculateGearFactor() |
|
{ |
|
float f = (1 / (float)NoOfGears); |
|
// gear factor is a normalised representation of the current speed within the current gear's range of speeds. |
|
// We smooth towards the 'target' gear factor, so that revs don't instantly snap up or down when changing gear. |
|
var targetGearFactor = Mathf.InverseLerp(f * m_GearNum, f * (m_GearNum + 1), Mathf.Abs(CurrentSpeed / MaxSpeed)); |
|
m_GearFactor = Mathf.Lerp(m_GearFactor, targetGearFactor, Time.deltaTime * 5f); |
|
} |
|
|
|
|
|
private void CalculateRevs() |
|
{ |
|
// calculate engine revs (for display / sound) |
|
// (this is done in retrospect - revs are not used in force/power calculations) |
|
CalculateGearFactor(); |
|
var gearNumFactor = m_GearNum / (float)NoOfGears; |
|
var revsRangeMin = ULerp(0f, m_RevRangeBoundary, CurveFactor(gearNumFactor)); |
|
var revsRangeMax = ULerp(m_RevRangeBoundary, 1f, gearNumFactor); |
|
Revs = ULerp(revsRangeMin, revsRangeMax, m_GearFactor); |
|
} |
|
|
|
|
|
public void Move(float steering, float accel, float footbrake, float handbrake) |
|
{ |
|
for (int i = 0; i < 4; i++) |
|
{ |
|
Quaternion quat; |
|
Vector3 position; |
|
m_WheelColliders[i].GetWorldPose(out position, out quat); |
|
m_WheelMeshes[i].transform.position = position; |
|
m_WheelMeshes[i].transform.rotation = quat; |
|
} |
|
|
|
//clamp input values |
|
steering = Mathf.Clamp(steering, -1, 1); |
|
//Debug.Log(steering); |
|
AccelInput = accel = Mathf.Clamp(accel, 0, 1); |
|
BrakeInput = footbrake = -1 * Mathf.Clamp(footbrake, -1, 0); |
|
handbrake = Mathf.Clamp(handbrake, 0, 1); |
|
|
|
//Set the steer on the front wheels. |
|
//Assuming that wheels 0 and 1 are the front wheels. |
|
m_SteerAngle = steering * m_MaximumSteerAngle; |
|
m_WheelColliders[0].steerAngle = m_SteerAngle; |
|
m_WheelColliders[1].steerAngle = m_SteerAngle; |
|
|
|
SteerHelper(); |
|
ApplyDrive(accel, footbrake); |
|
CapSpeed(); |
|
|
|
//Set the handbrake. |
|
//Assuming that wheels 2 and 3 are the rear wheels. |
|
if (handbrake > 0f) |
|
{ |
|
var hbTorque = handbrake * m_MaxHandbrakeTorque; |
|
m_WheelColliders[2].brakeTorque = hbTorque; |
|
m_WheelColliders[3].brakeTorque = hbTorque; |
|
} |
|
else |
|
{ |
|
m_WheelColliders[2].brakeTorque = 0; |
|
m_WheelColliders[3].brakeTorque = 0; |
|
} |
|
|
|
|
|
CalculateRevs(); |
|
GearChanging(); |
|
|
|
AddDownForce(); |
|
//CheckForWheelSpin(); |
|
TractionControl(); |
|
} |
|
|
|
|
|
private void CapSpeed() |
|
{ |
|
float speed = m_Rigidbody.velocity.magnitude; |
|
switch (m_SpeedType) |
|
{ |
|
case SpeedType.MPH: |
|
|
|
speed *= 2.23693629f; |
|
if (speed > m_Topspeed) |
|
m_Rigidbody.velocity = (m_Topspeed / 2.23693629f) * m_Rigidbody.velocity.normalized; |
|
break; |
|
|
|
case SpeedType.KPH: |
|
speed *= 3.6f; |
|
if (speed > m_Topspeed) |
|
m_Rigidbody.velocity = (m_Topspeed / 3.6f) * m_Rigidbody.velocity.normalized; |
|
break; |
|
} |
|
} |
|
|
|
|
|
private void ApplyDrive(float accel, float footbrake) |
|
{ |
|
|
|
float thrustTorque; |
|
switch (m_CarDriveType) |
|
{ |
|
case CarDriveType.FourWheelDrive: |
|
thrustTorque = accel * (m_CurrentTorque / 4f); |
|
for (int i = 0; i < 4; i++) |
|
{ |
|
m_WheelColliders[i].motorTorque = thrustTorque; |
|
} |
|
break; |
|
|
|
case CarDriveType.FrontWheelDrive: |
|
thrustTorque = accel * (m_CurrentTorque / 2f); |
|
m_WheelColliders[0].motorTorque = m_WheelColliders[1].motorTorque = thrustTorque; |
|
break; |
|
|
|
case CarDriveType.RearWheelDrive: |
|
thrustTorque = accel * (m_CurrentTorque / 2f); |
|
m_WheelColliders[2].motorTorque = m_WheelColliders[3].motorTorque = thrustTorque; |
|
break; |
|
|
|
} |
|
|
|
for (int i = 0; i < 4; i++) |
|
{ |
|
if (CurrentSpeed > 5 && Vector3.Angle(transform.forward, m_Rigidbody.velocity) < 50f) |
|
{ |
|
m_WheelColliders[i].brakeTorque = m_BrakeTorque * footbrake; |
|
} |
|
else if (footbrake > 0) |
|
{ |
|
m_WheelColliders[i].brakeTorque = 0f; |
|
m_WheelColliders[i].motorTorque = -m_ReverseTorque * footbrake; |
|
} |
|
} |
|
} |
|
|
|
|
|
private void SteerHelper() |
|
{ |
|
for (int i = 0; i < 4; i++) |
|
{ |
|
WheelHit wheelhit; |
|
m_WheelColliders[i].GetGroundHit(out wheelhit); |
|
if (wheelhit.normal == Vector3.zero) |
|
return; // wheels arent on the ground so dont realign the rigidbody velocity |
|
} |
|
|
|
// this if is needed to avoid gimbal lock problems that will make the car suddenly shift direction |
|
if (Mathf.Abs(m_OldRotation - transform.eulerAngles.y) < 10f) |
|
{ |
|
var turnadjust = (transform.eulerAngles.y - m_OldRotation) * m_SteerHelper; |
|
Quaternion velRotation = Quaternion.AngleAxis(turnadjust, Vector3.up); |
|
m_Rigidbody.velocity = velRotation * m_Rigidbody.velocity; |
|
} |
|
m_OldRotation = transform.eulerAngles.y; |
|
} |
|
|
|
|
|
// this is used to add more grip in relation to speed |
|
private void AddDownForce() |
|
{ |
|
m_WheelColliders[0].attachedRigidbody.AddForce(-transform.up * m_Downforce * |
|
m_WheelColliders[0].attachedRigidbody.velocity.magnitude); |
|
} |
|
|
|
|
|
// checks if the wheels are spinning and is so does three things |
|
// 1) emits particles |
|
// 2) plays tiure skidding sounds |
|
// 3) leaves skidmarks on the ground |
|
// these effects are controlled through the WheelEffects class |
|
// crude traction control that reduces the power to wheel if the car is wheel spinning too much |
|
private void TractionControl() |
|
{ |
|
WheelHit wheelHit; |
|
switch (m_CarDriveType) |
|
{ |
|
case CarDriveType.FourWheelDrive: |
|
// loop through all wheels |
|
for (int i = 0; i < 4; i++) |
|
{ |
|
m_WheelColliders[i].GetGroundHit(out wheelHit); |
|
|
|
AdjustTorque(wheelHit.forwardSlip); |
|
} |
|
break; |
|
|
|
case CarDriveType.RearWheelDrive: |
|
m_WheelColliders[2].GetGroundHit(out wheelHit); |
|
AdjustTorque(wheelHit.forwardSlip); |
|
|
|
m_WheelColliders[3].GetGroundHit(out wheelHit); |
|
AdjustTorque(wheelHit.forwardSlip); |
|
break; |
|
|
|
case CarDriveType.FrontWheelDrive: |
|
m_WheelColliders[0].GetGroundHit(out wheelHit); |
|
AdjustTorque(wheelHit.forwardSlip); |
|
|
|
m_WheelColliders[1].GetGroundHit(out wheelHit); |
|
AdjustTorque(wheelHit.forwardSlip); |
|
break; |
|
} |
|
} |
|
|
|
|
|
private void AdjustTorque(float forwardSlip) |
|
{ |
|
if (forwardSlip >= m_SlipLimit && m_CurrentTorque >= 0) |
|
{ |
|
m_CurrentTorque -= 10 * m_TractionControl; |
|
} |
|
else |
|
{ |
|
m_CurrentTorque += 10 * m_TractionControl; |
|
if (m_CurrentTorque > m_FullTorqueOverAllWheels) |
|
{ |
|
m_CurrentTorque = m_FullTorqueOverAllWheels; |
|
} |
|
} |
|
} |
|
|
|
}
|
|
|