Chapter 5: The Unity3D Physics Engine — Rigidbody, Colliders and Joints

Game Development Math for Unity3D June 2018 45 min read

Introduction to Unity Physics

Unity ships with two completely separate physics engines, both based on the PhysX library from NVIDIA:

  • 3D Physics: uses the Physics class, Rigidbody component, and Collider components (BoxCollider, SphereCollider, etc.).
  • 2D Physics: uses the Physics2D class, Rigidbody2D component, and Collider2D components (BoxCollider2D, CircleCollider2D, etc.).

The two systems are completely independent and do not interact. The physics simulation runs in FixedUpdate (by default at 50 Hz). This is important: all physics interactions applying forces, moving Rigidbodies, reading velocity should happen in FixedUpdate, not Update.

The Rigidbody Component

A Rigidbody is the component that hands an object over to the physics engine. Once a Rigidbody is attached, the object will be affected by gravity, forces, collisions, and torques.

Key Rigidbody Fields

  • Mass: the simulated weight in kilograms.
  • Drag: linear air resistance. Higher values slow linear movement faster.
  • Angular Drag: rotational resistance. Higher values stop spinning faster.
  • Use Gravity: toggles whether gravity is applied.
  • Is Kinematic: when true, the Rigidbody ignores physics forces but still participates in collision detection.
  • Collision Detection Mode: controls how fast-moving objects are handled.
  • Constraints: freeze specific position or rotation axes.

Applying Forces in Code

using UnityEngine;

[RequireComponent(typeof(Rigidbody))]
public class PhysicsExample : MonoBehaviour
{
    private Rigidbody _rb;
    public float jumpForce = 8f;
    public float thrustForce = 10f;

    void Awake()
    {
        _rb = GetComponent<Rigidbody>();
    }

    void FixedUpdate()
    {
        // Apply continuous force in world space (like a rocket engine)
        if (Input.GetKey(KeyCode.W))
        {
            _rb.AddForce(transform.forward * thrustForce);
        }

        // Apply an impulse (instantaneous push good for jumps, explosions)
        if (Input.GetKeyDown(KeyCode.Space))
        {
            _rb.AddForce(Vector3.up * jumpForce, ForceMode.Impulse);
        }

        // Apply torque (angular force makes the object spin)
        _rb.AddTorque(Vector3.up * 2f);
    }
}

ForceMode Options

  • ForceMode.Force (default): continuous force, affected by mass and fixed timestep
  • ForceMode.Acceleration: continuous force, NOT affected by mass
  • ForceMode.Impulse: instantaneous impulse, affected by mass use for explosions and jumps
  • ForceMode.VelocityChange: instantaneous velocity change, NOT affected by mass

Kinematic Rigidbodies

Setting rb.isKinematic = true puts the object in kinematic mode. A kinematic Rigidbody is NOT affected by gravity or forces, CAN be moved by script, and STILL participates in collision detection.

using UnityEngine;

// Example: a moving platform controlled by script, not physics
[RequireComponent(typeof(Rigidbody))]
public class KinematicPlatform : MonoBehaviour
{
    private Rigidbody _rb;
    public Vector3 pointA;
    public Vector3 pointB;
    public float speed = 2f;

    private float _t = 0f;
    private int _direction = 1;

    void Awake()
    {
        _rb = GetComponent<Rigidbody>();
        _rb.isKinematic = true;
    }

    void FixedUpdate()
    {
        _t += Time.fixedDeltaTime * speed * _direction;

        if (_t >= 1f) { _t = 1f; _direction = -1; }
        if (_t <= 0f) { _t = 0f; _direction =  1; }

        // Use MovePosition instead of transform.position for kinematic bodies
        _rb.MovePosition(Vector3.Lerp(pointA, pointB, _t));
    }
}

Colliders

A Collider defines the shape used for collision detection. An object can have a Rigidbody without a Collider (it will be affected by gravity but pass through everything), or a Collider without a Rigidbody (it becomes a static obstacle).

Primitive Colliders

  • BoxCollider: axis-aligned box. Ideal for crates, walls, floors.
  • SphereCollider: perfect sphere. Cheapest possible collider ideal for balls and trigger zones.
  • CapsuleCollider: cylinder with hemispherical ends. Standard for humanoid characters.

Collider as Trigger Zone

Any collider can be turned into a trigger zone by enabling its Is Trigger checkbox. Trigger colliders do not physically push objects; instead, they fire events when overlapping:

void OnTriggerEnter(Collider other)
{
    if (other.CompareTag("Player"))
    {
        Debug.Log("Player entered trigger zone");
    }
}

void OnTriggerStay(Collider other)
{
    // Called every FixedUpdate while the collider overlaps
}

void OnTriggerExit(Collider other)
{
    // Called when the collider leaves
}

Collision Callbacks (Physical Collisions)

void OnCollisionEnter(Collision collision)
{
    Debug.Log("Hit: " + collision.gameObject.name);
    Debug.Log("Impact speed: " + collision.relativeVelocity.magnitude);

    // Access contact points
    foreach (ContactPoint contact in collision.contacts)
    {
        Debug.DrawRay(contact.point, contact.normal, Color.red, 2f);
    }
}

void OnCollisionStay(Collision collision)  { }
void OnCollisionExit(Collision collision)  { }

Compound Colliders

Complex objects cannot be approximated well by a single primitive collider. Rather than using a slow MeshCollider, you build a compound collider: multiple primitive colliders attached to child GameObjects, all sharing the same root Rigidbody.

How to Build a Compound Collider

  1. Attach a single Rigidbody to the root GameObject.
  2. Add child empty GameObjects, positioned and scaled to cover different parts of the mesh.
  3. Attach a primitive Collider (Box, Sphere, or Capsule) to each child.
  4. The physics engine automatically combines all child colliders into one compound shape driven by the root Rigidbody.

Collision Detection Modes

  • Discrete (default): checks at each fixed timestep only. Can tunnel at high speeds.
  • Continuous: swept volume test against static colliders. Use for fast projectiles.
  • Continuous Dynamic: like Continuous but also tests against other Rigidbodies. Most accurate and most expensive.
  • Continuous Speculative: works well with Kinematic Rigidbodies and is cheaper than Continuous Dynamic.

The CharacterController

The CharacterController is a special Unity component designed for player character movement. Unlike a Rigidbody, it is not a fully physics-simulated body you control it entirely through code, but it still handles collision detection, slopes, and steps automatically.

using UnityEngine;

[RequireComponent(typeof(CharacterController))]
public class FPSController : MonoBehaviour
{
    public float moveSpeed = 5f;
    public float jumpHeight = 1.5f;
    public float gravity = -9.81f;

    private CharacterController _cc;
    private Vector3 _velocity;

    void Awake()
    {
        _cc = GetComponent<CharacterController>();
    }

    void Update()
    {
        bool isGrounded = _cc.isGrounded;

        if (isGrounded && _velocity.y < 0f)
            _velocity.y = -2f;

        float h = Input.GetAxis("Horizontal");
        float v = Input.GetAxis("Vertical");
        Vector3 moveDir = transform.right * h + transform.forward * v;

        _cc.Move(moveDir * moveSpeed * Time.deltaTime);

        if (Input.GetButtonDown("Jump") && isGrounded)
        {
            // v = sqrt(2 * g * h)
            _velocity.y = Mathf.Sqrt(jumpHeight * -2f * gravity);
        }

        _velocity.y += gravity * Time.deltaTime;
        _cc.Move(_velocity * Time.deltaTime);
    }
}

Joints

Joints connect two Rigidbodies (or a Rigidbody to a fixed world point) and constrain their relative movement.

HingeJoint

A HingeJoint allows rotation around a single axis like a door hinge or an axle.

HingeJoint hinge = GetComponent<HingeJoint>();

JointMotor motor = hinge.motor;
motor.force = 100f;
motor.targetVelocity = 200f; // degrees per second
motor.freeSpin = false;
hinge.motor = motor;
hinge.useMotor = true;

FixedJoint

A FixedJoint welds two Rigidbodies together they move and rotate as one unit but are separate physics objects.

public void AttachObject(Rigidbody objectToAttach)
{
    FixedJoint joint = gameObject.AddComponent<FixedJoint>();
    joint.connectedBody = objectToAttach;
    joint.breakForce = 500f;
    joint.breakTorque = 500f;
}

void OnJointBreak(float breakForce)
{
    Debug.Log("Joint broke with force: " + breakForce);
}

Summary

  • Rigidbody: makes objects physics-simulated. Use AddForce and FixedUpdate.
  • Kinematic Rigidbody: script-controlled movement that still participates in collision. Use MovePosition().
  • Primitive Colliders: Box, Sphere, Capsule fast and sufficient for most needs.
  • Compound Colliders: multiple primitives on children sharing one root Rigidbody.
  • Static Colliders: Colliders without Rigidbody never move at runtime.
  • Collision Modes: use Continuous Detection for fast-moving objects to prevent tunneling.
  • CharacterController: the right tool for player movement in most games.
  • Joints: connect bodies with physical constraints.
Unity3D Physics Rigidbody Colliders Joints C#

Need help with Unity3D development?

I'm a senior developer with 16+ years experience, including AAA projects at Ubisoft. Let's discuss how I can help with your game or interactive project.

Start a Conversation