2026-07-15 15:16:00 +03:00

188 lines
7.6 KiB
C#

using UnityEngine;
#if ENABLE_INPUT_SYSTEM
using UnityEngine.InputSystem;
#endif
namespace RaceFields.Vehicles
{
[RequireComponent(typeof(Rigidbody))]
public sealed class ArcadeBuggyController : MonoBehaviour
{
[Header("Wheel Colliders")]
[SerializeField] private WheelCollider frontLeftCollider;
[SerializeField] private WheelCollider frontRightCollider;
[SerializeField] private WheelCollider rearLeftCollider;
[SerializeField] private WheelCollider rearRightCollider;
[Header("Wheel Visuals")]
[SerializeField] private Transform frontLeftVisual;
[SerializeField] private Transform frontRightVisual;
[SerializeField] private Transform rearLeftVisual;
[SerializeField] private Transform rearRightVisual;
[Header("Driving")]
[SerializeField] private float motorTorque = 950f;
[SerializeField] private float brakeTorque = 1700f;
[SerializeField] private float maxSteerAngle = 32f;
[SerializeField] private float reverseTorqueScale = 0.45f;
[SerializeField] private float downforce = 35f;
[Header("Stability")]
[SerializeField] private Vector3 centerOfMassOffset = new Vector3(0f, -0.45f, 0.15f);
[SerializeField] private float maxSpeedKmh = 120f;
private Rigidbody body;
private float throttle;
private float steering;
private bool brake;
private Quaternion frontLeftVisualRotationOffset = Quaternion.identity;
private Quaternion frontRightVisualRotationOffset = Quaternion.identity;
private Quaternion rearLeftVisualRotationOffset = Quaternion.identity;
private Quaternion rearRightVisualRotationOffset = Quaternion.identity;
private void Awake()
{
body = GetComponent<Rigidbody>();
body.centerOfMass += centerOfMassOffset;
AutoFindReferences();
CacheVisualRotationOffsets();
}
private void Update()
{
ReadInput();
SyncWheelVisual(frontLeftCollider, frontLeftVisual, frontLeftVisualRotationOffset);
SyncWheelVisual(frontRightCollider, frontRightVisual, frontRightVisualRotationOffset);
SyncWheelVisual(rearLeftCollider, rearLeftVisual, rearLeftVisualRotationOffset);
SyncWheelVisual(rearRightCollider, rearRightVisual, rearRightVisualRotationOffset);
}
private void FixedUpdate()
{
ApplySteering();
ApplyMotorAndBrakes();
body.AddForce(-transform.up * downforce * body.linearVelocity.magnitude, ForceMode.Force);
}
private void ReadInput()
{
#if ENABLE_INPUT_SYSTEM
if (Keyboard.current != null)
{
float forward = Keyboard.current.wKey.isPressed || Keyboard.current.upArrowKey.isPressed ? 1f : 0f;
float reverse = Keyboard.current.sKey.isPressed || Keyboard.current.downArrowKey.isPressed ? 1f : 0f;
float left = Keyboard.current.aKey.isPressed || Keyboard.current.leftArrowKey.isPressed ? 1f : 0f;
float right = Keyboard.current.dKey.isPressed || Keyboard.current.rightArrowKey.isPressed ? 1f : 0f;
throttle = forward - reverse;
steering = right - left;
brake = Keyboard.current.spaceKey.isPressed;
return;
}
#endif
throttle = Input.GetAxisRaw("Vertical");
steering = Input.GetAxisRaw("Horizontal");
brake = Input.GetKey(KeyCode.Space);
}
private void ApplySteering()
{
float angle = steering * maxSteerAngle;
if (frontLeftCollider != null) frontLeftCollider.steerAngle = angle;
if (frontRightCollider != null) frontRightCollider.steerAngle = angle;
}
private void ApplyMotorAndBrakes()
{
float speedKmh = body.linearVelocity.magnitude * 3.6f;
float torque = speedKmh < maxSpeedKmh ? throttle * motorTorque : 0f;
if (throttle < 0f) torque *= reverseTorqueScale;
SetDriveTorque(rearLeftCollider, torque);
SetDriveTorque(rearRightCollider, torque);
float currentBrakeTorque = brake ? brakeTorque : 0f;
SetBrakeTorque(frontLeftCollider, currentBrakeTorque);
SetBrakeTorque(frontRightCollider, currentBrakeTorque);
SetBrakeTorque(rearLeftCollider, currentBrakeTorque);
SetBrakeTorque(rearRightCollider, currentBrakeTorque);
}
private static void SetDriveTorque(WheelCollider wheel, float torque)
{
if (wheel != null) wheel.motorTorque = torque;
}
private static void SetBrakeTorque(WheelCollider wheel, float torque)
{
if (wheel != null) wheel.brakeTorque = torque;
}
private static void SyncWheelVisual(WheelCollider collider, Transform visual, Quaternion visualRotationOffset)
{
if (collider == null || visual == null) return;
collider.GetWorldPose(out Vector3 position, out Quaternion rotation);
visual.SetPositionAndRotation(position, rotation * visualRotationOffset);
}
private void AutoFindReferences()
{
frontLeftCollider ??= FindChildComponent<WheelCollider>("WheelCollider_FL");
frontRightCollider ??= FindChildComponent<WheelCollider>("WheelCollider_FR");
rearLeftCollider ??= FindChildComponent<WheelCollider>("WheelCollider_RL");
rearRightCollider ??= FindChildComponent<WheelCollider>("WheelCollider_RR");
frontLeftVisual ??= FindFirstChildTransform("F_L", "WheelVisual_FL");
frontRightVisual ??= FindFirstChildTransform("F_R", "WheelVisual_FR");
rearLeftVisual ??= FindFirstChildTransform("B_L", "WheelVisual_RL");
rearRightVisual ??= FindFirstChildTransform("B_R", "WheelVisual_RR");
}
private void CacheVisualRotationOffsets()
{
frontLeftVisualRotationOffset = GetVisualRotationOffset(frontLeftCollider, frontLeftVisual);
frontRightVisualRotationOffset = GetVisualRotationOffset(frontRightCollider, frontRightVisual);
rearLeftVisualRotationOffset = GetVisualRotationOffset(rearLeftCollider, rearLeftVisual);
rearRightVisualRotationOffset = GetVisualRotationOffset(rearRightCollider, rearRightVisual);
}
private static Quaternion GetVisualRotationOffset(WheelCollider collider, Transform visual)
{
if (collider == null || visual == null)
return Quaternion.identity;
return Quaternion.Inverse(collider.transform.rotation) * visual.rotation;
}
private T FindChildComponent<T>(string childName) where T : Component
{
Transform child = FindChildTransform(childName);
return child != null ? child.GetComponent<T>() : null;
}
private Transform FindChildTransform(string childName)
{
Transform[] children = GetComponentsInChildren<Transform>(true);
for (int i = 0; i < children.Length; i++)
{
if (children[i].name == childName)
return children[i];
}
return null;
}
private Transform FindFirstChildTransform(params string[] childNames)
{
for (int i = 0; i < childNames.Length; i++)
{
Transform child = FindChildTransform(childNames[i]);
if (child != null)
return child;
}
return null;
}
}
}