363 lines
13 KiB
C#
363 lines
13 KiB
C#
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
|
|
[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 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);
|
|
}
|
|
|
|
|
|
// 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;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
}
|
|
|