Unity Physics
Unity ships two physics engines: 3D (NVIDIA PhysX) and 2D (Box2D). This document covers 3D physics, which is what the open-world city example uses.
Rigidbody
A Rigidbody puts a GameObject under physics control. There are two ways to
move a rigidbody:
Force-based (preferred for realistic motion):
_rb.AddForce(Vector3.up * jumpForce, ForceMode.VelocityChange); _rb.AddForceAtPosition(force, wheel.position, ForceMode.Force);Direct velocity set (preferred for arcade controllers):
Vector3 v = _rb.velocity; v.x = desired.x; v.z = desired.z; _rb.velocity = v;
| ForceMode | Mass dependent? | Use case |
|---|---|---|
Force |
Yes | Continuous pushes (default) |
Acceleration |
No | Continuous push ignoring mass |
Impulse |
Yes | Instantaneous burst (explosion) |
VelocityChange |
No | Instantaneous burst (jump) |
- Set
rb.freezeRotation = truewhen you want to control rotation manually (most third-person controllers). - Use
rb.interpolation = RigidbodyInterpolation.Interpolateto smooth out stutter when the physics timestep (50 Hz) is slower than the render rate (60+ Hz). - Set
rb.collisionDetectionMode = CollisionDetectionMode.Continuousfor fast-moving objects to prevent tunnelling.
Colliders
BoxCollider,SphereCollider,CapsuleCollider,MeshColliderare the built-in shapes.MeshCollidershould be convex unless the mesh is static (markConvexin the inspector) -- rigidbodies require convex colliders.- Set
isTrigger = trueto receiveOnTriggerEnter/Stay/Exitwithout physically blocking movement. Use triggers for pickups, zones, audio volumes.
private void OnTriggerEnter(Collider other)
{
if (other.CompareTag("Player"))
Debug.Log("Player entered the zone");
}
Raycasting
// Single hit
if (Physics.Raycast(origin, direction, out RaycastHit hit, maxDistance, layerMask))
{
Debug.Log(hit.collider.name);
}
// Sphere cast -- great for ground checks on characters
if (Physics.SphereCast(origin, radius, Vector3.down, out RaycastHit hit, distance, mask))
{
_isGrounded = true;
}
// Overlap sphere -- explosions, area damage
Collider[] hits = Physics.OverlapSphere(center, radius);
foreach (var h in hits)
{
var hp = h.GetComponentInParent<HealthSystem>();
if (hp != null) hp.TakeDamage(damage);
}
Use
QueryTriggerInteraction.Ignoreto skip trigger colliders in physics queries unless you explicitly want them.
Layer masks
Layer masks let you filter physics queries and collisions:
public LayerMask groundMask = ~0; // everything by default
if (Physics.Raycast(origin, dir, out _, distance, groundMask)) { ... }
You can also use LayerMask.GetMask("Ground", "Default") to convert names
at runtime, but exposing the mask as a serialised field is cleaner.
Physics step vs frame step
| Step | Method | Frequency | Use it for |
|---|---|---|---|
| Physics | FixedUpdate |
Fixed (50 Hz by default) | rb.AddForce, wheel torque |
| Frame | Update |
Variable (frame rate) | Input.GetAxis, gameplay |
| Post-frame | LateUpdate |
After all Updates | Camera follow |
Never read Input in FixedUpdate -- the input system only updates once
per frame, so FixedUpdate may run zero or multiple times per frame.
Collision matrix gotchas
- Two trigger colliders do not fire
OnTriggerEnterunless at least one of their GameObjects has a Rigidbody. - A Rigidbody marked
IsKinematicwill not respond to forces but will push other rigidbodies. - Adjust the collision matrix in
Project Settings > Physicsto skip collision between layers (e.g. Player vs. PlayerBullet).