2025-09-26 18:08:07 +09:00
using System ;
using UnityEngine ;
internal enum CarDriveType
{
FrontWheelDrive ,
RearWheelDrive ,
FourWheelDrive
}
internal enum CarSteerType
{
FrontSteerWheels ,
RearSteerWheels ,
FourSteerWheels
}
internal enum SpeedType
{
MPH ,
KPH
}
public class NewCarController : MonoBehaviour
{
[SerializeField] private int colWheels ; //the number of wheels that can rotate, can be used for the trailer
// [SerializeField] private bool FrontSteerWheels = true; //the rotation of the front or rear wheels, made for a forklift truck
[SerializeField] private CarSteerType m_CarSteerType = CarSteerType . FrontSteerWheels ; //the rotation of the front or rear wheels or FourSteerWheels, made for a forklift truck, a TelehandlerForklift
[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
2025-10-30 18:36:26 +09:00
[Range(0, 1)] [ SerializeField ] private float m_TractionControl ; // 0 is no traction treeList, 1 is full interference
2025-09-26 18:08:07 +09:00
[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 static 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 . linearVelocity . 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 [ colWheels ] ;
for ( int i = 0 ; i < colWheels ; 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 < colWheels ; 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 ) ;
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 ;
switch ( m_CarSteerType )
{
case CarSteerType . FrontSteerWheels :
m_WheelColliders [ 0 ] . steerAngle = m_SteerAngle ;
m_WheelColliders [ 1 ] . steerAngle = m_SteerAngle ;
break ;
case CarSteerType . RearSteerWheels :
m_WheelColliders [ 2 ] . steerAngle = m_SteerAngle ;
m_WheelColliders [ 3 ] . steerAngle = m_SteerAngle ;
break ;
case CarSteerType . FourSteerWheels :
m_WheelColliders [ 0 ] . steerAngle = m_SteerAngle ;
m_WheelColliders [ 1 ] . steerAngle = m_SteerAngle ;
m_WheelColliders [ 2 ] . steerAngle = m_SteerAngle ;
m_WheelColliders [ 3 ] . steerAngle = m_SteerAngle ;
break ;
}
/ * if ( FrontSteerWheels )
{
m_WheelColliders [ 0 ] . steerAngle = m_SteerAngle ;
m_WheelColliders [ 1 ] . steerAngle = m_SteerAngle ;
}
else
{
m_WheelColliders [ 2 ] . steerAngle = m_SteerAngle ;
m_WheelColliders [ 3 ] . 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 . linearVelocity . magnitude ;
switch ( m_SpeedType )
{
case SpeedType . MPH :
speed * = 2.23693629f ;
if ( speed > m_Topspeed )
m_Rigidbody . linearVelocity = ( m_Topspeed / 2.23693629f ) * m_Rigidbody . linearVelocity . normalized ;
break ;
case SpeedType . KPH :
speed * = 3.6f ;
if ( speed > m_Topspeed )
m_Rigidbody . linearVelocity = ( m_Topspeed / 3.6f ) * m_Rigidbody . linearVelocity . 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 < colWheels ; 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 < colWheels ; i + + )
{
if ( CurrentSpeed > 5 & & Vector3 . Angle ( transform . forward , m_Rigidbody . linearVelocity ) < 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 . linearVelocity = velRotation * m_Rigidbody . linearVelocity ;
}
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 . linearVelocity . magnitude ) ;
}
2025-10-30 18:36:26 +09:00
// crude traction treeList that reduces the power to wheel if the car is wheel spinning too much
2025-09-26 18:08:07 +09:00
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 ;
}
}
}
}