File size: 4,727 Bytes
6cae93f | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 | using UnityEngine;
namespace UnityAgent
{
/// <summary>
/// Arcade car physics: motor torque, steering, braking and a simple
/// four-wheel raycast suspension. Place this on a root GameObject with
/// a Rigidbody and four wheel transforms.
/// </summary>
[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)
{
// Rotate the visual wheel and apply a small yaw torque.
w.transform.localRotation = Quaternion.Euler(0f, _steer * maxSteerAngle, 0f);
}
}
// Steering yaw torque.
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);
}
}
}
|