Physics — Rigidbody, Collider and Collisions
How to move objects with physics and react to collisions.
Unity uses NVIDIA’s PhysX for 3D physics (for 2D — Box2D). This means: rigid body simulation, collisions, forces, joints. All of it works out of the box; your job is to configure the components correctly.
Three classes of participants
- Static — a GameObject with a Collider but no Rigidbody. It does not move, ideal for walls, floors, static geometry.
- Kinematic — a Rigidbody with
isKinematic = true. Moves only through your script (viaMovePosition/MoveRotation), does not react to forces. - Dynamic — an ordinary Rigidbody. Reacts to gravity, forces, collisions.
If an object has no Rigidbody, Unity caches it in the so-called “static collider tree”. Moving such
a collider every frame forces PhysX to rebuild the tree — expensive. Need to move something without
physics? Add a Rigidbody with isKinematic = true.
Rigidbody
Key fields:
mass— mass in kg.linearVelocity(formerlyvelocity) — current linear velocity in m/s. The old namevelocityis marked[Obsolete]in Unity 6.angularVelocity— angular velocity in rad/s.linearDamping/angularDamping— the new names in Unity 6 scripting (formerlydrag/angularDrag). The old namesdrag/angularDragstill work and are not marked [Obsolete]; in the Inspector the properties are still displayed as “Linear Drag” / “Angular Drag”.useGravity— whether to applyPhysics.gravity(default(0, -9.81, 0)).isKinematic— whether it is controlled only by scripts.interpolation—None/Interpolate/Extrapolate. Smooths movement between physics steps. For the player you usually setInterpolate.collisionDetectionMode—Discrete/Continuous/ContinuousDynamic/ContinuousSpeculative. For fast objects (bullets, balls) theContinuousvariants are important to avoid “tunneling” through thin colliders.
In Unity 6 the velocity field is marked [Obsolete] (the compiler will emit a warning) — use
linearVelocity in new code. For drag/angularDrag no Obsolete marker is set yet, but in new
projects linearDamping/angularDamping are idiomatic. angularVelocity was not renamed.
public class Ball : MonoBehaviour
{
private Rigidbody _rb;
private void Awake() {
_rb = GetComponent<Rigidbody>();
}
private void FixedUpdate() {
// Impulse — an instantaneous change in velocity
if (Input.GetKey(KeyCode.Space)) {
_rb.AddForce(Vector3.up * 10f, ForceMode.Impulse);
}
}
}
Force modes
AddForce takes a ForceMode:
| Mode | Meaning | Formula |
|---|---|---|
Force | A force applied continuously | Δv = (F/m) * dt |
Acceleration | Acceleration ignoring mass | Δv = a * dt |
Impulse | An instantaneous impulse | Δv = F/m |
VelocityChange | An instantaneous change in velocity | Δv = F |
For a jump you usually use Impulse or VelocityChange. For continuous thrust (a rocket engine) — Force.
Collider — the shape for physics
A Collider describes the collision geometry. There are several types:
- BoxCollider, SphereCollider, CapsuleCollider — primitives. Cheap, precise.
- MeshCollider — an arbitrary mesh. Expensive. Must be
convexif the object is dynamic. - TerrainCollider — for terrain.
- WheelCollider — specialized, for vehicles (with suspension and tire friction).
Complex static geometry? A single MeshCollider is enough. A dynamic object with a complex shape? Assemble it from several BoxCollider/SphereCollider — performance is orders of magnitude higher than with a single MeshCollider.
Triggers vs collisions
A Collider has an isTrigger flag. If it is enabled, the object does not block others — but it
generates OnTrigger* events. Without isTrigger it is a physical collision, generating OnCollision*.
// Collecting coins via a trigger
private void OnTriggerEnter(Collider other) {
if (other.CompareTag("Player")) {
ScoreManager.Instance.Add(1);
Destroy(gameObject);
}
}
// Reacting to a collision
private void OnCollisionEnter(Collision collision) {
var contact = collision.GetContact(0);
Debug.Log($"Hit at {contact.point}, force: {collision.impulse.magnitude:F2}");
AudioSource.PlayClipAtPoint(_hitSound, contact.point);
}
Event invocation rules
Not intuitive, but critical to understand:
| Object A | Object B | OnCollision | OnTrigger |
|---|---|---|---|
| Static collider | Static collider | — | — |
| Static collider | Dynamic Rigidbody | Yes (on both) | Yes, if isTrigger |
| Dynamic Rigidbody | Dynamic Rigidbody | Yes (on both) | Yes, if isTrigger |
| Kinematic Rigidbody | Static collider | — | Yes, if isTrigger |
| Kinematic Rigidbody | Dynamic Rigidbody | Yes | Yes, if isTrigger |
| Kinematic Rigidbody | Kinematic Rigidbody | — | Yes, if isTrigger |
Remember the main point: for OnCollisionEnter at least one of the objects must be a non-kinematic
Rigidbody. For OnTriggerEnter a single Rigidbody of any type is enough.
Layers and the Layer Collision Matrix
Every GameObject has a Layer (0..31). In Project Settings → Physics → Layer Collision Matrix you enable/disable interactions between layers. Example use:
- The
Playerlayer does not collide withTrigger. - The
Bulletlayer collides withEnemy, but not with otherBullets.
This is more efficient than filtering in OnCollisionEnter — PhysX will not even fire the event if
the layers do not interact.
Raycast and physics
We already saw this in the input chapter. The full API:
// A simple raycast
if (Physics.Raycast(origin, direction, out RaycastHit hit, maxDistance, layerMask)) { }
// Multiple hits
RaycastHit[] hits = Physics.RaycastAll(ray, maxDistance, layerMask);
// A "thick" ray — SphereCast / CapsuleCast / BoxCast
Physics.SphereCast(origin, radius, direction, out hit, maxDistance);
// Not a ray, but an area check
Collider[] inSphere = Physics.OverlapSphere(center, radius, layerMask);
layerMask is a bitmask of the layers the ray passes through. Convenient with
LayerMask.GetMask("Enemy", "Wall").
CharacterController vs Rigidbody for the player
There are two approaches to controlling a character:
- CharacterController — a special component with a capsule shape. Does not use a Rigidbody, does
not react to forces. You have full control via
Move(). Ideal for first-person shooters and platformers — where you need predictability rather than simulation. - Rigidbody + CapsuleCollider — a physical character. Realistic collisions, but it requires careful tuning of damping, max-slope handling, anti-slide.
// CharacterController
public class FpsMove : MonoBehaviour
{
private CharacterController _cc;
private Vector3 _velocity;
[SerializeField] private float speed = 5f;
[SerializeField] private float gravity = -20f;
private void Awake() => _cc = GetComponent<CharacterController>();
private void Update() {
float h = Input.GetAxis("Horizontal");
float v = Input.GetAxis("Vertical");
Vector3 move = transform.right * h + transform.forward * v;
_cc.Move(move * speed * Time.deltaTime);
if (_cc.isGrounded && _velocity.y < 0) _velocity.y = -2f;
_velocity.y += gravity * Time.deltaTime;
_cc.Move(_velocity * Time.deltaTime);
}
}
We will examine this scheme in more detail in the practical chapter.
In the next chapter — the camera and Cinemachine.