191 lines
7.0 KiB
C#
191 lines
7.0 KiB
C#
using UnityEngine;
|
||
|
||
public enum DriveType { FWD, RWD, AWD }
|
||
|
||
public class CarController : MonoBehaviour
|
||
{
|
||
public float SpeedKmH => rb.linearVelocity.magnitude * 3.6f;
|
||
|
||
[Header("Drive Settings")]
|
||
public DriveType driveType = DriveType.AWD;
|
||
[SerializeField] private float motorForce = 1500f;
|
||
[SerializeField] private float brakeForce = 3000f;
|
||
[SerializeField] private float maxSteerAngle = 30f;
|
||
|
||
[Header("Grip Settings (Friction Stiffness)")]
|
||
[Tooltip("Сцепление для переднего привода (FWD)")]
|
||
[SerializeField] private float fwdFrontGrip = 1.2f;
|
||
[SerializeField] private float fwdRearGrip = 2.0f;
|
||
|
||
[Tooltip("Сцепление для заднего привода (RWD)")]
|
||
[SerializeField] private float rwdFrontGrip = 2.0f;
|
||
[SerializeField] private float rwdRearGrip = 1.2f;
|
||
|
||
[Tooltip("Сцепление для полного привода (AWD)")]
|
||
[SerializeField] private float awdFrontGrip = 2.5f;
|
||
[SerializeField] private float awdRearGrip = 2.5f;
|
||
|
||
[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 void Start()
|
||
{
|
||
rb = GetComponent<Rigidbody>();
|
||
}
|
||
|
||
public void ProcessInput(float vertical, float horizontal, bool handbrake)
|
||
{
|
||
inputVertical = vertical;
|
||
inputHorizontal = horizontal;
|
||
inputHandbrake = handbrake;
|
||
}
|
||
|
||
private void FixedUpdate()
|
||
{
|
||
HandleSteering();
|
||
HandleMotorAndBrakes();
|
||
UpdateWheelPoses();
|
||
}
|
||
|
||
private void HandleSteering()
|
||
{
|
||
float steerAngle = maxSteerAngle * inputHorizontal;
|
||
frontLeftCollider.steerAngle = steerAngle;
|
||
frontRightCollider.steerAngle = steerAngle;
|
||
}
|
||
|
||
private void HandleMotorAndBrakes()
|
||
{
|
||
float currentMotorTorque = 0f;
|
||
float currentBrakeTorque = 0f;
|
||
|
||
// Определяем, куда физически движется машина (вперед или назад)
|
||
float localVelocityZ = transform.InverseTransformDirection(rb.linearVelocity).z;
|
||
|
||
// ЛОГИКА ГАЗА (W)
|
||
if (inputVertical > 0)
|
||
{
|
||
if (localVelocityZ < -1f) // Если катимся назад, а жмем W -> Тормозим
|
||
currentBrakeTorque = brakeForce;
|
||
else // Иначе едем вперед
|
||
currentMotorTorque = inputVertical * motorForce;
|
||
}
|
||
// ЛОГИКА ТОРМОЗА И ЗАДНЕГО ХОДА (S)
|
||
else if (inputVertical < 0)
|
||
{
|
||
if (localVelocityZ > 1f) // Если едем вперед, а жмем S -> Тормозим
|
||
currentBrakeTorque = brakeForce;
|
||
else // Иначе сдаем назад (inputVertical отрицательный, поэтому момент тоже отрицательный)
|
||
currentMotorTorque = inputVertical * motorForce;
|
||
}
|
||
|
||
// ЛОГИКА РУЧНИКА (Пробел)
|
||
if (inputHandbrake)
|
||
{
|
||
currentMotorTorque = 0f; // Глушим газ
|
||
currentBrakeTorque = brakeForce * 2f; // Ручник хватает сильнее обычного тормоза
|
||
}
|
||
|
||
ApplyBrakes(currentBrakeTorque, inputHandbrake);
|
||
ApplyMotor(currentMotorTorque);
|
||
}
|
||
|
||
private void ApplyMotor(float torque)
|
||
{
|
||
// Получаем базовые настройки трения
|
||
WheelFrictionCurve frontFriction = frontLeftCollider.sidewaysFriction;
|
||
WheelFrictionCurve rearFriction = rearLeftCollider.sidewaysFriction;
|
||
|
||
switch (driveType)
|
||
{
|
||
case DriveType.FWD:
|
||
frontLeftCollider.motorTorque = torque;
|
||
frontRightCollider.motorTorque = torque;
|
||
rearLeftCollider.motorTorque = 0;
|
||
rearRightCollider.motorTorque = 0;
|
||
|
||
frontFriction.stiffness = fwdFrontGrip;
|
||
rearFriction.stiffness = fwdRearGrip;
|
||
break;
|
||
|
||
case DriveType.RWD:
|
||
frontLeftCollider.motorTorque = 0;
|
||
frontRightCollider.motorTorque = 0;
|
||
rearLeftCollider.motorTorque = torque;
|
||
rearRightCollider.motorTorque = torque;
|
||
|
||
frontFriction.stiffness = rwdFrontGrip;
|
||
rearFriction.stiffness = rwdRearGrip;
|
||
break;
|
||
|
||
case DriveType.AWD:
|
||
float halfTorque = torque / 2f;
|
||
frontLeftCollider.motorTorque = halfTorque;
|
||
frontRightCollider.motorTorque = halfTorque;
|
||
rearLeftCollider.motorTorque = halfTorque;
|
||
rearRightCollider.motorTorque = halfTorque;
|
||
|
||
frontFriction.stiffness = awdFrontGrip;
|
||
rearFriction.stiffness = awdRearGrip;
|
||
break;
|
||
}
|
||
|
||
// Применяем измененное трение к колесам
|
||
frontLeftCollider.sidewaysFriction = frontFriction;
|
||
frontRightCollider.sidewaysFriction = frontFriction;
|
||
rearLeftCollider.sidewaysFriction = rearFriction;
|
||
rearRightCollider.sidewaysFriction = rearFriction;
|
||
}
|
||
|
||
private void ApplyBrakes(float brake, bool isHandbrake)
|
||
{
|
||
if (isHandbrake)
|
||
{
|
||
// Ручник блокирует только задние колеса (эффект заноса)
|
||
frontLeftCollider.brakeTorque = 0;
|
||
frontRightCollider.brakeTorque = 0;
|
||
rearLeftCollider.brakeTorque = brake;
|
||
rearRightCollider.brakeTorque = brake;
|
||
}
|
||
else
|
||
{
|
||
// Обычный тормоз (S) срабатывает на все колеса, передок сильнее
|
||
frontLeftCollider.brakeTorque = brake;
|
||
frontRightCollider.brakeTorque = brake;
|
||
rearLeftCollider.brakeTorque = brake * 0.5f;
|
||
rearRightCollider.brakeTorque = brake * 0.5f;
|
||
}
|
||
}
|
||
|
||
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) return;
|
||
collider.GetWorldPose(out Vector3 pos, out Quaternion rot);
|
||
mesh.position = pos;
|
||
mesh.rotation = rot;
|
||
}
|
||
} |