HOW TO MOVE A CAR IN UNITY USING C#

Build a solid, responsive arcade car controller in Unity using Rigidbody physics, velocity steering, and acceleration torque.

Quick Answer

To move a car smoothly with realistic physics in Unity, apply rb.AddRelativeForce() or modify rb.linearVelocity inside FixedUpdate() based on vertical input (W/S), and rotate the car transform around its Y-axis using Quaternion.Euler() or rb.MoveRotation() scaled by horizontal input (A/D) and forward velocity.

Requirements & Setup

Unity Version
2020.3 LTS or Newer
Required Components
Rigidbody + BoxCollider
Physics Execution
FixedUpdate()
Recommended Tool
1

Configure the Car GameObject & Physics Components

Create a 3D car model or a simple 3D Cube (scaled X: 2, Y: 0.8, Z: 4). Attach a Rigidbody component and a BoxCollider to the root object.

  • Mass: Set to 1200 - 1500 kg (realistic passenger vehicle weight).
  • Drag (Linear Damping): Set to 0.5 to provide natural rolling air resistance.
  • Angular Drag: Set to 2.0 to prevent endless spinning when turning.
  • Interpolate: Set to Interpolate (crucial for smooth camera tracking without jitter).
  • Collision Detection: Set to Continuous to prevent clipping through guardrails.
2

Create the Arcade Car Controller C# Script

Create a new C# script named ArcadeCarController.cs and attach it to your car GameObject:

ArcadeCarController.cs (C#)
using UnityEngine;

[RequireComponent(typeof(Rigidbody))]
public class ArcadeCarController : MonoBehaviour
{
    [Header("Engine & Acceleration")]
    [SerializeField] private float accelerationSpeed = 25f;
    [SerializeField] private float maxForwardSpeed = 45f;
    [SerializeField] private float reverseSpeed = 15f;
    [SerializeField] private float brakeForce = 35f;

    [Header("Steering Dynamics")]
    [SerializeField] private float turnSpeed = 90f;
    [SerializeField] private float traction = 0.9f;

    private Rigidbody rb;
    private float moveInput;
    private float steerInput;

    public float CurrentSpeedKmh => rb.linearVelocity.magnitude * 3.6f;

    private void Awake()
    {
        rb = GetComponent<Rigidbody>();
        // Lower Center of Mass to prevent flipping over during sharp corners
        rb.centerOfMass = new Vector3(0f, -0.4f, 0f);
    }

    private void Update()
    {
        // Read Player Input (WASD or Arrow Keys)
        moveInput = Input.GetAxisRaw("Vertical");
        steerInput = Input.GetAxisRaw("Horizontal");
    }

    private void FixedUpdate()
    {
        HandleAcceleration();
        HandleSteering();
        ApplyLateralFriction();
    }

    private void HandleAcceleration()
    {
        if (Mathf.Abs(moveInput) > 0.05f)
        {
            float targetSpeed = moveInput > 0 ? maxForwardSpeed : reverseSpeed;
            Vector3 forwardForce = transform.forward * (moveInput * accelerationSpeed);
            
            if (rb.linearVelocity.magnitude < targetSpeed)
            {
                rb.AddForce(forwardForce, ForceMode.Acceleration);
            }
        }
        else
        {
            // Deceleration / Natural Braking
            rb.linearVelocity = Vector3.Lerp(rb.linearVelocity, Vector3.zero, Time.fixedDeltaTime * 0.8f);
        }
    }

    private void HandleSteering()
    {
        // Steer only when moving to simulate tire grip
        float speedRatio = Mathf.Clamp01(rb.linearVelocity.magnitude / 3f);
        float direction = Vector3.Dot(transform.forward, rb.linearVelocity) >= 0 ? 1f : -1f;

        float turnAmount = steerInput * turnSpeed * speedRatio * direction * Time.fixedDeltaTime;
        Quaternion turnRotation = Quaternion.Euler(0f, turnAmount, 0f);
        rb.MoveRotation(rb.rotation * turnRotation);
    }

    private void ApplyLateralFriction()
    {
        // Cancel sideways drift based on traction ratio
        Vector3 forwardVelocity = transform.forward * Vector3.Dot(rb.linearVelocity, transform.forward);
        Vector3 lateralVelocity = transform.right * Vector3.Dot(rb.linearVelocity, transform.right);
        
        rb.linearVelocity = forwardVelocity + (lateralVelocity * (1f - traction));
    }
}
3

Understanding the Code Architecture

  • Center of Mass (centerOfMass = new Vector3(0, -0.4f, 0)): Lowering the physics pivot ensures the car stays glued to the road rather than flipping over whenever turning at high velocity.
  • Input in Update vs Physics in FixedUpdate: We gather raw input inside Update() for responsiveness and execute rb.AddForce() inside FixedUpdate() to maintain frame-rate independent physics simulation.
  • Lateral Drift Cancellation: We separate the velocity into forward and lateral vectors. Multiplying the lateral component by (1 - traction) eliminates unwanted ice-skating drift while allowing subtle slide.
4

Common Problems & How to Fix Them

Problem 1: The Car Flies or Bounces Aggressively
Check if your wheel colliders or chassis collider are intersecting the ground mesh at spawn. Ensure your road surface has a standard Physics Material with Dynamic Friction = 0.6.
Problem 2: The Car Stutters When the Camera Follows
Set Rigidbody.Interpolation = Interpolate on the Car, and update your Camera position inside LateUpdate(). For more troubleshooting, see our Transform Jitter Fix.

Related Developer Tools & Tutorials

Frequently Asked Questions

Always use Rigidbody forces (or WheelColliders) for cars. Transform.Translate teleports the mesh through walls, ignores mass, breaks collision impulses, and creates micro-jitter on mobile devices.
When the Spacebar (handbrake) is pressed, temporarily decrease the traction variable from 0.9 down to 0.25 and increase angular turning speed slightly to let the rear wheels slide smoothly.