188 lines
7.6 KiB
C#
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;
|
|
}
|
|
}
|
|
}
|