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(); } 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; } }