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

170 lines
6.8 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;
[RequireComponent(typeof(Rigidbody))]
public class AdvancedCarController : MonoBehaviour
{
public float SpeedKmH => rb.linearVelocity.magnitude * 3.6f;
[Header("Данные авто")]
[SerializeField] private CarPhysicsData data;
[Header("Колеса (WheelColliders)")]
[SerializeField] private WheelCollider[] frontWheels;
[SerializeField] private WheelCollider[] rearWheels;
[Header("Состояние деталей (Для диплома)")]
[Range(0.1f, 1f)] public float engineCondition = 1f; // 1 - новое, 0.1 - убитое (не тянет)
[Range(0.1f, 1f)] public float tiresCondition = 1f; // Влияет на держак резины
// Состояние авто
public float CurrentSpeedKmH => rb.linearVelocity.magnitude * 3.6f;
public float EngineRPM { get; private set; }
public int CurrentGear { get; private set; }
private Rigidbody rb;
private float currentSteerAngle;
private WheelFrictionCurve originalRearSidewaysFriction;
// Ввод
private float moveInput, steerInput;
private bool isBraking, isHandbrake;
private void Awake()
{
rb = GetComponent<Rigidbody>();
rb.centerOfMass = data.centerOfMassOffset; // Понижаем центр масс для стабильности
// Запоминаем оригинальное трение для ручника
if (rearWheels.Length > 0)
originalRearSidewaysFriction = rearWheels[0].sidewaysFriction;
}
private void Update()
{
// Чтение инпута (можно заменить на новый Input System)
moveInput = Input.GetAxis("Vertical");
steerInput = Input.GetAxis("Horizontal");
isBraking = Input.GetKey(KeyCode.Space) || (moveInput < 0 && CurrentSpeedKmH > 5f);
isHandbrake = Input.GetKey(KeyCode.LeftShift) || Input.GetKey(KeyCode.Space);
}
private void FixedUpdate()
{
ApplySteering();
ProcessEngineAndGears();
ApplyAntiRollBar();
ProcessHandbrake();
}
public void ProcessInput(float vertical, float horizontal, bool handbrake)
{
moveInput = vertical;
steerInput = horizontal;
isHandbrake = handbrake;
}
private void ApplySteering()
{
// 1. Угол зависит от скорости (на 100 км/ч руль не вывернется на 35 градусов)
float speedMultiplier = data.steerAngleBySpeed.Evaluate(CurrentSpeedKmH);
float targetSteer = steerInput * data.maxSteerAngle * speedMultiplier;
// 2. Сглаживание поворота (руль не дергается мгновенно)
currentSteerAngle = Mathf.Lerp(currentSteerAngle, targetSteer, Time.fixedDeltaTime * data.steerSmoothing);
foreach (var wheel in frontWheels)
{
wheel.steerAngle = currentSteerAngle;
}
}
private void ProcessEngineAndGears()
{
// Считаем средние обороты ведущих колес (допустим, задний привод)
float wheelRPM = 0;
foreach (var w in rearWheels) wheelRPM += Mathf.Abs(w.rpm);
wheelRPM /= rearWheels.Length;
// Вычисляем обороты двигателя через передаточные числа
float gearRatio = data.gearRatios[CurrentGear];
EngineRPM = Mathf.Clamp(wheelRPM * gearRatio * data.finalDrive, data.minRPM, data.maxRPM);
// Автоматическая коробка передач
if (EngineRPM > data.shiftUpRPM && CurrentGear < data.gearRatios.Length - 1)
CurrentGear++;
else if (EngineRPM < data.shiftDownRPM && CurrentGear > 0)
CurrentGear--;
// Получаем крутящий момент по графику и умножаем на состояние двигателя!
float motorTorque = data.torqueCurve.Evaluate(EngineRPM) * gearRatio * data.finalDrive;
float finalTorque = moveInput * motorTorque * engineCondition;
// Применяем газ или тормоз
foreach (var wheel in rearWheels)
{
if (isBraking)
{
wheel.motorTorque = 0;
wheel.brakeTorque = data.brakeForce;
}
else
{
wheel.motorTorque = finalTorque / rearWheels.Length;
wheel.brakeTorque = 0;
}
}
}
private void ProcessHandbrake()
{
foreach (var wheel in rearWheels)
{
if (isHandbrake)
{
wheel.brakeTorque = data.brakeForce;
// ДРИФТ: Уменьшаем боковое сцепление, умножая на износ шин
WheelFrictionCurve fc = wheel.sidewaysFriction;
fc.stiffness = originalRearSidewaysFriction.stiffness * data.handbrakeFrictionMultiplier * tiresCondition;
wheel.sidewaysFriction = fc;
}
else if (!isBraking)
{
wheel.brakeTorque = 0;
// Возвращаем обычное сцепление с поправкой на убитость шин
WheelFrictionCurve fc = wheel.sidewaysFriction;
fc.stiffness = originalRearSidewaysFriction.stiffness * tiresCondition;
wheel.sidewaysFriction = fc;
}
}
}
private void ApplyAntiRollBar()
{
// Стабилизатор поперечной устойчивости для передней и задней оси
ApplyAxleAntiRoll(frontWheels[0], frontWheels[1]);
ApplyAxleAntiRoll(rearWheels[0], rearWheels[1]);
}
private void ApplyAxleAntiRoll(WheelCollider left, WheelCollider right)
{
float travelL = 1.0f;
float travelR = 1.0f;
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 antiRollVal = (travelL - travelR) * data.antiRollForce;
// Применяем противодействующую силу (прижимаем поднявшееся колесо)
if (groundedL) rb.AddForceAtPosition(left.transform.up * -antiRollVal, left.transform.position);
if (groundedR) rb.AddForceAtPosition(right.transform.up * antiRollVal, right.transform.position);
}
}