Hệ thống
Showroom
frontLeftSteer.TargetAngle = seat.Steer * steerAngle frontRightSteer.TargetAngle = seat.Steer * steerAngle Use code with caution. Copied to clipboard Pro-Level Enhancements A-Chassis:
The script should automatically or manually change gears, affecting speed and acceleration.
Monitors script variables to trigger tire smoke particles, leave skid marks on the pavement, change engine audio pitch based on RPM, and activate brake light meshes.
Monitors wheel slip during braking. If a wheel locks up ( realistic car driving script
using UnityEngine; public class RealisticCarDriving : MonoBehaviour [System.Serializable] public class WheelData public Transform wheelTransform; public bool isDriven; public bool canSteer; [HideInInspector] public float rotationSpeed; [HideInInspector] public float slipRatio; [HideInInspector] public float slipAngle; [Header("Vehicle Architecture")] public Rigidbody rb; public WheelData[] wheels = new WheelData[4]; public Transform centerOfMass; [Header("Engine & Transmission")] public AnimationCurve torqueCurve; public float[] gearRatios = 3.66f, 2.15f, 1.45f, 1.03f, 0.80f ; public float finalDriveRatio = 3.42f; public int currentGear = 1; public float idleRPM = 800f; public float maxRPM = 6500f; [Header("Suspension Physics")] public float springStiffness = 35000f; public float damperCoefficient = 4500f; public float suspensionRestLength = 0.4f; // Inputs private float throttleInput; private float brakeInput; private float steerInput; private float currentRPM; void Start() rb.centerOfMass = centerOfMass.localPosition; void Update() // Capture user inputs fluently throttleInput = Input.GetAxis("Vertical"); steerInput = Input.GetAxis("Horizontal"); brakeInput = Input.GetKey(KeyCode.Space) ? 1.0f : 0.0f; void FixedUpdate() CalculateEngineRPM(); foreach (var wheel in wheels) // 1. Suspension Force Calculation RaycastHit hit; Vector3 tireDown = -wheel.wheelTransform.up; if (Physics.Raycast(wheel.wheelTransform.position, tireDown, out hit, suspensionRestLength)) float compression = suspensionRestLength - hit.distance; Vector3 wheelVelocity = rb.GetPointVelocity(wheel.wheelTransform.position); float suspensionVelocity = Vector3.Dot(wheelVelocity, tireDown); float suspensionForceMag = (compression * springStiffness) - (suspensionVelocity * damperCoefficient); rb.AddForceAtPosition(wheel.wheelTransform.up * suspensionForceMag, wheel.wheelTransform.position); // 2. Wheel Slip Calculations Vector3 tireForward = wheel.wheelTransform.forward; Vector3 tireRight = wheel.wheelTransform.right; float forwardSpeed = Vector3.Dot(wheelVelocity, tireForward); float lateralSpeed = Vector3.Dot(wheelVelocity, tireRight); wheel.slipAngle = Mathf.Atan2(lateralSpeed, Mathf.Abs(forwardSpeed)) * Mathf.Rad2Deg; wheel.slipRatio = (wheel.rotationSpeed * 0.33f - forwardSpeed) / Mathf.Max(Mathf.Abs(forwardSpeed), 0.1f); // 3. Force Generation (Simplified Pacejka application) float longitudinalForce = CalculatePacejkaLongitudinal(wheel.slipRatio, suspensionForceMag) * throttleInput; float lateralForce = CalculatePacejkaLateral(wheel.slipAngle, suspensionForceMag); // Apply braking friction if (brakeInput > 0) longitudinalForce -= Mathf.Sign(forwardSpeed) * brakeInput * 8000f; // 4. Distribute Driving Torque if (wheel.isDriven) float engineTorque = torqueCurve.Evaluate(currentRPM); float totalTorqueMultiplier = gearRatios[currentGear - 1] * finalDriveRatio; float driveForce = (engineTorque * totalTorqueMultiplier) / 0.33f; // 0.33m assumed wheel radius longitudinalForce += driveForce * throttleInput; // Apply calculated vectors back to the rigid body Vector3 finalForceVector = (tireForward * longitudinalForce) + (tireRight * lateralForce); rb.AddForceAtPosition(finalForceVector, wheel.wheelTransform.position); void CalculateEngineRPM() // Average wheel speed of driven wheels drives the RPM feedback loop float avgWheelRPM = 0f; int drivenWheelCount = 0; foreach(var wheel in wheels) if(wheel.isDriven) avgWheelRPM += wheel.rotationSpeed; drivenWheelCount++; avgWheelRPM /= drivenWheelCount; currentRPM = avgWheelRPM * gearRatios[currentGear - 1] * finalDriveRatio; currentRPM = Mathf.Clamp(currentRPM, idleRPM, maxRPM); float CalculatePacejkaLongitudinal(float slip, float load) return load * 1.2f * Mathf.Sin(1.5f * Mathf.Atan(10f * slip)); float CalculatePacejkaLateral(float angle, float load) return load * -1.1f * Mathf.Sin(1.4f * Mathf.Atan(0.12f * angle)); Use code with caution. 6. Optimization and Polishing the Physics Loop
Beyond pure physics, small touches make the feel alive:
: In high-intensity driving scenes, dialogue should be minimal. Realistic scenes often rely on "beats" and reaction shots rather than long exposition. Safety & Logistics in Narrative : frontLeftSteer
Every wheel needs an independent raycast or suspension bone governed by Hooke's Law ( Determines how much the car body compresses under load. Damper Rating (
A common issue with arcade scripts is cars flipping too easily. A realistic script must calculate a low, centralized to ensure stability during high-speed turns. 3. Implementing Physics: The "How-To"
using System.Collections; using System.Collections.Generic; using UnityEngine; [System.Serializable] public class WheelAxle public WheelCollider leftWheel; public WheelCollider rightWheel; public bool isMotorized; // Rear-wheel, front-wheel, or all-wheel drive public bool isSteerable; // Front-wheel steering public class RealisticVehicleController : MonoBehaviour [Header("Input Settings")] private float throttleInput; private float steeringInput; private float brakeInput; [Header("Engine Physics")] public List axleInfos; public float maxMotorTorque = 1500f; public float maxBrakeTorque = 3000f; public float maxSteeringAngle = 35f; [Header("Center of Mass")] public Rigidbody carRigidbody; public Vector3 centerOfMassOffset = new Vector3(0, -0.5f, 0); void Start() // Lower the center of mass to prevent the car from rolling over easily carRigidbody.centerOfMass += centerOfMassOffset; void Update() GetPlayerInput(); AnimateWheelMeshes(); void FixedUpdate() ApplySteering(); ApplyMotorTorque(); ApplyBraking(); private void GetPlayerInput() throttleInput = Input.GetAxis("Vertical"); steeringInput = Input.GetAxis("Horizontal"); brakeInput = Input.GetKey(KeyCode.Space) ? 1f : 0f; private void ApplySteering() float steeringAngle = steeringInput * maxSteeringAngle; foreach (WheelAxle axle in axleInfos) if (axle.isSteerable) axle.leftWheel.steerAngle = steeringAngle; axle.rightWheel.steerAngle = steeringAngle; private void ApplyMotorTorque() float torque = throttleInput * maxMotorTorque; foreach (WheelAxle axle in axleInfos) if (axle.isMotorized) // If braking, remove motor torque axle.leftWheel.motorTorque = brakeInput > 0 ? 0 : torque; axle.rightWheel.motorTorque = brakeInput > 0 ? 0 : torque; private void ApplyBraking() float brakeForce = brakeInput * maxBrakeTorque; foreach (WheelAxle axle in axleInfos) axle.leftWheel.brakeTorque = brakeForce; axle.rightWheel.brakeTorque = brakeForce; private void AnimateWheelMeshes() foreach (WheelAxle axle in axleInfos) UpdateWheelVisuals(axle.leftWheel); UpdateWheelVisuals(axle.rightWheel); private void UpdateWheelVisuals(WheelCollider collider) if (collider.transform.childCount == 0) return; Transform wheelMesh = collider.transform.GetChild(0); Vector3 position; Quaternion rotation; // Get the world space position of the wheel from the physics collider collider.GetWorldPose(out position, out rotation); wheelMesh.position = position; wheelMesh.rotation = rotation; Use code with caution. 4. Advanced Techniques for Maximum Realism Monitors wheel slip during braking
A car is nothing without a driver. Creating a realistic driving script also extends to artificial intelligence (AI) that can navigate roads, obey traffic rules, and react to danger like a human.
– throttle (0–1), steering (−1 to 1), brake (0–1), handbrake (bool).
If the calculated RPM drops below idle, the script should stall the engine unless a clutch mechanism is engaged. 3. Tire Physics: The Crucial Foundation
Now, fire up your editor, place a cube with wheels on a test track, and start coding. The open road (and thousands of eager players) awaits.