← Cheatsheets
Tags: unity, vehicle, character-controller, wheel-collider, friction, drag, torque, raycast, game-dev
Last updated: 2026-06-27
Vehicle / Character Controllers Cheatsheet
Quick Reference
| Approach | Best For | Complexity |
| CharacterController | Player movement (FPS, TPS) | Low |
| Rigidbody + forces | Physics-driven characters | Medium |
| WheelCollider | Wheeled vehicles (cars, trucks) | Medium |
| Raycast-based | Custom vehicles, hovercraft, motorbikes | High |
CharacterController Component
Key Properties
| Property | Description |
| Height | Capsule height (centre to top/bottom) |
| Radius | Capsule radius |
| Slope Limit | Max walkable slope angle (default 45°) |
| Step Offset | Max step height to climb |
| Skin Width | Overlap tolerance (0.01–0.1, smaller = more likely to get stuck) |
| Min Move Distance | Threshold before movement registers (default 0.001) |
| Center | Offset 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
cc.isGrounded returns false on slopes steeper than Slope Limit.
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
| Setting | Typical Value | Effect |
| Mass | 1–10 | Heavier = harder to push by other physics objects |
| Drag | 0–2 | Higher = tighter stopping (instant direction changes) |
| Angular Drag | 0–10 | Higher = less spin on collision |
| Max Velocity | ~20 | Clamp with rb.velocity = Vector3.ClampMagnitude(...) |
WheelCollider (Vehicle Physics)
Setup Checklist
- Add Rigidbody to car root (mass 1000–1500 kg).
- Add WheelCollider to each wheel GameObject.
- Assign WheelCollider to a script for motor/steering/brake control.
- Add a visual wheel mesh (separate Transform, synced to WheelCollider in Update).
WheelCollider Properties
| Property | Description |
| Mass | Wheel mass (kg, 10–30 typical) |
| Radius | Wheel radius (metres) |
| Wheel Damping Rate | Suspension damping |
| Suspension Distance | Max suspension travel |
| Suspension Spring | Spring force curve (target position + stiffness) |
| Forward Friction | Grip in forward/reverse direction |
| Sideways Friction | Grip 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
| Issue | Fix |
| Car flips on turns | Lower centre of mass: rb.centerOfMass = new Vector3(0, -0.5f, 0); |
| Wheels spin out | Reduce motorTorque, increase forward friction stiffness |
| Car slides sideways | Increase sideways friction extremumValue |
| Bouncy suspension | Reduce spring stiffness, increase damping |
| Car doesn’t turn | Increase 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
| Concept | Description | Typical Value |
| Static Friction | Force needed to start sliding | 0.6–1.0 |
| Dynamic Friction | Force needed to keep sliding | 0.3–0.6 |
| Rolling Resistance | Tyre deformation loss | 0.01–0.05 |
| Air Drag | Air resistance on body | 0.2–0.5 |
| Angular Drag | Spin damping | 0.5–5.0 |
Choosing an Approach
| Goal | Use |
| FPS/TPS player | CharacterController (kinematic, precise) |
| Physics-based player (pushing crates) | Rigidbody + forces |
| Arcade kart racer | WheelCollider (quick setup) |
| Sim racing | WheelCollider with tuned friction curves |
| Hovercraft / spaceship | Raycast or custom force-based |
| Motorbike (2 wheels) | Custom raycast suspension |
| Anti-grav racer | Raycast suspension + torque steering |