| using UnityEngine; |
|
|
| namespace UnityAgent |
| { |
| |
| |
| |
| |
| |
| [RequireComponent(typeof(Rigidbody))] |
| public class VehicleController : MonoBehaviour |
| { |
| [System.Serializable] |
| public class Wheel |
| { |
| public Transform transform; |
| public bool isSteering; |
| public bool isPowered; |
| public bool isBraking; |
| [HideInInspector] public bool isGrounded; |
| [HideInInspector] public float suspensionOffset; |
| } |
|
|
| [Header("Wheels")] |
| public Wheel[] wheels = new Wheel[4]; |
|
|
| [Header("Engine")] |
| public float motorTorque = 1500f; |
| public float brakeTorque = 3000f; |
| public float maxSteerAngle = 30f; |
| public float maxSpeedKph = 180f; |
|
|
| [Header("Suspension")] |
| public float suspensionRestLength = 0.5f; |
| public float suspensionSpring = 35000f; |
| public float suspensionDamper = 4500f; |
| public float wheelRadius = 0.35f; |
| public LayerMask wheelMask = ~0; |
|
|
| [Header("Aerodynamics")] |
| public float downforce = 100f; |
| public float centerOfMassOffset = -0.5f; |
|
|
| private Rigidbody _rb; |
| private float _throttle; |
| private float _steer; |
| private float _brake; |
|
|
| private void Awake() |
| { |
| _rb = GetComponent<Rigidbody>(); |
| _rb.centerOfMass += Vector3.up * centerOfMassOffset; |
| _rb.interpolation = RigidbodyInterpolation.Interpolate; |
| } |
|
|
| private void Update() |
| { |
| _throttle = Input.GetAxis("Vertical"); |
| _steer = Input.GetAxis("Horizontal"); |
| _brake = Input.GetKey(KeyCode.Space) ? 1f : 0f; |
| } |
|
|
| private void FixedUpdate() |
| { |
| UpdateWheels(); |
| ApplySuspension(); |
| ApplyDrive(); |
| ApplyDownforce(); |
| } |
|
|
| private void UpdateWheels() |
| { |
| foreach (var w in wheels) |
| { |
| if (w.transform == null) continue; |
| Vector3 origin = w.transform.position; |
| bool hit = Physics.Raycast( |
| origin, -transform.up, out RaycastHit r, |
| suspensionRestLength + wheelRadius, wheelMask, |
| QueryTriggerInteraction.Ignore); |
| w.isGrounded = hit; |
| w.suspensionOffset = hit ? r.distance - wheelRadius : suspensionRestLength; |
| } |
| } |
|
|
| private void ApplySuspension() |
| { |
| foreach (var w in wheels) |
| { |
| if (!w.isGrounded) continue; |
| float force = (suspensionRestLength - w.suspensionOffset) * suspensionSpring; |
| Vector3 velocity = _rb.GetPointVelocity(w.transform.position); |
| float upwardVel = Vector3.Dot(transform.up, velocity); |
| force -= upwardVel * suspensionDamper; |
| _rb.AddForceAtPosition(transform.up * Mathf.Max(0f, force), w.transform.position); |
| } |
| } |
|
|
| private void ApplyDrive() |
| { |
| float kph = _rb.velocity.magnitude * 3.6f; |
| float speedFactor = Mathf.Clamp01(1f - kph / maxSpeedKph); |
|
|
| foreach (var w in wheels) |
| { |
| if (!w.isGrounded) continue; |
| if (w.isPowered) |
| { |
| Vector3 force = transform.forward * (_throttle * motorTorque * speedFactor); |
| _rb.AddForceAtPosition(force, w.transform.position, ForceMode.Force); |
| } |
| if (w.isBraking && _brake > 0.01f) |
| { |
| Vector3 brakeForce = -transform.forward * (_brake * brakeTorque); |
| _rb.AddForceAtPosition(brakeForce, w.transform.position, ForceMode.Force); |
| } |
| if (w.isSteering) |
| { |
| |
| w.transform.localRotation = Quaternion.Euler(0f, _steer * maxSteerAngle, 0f); |
| } |
| } |
| |
| if (Mathf.Abs(_steer) > 0.01f) |
| { |
| float turn = _steer * maxSteerAngle * 0.02f; |
| _rb.AddTorque(transform.up * turn * (_rb.velocity.magnitude + 1f), ForceMode.Force); |
| } |
| } |
|
|
| private void ApplyDownforce() |
| { |
| _rb.AddForce(-transform.up * downforce * _rb.velocity.magnitude); |
| } |
| } |
| } |
|
|