Perekyp/Assets/Scripts/Car/Physics/CarController.cs

240 lines
8.3 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using UnityEngine;
public enum DriveType { FWD, RWD, AWD }
[RequireComponent(typeof(Rigidbody))]
public class CarController : MonoBehaviour
{
public float SpeedKmH => rb != null ? rb.linearVelocity.magnitude * 3.6f : 0f;
[Header("Аркадные настройки движения")]
public DriveType driveType = DriveType.AWD;
[SerializeField] private float motorForce = 2200f;
[SerializeField] private float maxSpeedKmH = 160f;
[SerializeField] private float brakeForce = 4500f;
[Header("Рулевое управление")]
[SerializeField] private float maxSteerAngle = 32f;
[SerializeField] private float steerSpeed = 8f;
[Tooltip("Во сколько раз уменьшается выворот руля на максимальной скорости")]
[SerializeField] private float highSpeedSteerFactor = 0.35f;
[Header("Устойчивость и Дрифт")]
[SerializeField] private Vector3 centerOfMassOffset = new Vector3(0f, -0.45f, 0f);
[SerializeField] private float antiRollForce = 8000f;
[SerializeField] private float normalGrip = 2.2f;
[SerializeField] private float handbrakeRearGrip = 0.65f;
[Header("Wheel Colliders")]
[SerializeField] private WheelCollider frontLeftCollider;
[SerializeField] private WheelCollider frontRightCollider;
[SerializeField] private WheelCollider rearLeftCollider;
[SerializeField] private WheelCollider rearRightCollider;
[Header("Wheel Meshes")]
[SerializeField] private Transform frontLeftMesh;
[SerializeField] private Transform frontRightMesh;
[SerializeField] private Transform rearLeftMesh;
[SerializeField] private Transform rearRightMesh;
private Rigidbody rb;
private float inputVertical;
private float inputHorizontal;
private bool inputHandbrake;
private float currentSteerAngle;
private void Awake()
{
rb = GetComponent<Rigidbody>();
rb.centerOfMass = centerOfMassOffset;
rb.angularDamping = 1.5f;
SetupArcadeWheel(frontLeftCollider);
SetupArcadeWheel(frontRightCollider);
SetupArcadeWheel(rearLeftCollider);
SetupArcadeWheel(rearRightCollider);
}
private void SetupArcadeWheel(WheelCollider wc)
{
if (wc == null) return;
JointSpring spring = wc.suspensionSpring;
spring.spring = 35000f;
spring.damper = 4500f;
spring.targetPosition = 0.5f;
wc.suspensionSpring = spring;
wc.suspensionDistance = 0.18f;
WheelFrictionCurve fwd = wc.forwardFriction;
fwd.stiffness = 2.0f;
wc.forwardFriction = fwd;
WheelFrictionCurve side = wc.sidewaysFriction;
side.stiffness = normalGrip;
wc.sidewaysFriction = side;
}
public void ProcessInput(float vertical, float horizontal, bool handbrake)
{
inputVertical = vertical;
inputHorizontal = horizontal;
inputHandbrake = handbrake;
}
private void FixedUpdate()
{
HandleSteering();
HandleMotorAndBrakes();
ApplyAntiRoll(frontLeftCollider, frontRightCollider);
ApplyAntiRoll(rearLeftCollider, rearRightCollider);
UpdateWheelPoses();
}
private void HandleSteering()
{
float speedFactor = Mathf.Lerp(1f, highSpeedSteerFactor, SpeedKmH / maxSpeedKmH);
float targetSteer = inputHorizontal * maxSteerAngle * speedFactor;
currentSteerAngle = Mathf.Lerp(currentSteerAngle, targetSteer, Time.fixedDeltaTime * steerSpeed);
if (frontLeftCollider != null) frontLeftCollider.steerAngle = currentSteerAngle;
if (frontRightCollider != null) frontRightCollider.steerAngle = currentSteerAngle;
}
private void HandleMotorAndBrakes()
{
float localVelZ = transform.InverseTransformDirection(rb.linearVelocity).z;
float motor = 0f;
float brake = 0f;
if (inputVertical > 0.01f)
{
if (localVelZ < -1.5f)
brake = brakeForce;
else if (SpeedKmH < maxSpeedKmH)
motor = inputVertical * motorForce;
}
else if (inputVertical < -0.01f)
{
if (localVelZ > 1.5f)
brake = brakeForce;
else if (SpeedKmH < 45f)
motor = inputVertical * motorForce * 0.7f;
}
else
{
// Плавное торможение двигателем, когда газ отпущен
brake = 150f;
}
if (inputHandbrake)
{
motor = 0f;
brake = brakeForce * 1.5f;
}
ApplyDriveTorque(motor);
ApplyBrakesAndDrift(brake, inputHandbrake);
}
private void ApplyDriveTorque(float torque)
{
if (driveType == DriveType.FWD)
{
SetMotor(frontLeftCollider, torque);
SetMotor(frontRightCollider, torque);
SetMotor(rearLeftCollider, 0f);
SetMotor(rearRightCollider, 0f);
}
else if (driveType == DriveType.RWD)
{
SetMotor(frontLeftCollider, 0f);
SetMotor(frontRightCollider, 0f);
SetMotor(rearLeftCollider, torque);
SetMotor(rearRightCollider, torque);
}
else
{
float half = torque * 0.55f;
SetMotor(frontLeftCollider, half);
SetMotor(frontRightCollider, half);
SetMotor(rearLeftCollider, half);
SetMotor(rearRightCollider, half);
}
}
private void SetMotor(WheelCollider wc, float val)
{
if (wc != null && wc.enabled) wc.motorTorque = val;
}
private void ApplyBrakesAndDrift(float brake, bool handbrake)
{
float targetRearStiffness = handbrake ? handbrakeRearGrip : normalGrip;
SetRearGrip(rearLeftCollider, targetRearStiffness);
SetRearGrip(rearRightCollider, targetRearStiffness);
if (handbrake)
{
if (frontLeftCollider != null) frontLeftCollider.brakeTorque = 0f;
if (frontRightCollider != null) frontRightCollider.brakeTorque = 0f;
if (rearLeftCollider != null) rearLeftCollider.brakeTorque = brake;
if (rearRightCollider != null) rearRightCollider.brakeTorque = brake;
}
else
{
if (frontLeftCollider != null) frontLeftCollider.brakeTorque = brake;
if (frontRightCollider != null) frontRightCollider.brakeTorque = brake;
if (rearLeftCollider != null) rearLeftCollider.brakeTorque = brake * 0.6f;
if (rearRightCollider != null) rearRightCollider.brakeTorque = brake * 0.6f;
}
}
private void SetRearGrip(WheelCollider wc, float targetStiff)
{
if (wc == null) return;
WheelFrictionCurve side = wc.sidewaysFriction;
side.stiffness = Mathf.Lerp(side.stiffness, targetStiff, Time.fixedDeltaTime * 10f);
wc.sidewaysFriction = side;
}
private void ApplyAntiRoll(WheelCollider left, WheelCollider right)
{
if (left == null || right == null || !left.enabled || !right.enabled) return;
float travelL = 1f;
float travelR = 1f;
bool groundedL = left.GetGroundHit(out WheelHit hitL);
if (groundedL)
travelL = (-left.transform.InverseTransformPoint(hitL.point).y - left.radius) / left.suspensionDistance;
bool groundedR = right.GetGroundHit(out WheelHit hitR);
if (groundedR)
travelR = (-right.transform.InverseTransformPoint(hitR.point).y - right.radius) / right.suspensionDistance;
float force = (travelL - travelR) * antiRollForce;
if (groundedL) rb.AddForceAtPosition(left.transform.up * -force, left.transform.position);
if (groundedR) rb.AddForceAtPosition(right.transform.up * force, right.transform.position);
}
private void UpdateWheelPoses()
{
UpdateWheelPose(frontLeftCollider, frontLeftMesh);
UpdateWheelPose(frontRightCollider, frontRightMesh);
UpdateWheelPose(rearLeftCollider, rearLeftMesh);
UpdateWheelPose(rearRightCollider, rearRightMesh);
}
private void UpdateWheelPose(WheelCollider collider, Transform mesh)
{
if (mesh == null || collider == null || !collider.enabled || !mesh.IsChildOf(transform))
return;
collider.GetWorldPose(out Vector3 pos, out Quaternion rot);
mesh.position = pos;
mesh.rotation = rot;
}
}