Tags: unity, vehicle, character-controller, wheel-collider, friction, drag, torque, raycast, game-dev Last updated: 2026-06-27

Vehicle / Character Controllers Cheatsheet

Quick Reference

ApproachBest ForComplexity
CharacterControllerPlayer movement (FPS, TPS)Low
Rigidbody + forcesPhysics-driven charactersMedium
WheelColliderWheeled vehicles (cars, trucks)Medium
Raycast-basedCustom vehicles, hovercraft, motorbikesHigh

CharacterController Component

Key Properties

PropertyDescription
HeightCapsule height (centre to top/bottom)
RadiusCapsule radius
Slope LimitMax walkable slope angle (default 45°)
Step OffsetMax step height to climb
Skin WidthOverlap tolerance (0.01–0.1, smaller = more likely to get stuck)
Min Move DistanceThreshold before movement registers (default 0.001)
CenterOffset from transform position

Basic Movement

CharacterController cc = GetComponent<CharacterController>();

Vector3 move = (transform.right * Input.GetAxis("Horizontal")
              + transform.forward * Input.GetAxis("Vertical"));
move *= speed;

if (!cc.isGrounded) move.y += Physics.gravity.y * Time.deltaTime;

cc.Move(move * Time.deltaTime);

Jumping

float verticalVelocity;
bool isGrounded;

void Update() {
    isGrounded = cc.isGrounded;
    if (isGrounded && verticalVelocity < 0)
        verticalVelocity = -2f;   # Stick to ground

    if (isGrounded && Input.GetButtonDown("Jump"))
        verticalVelocity = Mathf.Sqrt(jumpHeight * -2f * Physics.gravity.y);

    verticalVelocity += Physics.gravity.y * Time.deltaTime;
    Vector3 move = inputMove * speed;
    move.y = verticalVelocity;
    cc.Move(move * Time.deltaTime);
}

Slope Handling

RaycastHit hit;
Physics.Raycast(transform.position, Vector3.down, out hit, 2f);
float slopeAngle = Vector3.Angle(hit.normal, Vector3.up);
bool tooSteep = slopeAngle > cc.slopeLimit;

Rigidbody Character Controller

Force-Based Movement

Rigidbody rb = GetComponent<Rigidbody>();
Vector3 targetVel = inputDir * speed;
Vector3 delta = targetVel - new Vector3(rb.velocity.x, 0, rb.velocity.z);
rb.AddForce(delta, ForceMode.VelocityChange);

# Jump
if (isGrounded && Input.GetButtonDown("Jump"))
    rb.AddForce(Vector3.up * jumpForce, ForceMode.Impulse);

Ground Detection (SphereCast)

bool CheckGrounded() {
    return Physics.SphereCast(
        transform.position + Vector3.up * 0.1f,
        radius, Vector3.down, out RaycastHit hit,
        0.15f, groundMask);
}

Movement Settings Tuning

SettingTypical ValueEffect
Mass1–10Heavier = harder to push by other physics objects
Drag0–2Higher = tighter stopping (instant direction changes)
Angular Drag0–10Higher = less spin on collision
Max Velocity~20Clamp with rb.velocity = Vector3.ClampMagnitude(...)

WheelCollider (Vehicle Physics)

Setup Checklist

  1. Add Rigidbody to car root (mass 1000–1500 kg).
  2. Add WheelCollider to each wheel GameObject.
  3. Assign WheelCollider to a script for motor/steering/brake control.
  4. Add a visual wheel mesh (separate Transform, synced to WheelCollider in Update).

WheelCollider Properties

PropertyDescription
MassWheel mass (kg, 10–30 typical)
RadiusWheel radius (metres)
Wheel Damping RateSuspension damping
Suspension DistanceMax suspension travel
Suspension SpringSpring force curve (target position + stiffness)
Forward FrictionGrip in forward/reverse direction
Sideways FrictionGrip in lateral direction

Friction Curve

WheelFrictionCurve friction = wheel.forwardFriction;
friction.extremumSlip = 0.4f;     # Slip where grip peaks
friction.extremumValue = 2f;      # Maximum grip multiplier
friction.asymptoteSlip = 0.8f;    # Slip where grip plateaus
friction.asymptoteValue = 1.5f;   # Grip at plateau
friction.stiffness = 1f;          # Overall grip multiplier
wheel.forwardFriction = friction;

Motor, Steering, Brakes

# Motor torque (Nm)
wheel.motorTorque = Input.GetAxis("Vertical") * motorForce;

# Steering angle (degrees)
wheel.steerAngle = Input.GetAxis("Horizontal") * maxSteerAngle;

# Brake torque (Nm)
wheel.brakeTorque = Input.GetKey(KeyCode.Space) ? brakeForce : 0f;

Wheel Visual Sync

void Update() {
    wheelCollider.GetWorldPose(out Vector3 pos, out Quaternion rot);
    wheelVisual.position = pos;
    wheelVisual.rotation = rot;
}

Vehicle Tuning Tips

IssueFix
Car flips on turnsLower centre of mass: rb.centerOfMass = new Vector3(0, -0.5f, 0);
Wheels spin outReduce motorTorque, increase forward friction stiffness
Car slides sidewaysIncrease sideways friction extremumValue
Bouncy suspensionReduce spring stiffness, increase damping
Car doesn’t turnIncrease steerAngle, check forward friction

Raycast-Based Vehicle Controller

Alternative to WheelCollider — more control, less PhysX magic.

Hover / Anti-gravity Pattern

void FixedUpdate() {
    RaycastHit hit;
    float hoverHeight = 2f;
    float hoverForce = 100f;

    if (Physics.Raycast(transform.position, Vector3.down,
        out hit, hoverHeight)) {
        float compression = 1f - (hit.distance / hoverHeight);
        Vector3 force = Vector3.up * hoverForce * compression;
        rb.AddForceAtPosition(force, transform.position);
        rb.AddForceAtPosition(-rb.velocity * damping, transform.position);
    }

    # Apply torque for rotation/steering
    rb.AddTorque(transform.up * steerInput * torque);
}

Raycast Suspension (per-corner)

# Place ray origin above each wheel position
Vector3 rayOrigin = wheelPositions[i] + Vector3.up * maxSuspension;
if (Physics.Raycast(rayOrigin, Vector3.down, out hit, maxSuspension * 2f)) {
    float compRatio = 1f - (hit.distance / (maxSuspension * 2f));
    rb.AddForceAtPosition(Vector3.up * springStrength * compRatio,
                          wheelPositions[i]);
}

Friction & Drag Reference

ConceptDescriptionTypical Value
Static FrictionForce needed to start sliding0.6–1.0
Dynamic FrictionForce needed to keep sliding0.3–0.6
Rolling ResistanceTyre deformation loss0.01–0.05
Air DragAir resistance on body0.2–0.5
Angular DragSpin damping0.5–5.0

Choosing an Approach

GoalUse
FPS/TPS playerCharacterController (kinematic, precise)
Physics-based player (pushing crates)Rigidbody + forces
Arcade kart racerWheelCollider (quick setup)
Sim racingWheelCollider with tuned friction curves
Hovercraft / spaceshipRaycast or custom force-based
Motorbike (2 wheels)Custom raycast suspension
Anti-grav racerRaycast suspension + torque steering