From unity
Unity 6 physics development guide. Use when working with Rigidbody, colliders, raycasting, triggers, collisions, joints, or physics simulation. Covers PhysX 5 (3D) and Box2D (2D) physics, FixedUpdate timing, layer masks, and contact data. Based on Unity 6.3 LTS documentation.
npx claudepluginhub cdata/aria-skills --plugin unityThis skill uses the workspace's default tool permissions.
Unity provides different physics engine integrations for different project needs:
Unity physics query correctness patterns. Catches common mistakes with Raycast, SphereCast, OverlapSphere, NonAlloc allocation, LayerMask construction, trigger interaction, hit ordering, and query type selection. PATTERN format: WHEN/WRONG/RIGHT/GOTCHA. Based on Unity 6.3 LTS.
Guides Godot 4.3+ physics systems: bodies, collision shapes, raycasting, areas, rigid bodies, ragdolls, soft bodies, Jolt physics, and interpolation via GDScript/C# examples.
Provides Unity C# scripting patterns covering MonoBehaviour lifecycle, coroutines, async/await, physics APIs, raycasts, collisions, animations, NavMesh, pooling, singletons, ECS, Jobs, and Burst.
Share bugs, ideas, or general feedback.
Unity provides different physics engine integrations for different project needs:
Physics runs on a fixed timestep via FixedUpdate, separate from the rendering frame rate:
void FixedUpdate()
{
// All physics code belongs here, not in Update()
rb.AddForce(Vector3.forward * speed);
}
Key timing rules:
FixedUpdate runs at a fixed interval (default 0.02s / 50Hz)FixedUpdate calls can occur per frame, or noneTime.fixedDeltaTime controls the intervalUpdate for input, FixedUpdate for physics forcesPhysics.simulationMode controls when the physics engine steps:
Physics.Simulate()A Rigidbody component places a GameObject under physics engine control. A rigid body does not deform or change shape under physics forces.
| Property | Description | Default |
|---|---|---|
mass | Mass in kg; affects force interactions | 1 |
linearDamping | Resistance to linear velocity | 0 |
angularDamping | Resistance to angular velocity | 0.05 |
useGravity | Whether gravity affects this body | true |
isKinematic | If true, not driven by physics forces | false |
interpolation | Smooths visual jitter between physics steps | None |
collisionDetectionMode | Algorithm for detecting collisions | Discrete |
Rigidbody rb = GetComponent<Rigidbody>();
// Apply continuous force (call in FixedUpdate)
rb.AddForce(Vector3.forward * 10f);
rb.AddForce(Vector3.up * 5f, ForceMode.Impulse);
// Apply torque
rb.AddTorque(Vector3.up * 2f);
// Apply force at a world position (creates both force and torque)
rb.AddForceAtPosition(Vector3.forward * 10f, hitPoint);
// Kinematic movement (use for isKinematic=true bodies)
rb.MovePosition(rb.position + direction * speed * Time.fixedDeltaTime);
rb.MoveRotation(targetRotation);
| Mode | Description |
|---|---|
ForceMode.Force | Continuous force, uses mass (default) |
ForceMode.Acceleration | Continuous force, ignores mass |
ForceMode.Impulse | Instant force, uses mass |
ForceMode.VelocityChange | Instant force, ignores mass |
When a Rigidbody's energy falls below the sleep threshold, the physics engine stops calculating it. Control manually with rb.Sleep() and rb.WakeUp().
Unity assumes 1 world unit = 1 metre. Incorrect scale causes unrealistic physics behavior. Keep GameObjects at realistic proportions.
Colliders are invisible shapes that define a GameObject's physical boundaries. They do not need to match the visual mesh.
| Category | Description |
|---|---|
| Static Collider | Collider only, no Rigidbody. For immovable geometry (walls, floors). |
| Dynamic Rigidbody Collider | Collider + Rigidbody (isKinematic=false). Fully simulated. |
| Kinematic Rigidbody Collider | Collider + Rigidbody (isKinematic=true). Moved via script. |
Triggers detect overlapping colliders without physical collision response:
// Set via Inspector: Collider > Is Trigger = true
// Or via script:
GetComponent<BoxCollider>().isTrigger = true;
Requirements for trigger events:
isTrigger enabledColliders support PhysicsMaterial with adjustable friction and bounciness properties for surface interactions.
Raycasting projects invisible rays to detect colliders in the scene.
// Basic: check if anything is ahead
Vector3 fwd = transform.TransformDirection(Vector3.forward);
if (Physics.Raycast(transform.position, fwd, 10))
Debug.Log("Something ahead!");
// With hit info
RaycastHit hit;
if (Physics.Raycast(transform.position, -Vector3.up, out hit, 100.0f))
Debug.Log("Distance to ground: " + hit.distance);
// With layer mask
int layerMask = 1 << LayerMask.NameToLayer("Enemies");
if (Physics.Raycast(origin, direction, out hit, maxDist, layerMask))
Debug.Log("Hit enemy: " + hit.collider.gameObject.name);
// Mouse-to-world raycast
Ray ray = Camera.main.ScreenPointToRay(Mouse.current.position.ReadValue());
if (Physics.Raycast(ray, out hit, 100))
Debug.DrawLine(ray.origin, hit.point);
| Property | Type | Description |
|---|---|---|
point | Vector3 | World-space hit position |
normal | Vector3 | Surface normal at hit point |
distance | float | Distance from ray origin |
collider | Collider | Collider that was hit |
transform | Transform | Transform of the hit object |
| Method | Description |
|---|---|
Physics.RaycastAll() | Returns all intersections along the ray |
Physics.RaycastNonAlloc() | Fills a pre-allocated buffer (no GC alloc) |
Physics.SphereCast() | Sweeps a sphere along a direction |
Physics.BoxCast() | Sweeps a box along a direction |
Physics.CapsuleCast() | Sweeps a capsule along a direction |
Physics.Linecast() | Checks for colliders between two points |
| Method | Description |
|---|---|
Physics.OverlapSphere() | Find all colliders within a sphere |
Physics.OverlapBox() | Find all colliders within a box |
Physics.OverlapCapsule() | Find all colliders within a capsule |
Physics.CheckSphere() | Returns true if any collider overlaps the sphere |
Physics.CheckBox() | Returns true if any collider overlaps the box |
Physics.CheckCapsule() | Returns true if any collider overlaps the capsule |
Controls whether casts/overlaps detect trigger colliders:
QueryTriggerInteraction.UseGlobal -- uses Physics.queriesHitTriggersQueryTriggerInteraction.Collide -- always detect triggersQueryTriggerInteraction.Ignore -- never detect triggers| Mode | Description | Use Case |
|---|---|---|
| Discrete | Checks collisions at end of each physics step. High efficiency. | Default for most objects |
| Continuous | Checks collisions over the entire step. Prevents tunneling. | Fast objects hitting static geometry |
| Continuous Dynamic | Continuous detection against both static and dynamic colliders | Fast objects hitting other fast objects |
| Continuous Speculative | Predictive CCD using speculative contacts | Kinematic bodies, general CCD |
void OnCollisionEnter(Collision other) { // First frame of contact
if (other.gameObject.CompareTag("Player"))
Debug.Log("Player hit this object");
}
void OnCollisionStay(Collision other) { } // Every physics frame while touching
void OnCollisionExit(Collision other) { } // Contact ended
void OnTriggerEnter(Collider other) { // Entered trigger volume
if (other.CompareTag("Player"))
Debug.Log("Player entered trigger zone");
}
void OnTriggerStay(Collider other) { // Each frame inside trigger
other.attachedRigidbody?.AddForce(Vector3.up * 12f, ForceMode.Acceleration);
}
void OnTriggerExit(Collider other) { } // Left trigger volume
| Pair | Collision Events | Trigger Events |
|---|---|---|
| Static + Dynamic | Yes | -- |
| Static + Kinematic | No | -- |
| Static + Static | No | -- |
| Dynamic + Dynamic | Yes | -- |
| Dynamic + Kinematic | Yes | -- |
| Kinematic + Kinematic | No | -- |
| Trigger + Dynamic/Kinematic | -- | Yes |
| Static Trigger + Dynamic/Kinematic | -- | Yes |
At least one dynamic (non-kinematic) Rigidbody is required for collision events. The physics engine only applies forces to GameObjects with Rigidbody or ArticulationBody components.
Joints connect Rigidbody components together or to fixed points in space.
| Joint Type | Description | Use Case |
|---|---|---|
| FixedJoint | Locks two bodies together (implemented as a spring) | Attaching objects, breakable connections |
| HingeJoint | Rotation around a single axis | Doors, pendulums, chains |
| SpringJoint | Elastic connection maintaining distance | Bungees, tethers |
| CharacterJoint | Ball-and-socket with constrained angles | Ragdolls (hips, shoulders) |
| ConfigurableJoint | Fully customizable constraints | Any specialized connection |
For industrial/robotics applications requiring precise articulation, use ArticulationBody instead of regular joints.
Unity's 2D physics uses a separate engine optimized for 2D workflows.
| Aspect | 3D | 2D |
|---|---|---|
| Engine | PhysX | Box2D |
| Rigidbody | Rigidbody | Rigidbody2D |
| Collider | Collider | Collider2D |
| Physics class | Physics | Physics2D |
| Vectors | Vector3 | Vector2 |
| Rotation | Quaternion (3-axis) | float (Z-axis only) |
| Gravity | Vector3 | Vector2 |
| ForceMode | ForceMode | ForceMode2D |
| Type | Description |
|---|---|
| Dynamic | Fully simulated, responds to forces and gravity |
| Kinematic | Moved via script, detects collisions but not affected by forces |
| Static | Immovable, for level geometry |
BoxCollider2D, CircleCollider2D, CapsuleCollider2D, PolygonCollider2D, EdgeCollider2D, CompositeCollider2D, TilemapCollider2D
See reference file references/physics2d-api.md for full 2D API details.
public class PlayerMovement : MonoBehaviour
{
public float moveSpeed = 5f;
public float jumpForce = 7f;
private Rigidbody rb;
private bool jumpRequested;
void Awake()
{
rb = GetComponent<Rigidbody>();
}
void Update()
{
// Capture input in Update (legacy Input Manager; see unity-input for new Input System)
if (Input.GetButtonDown("Jump"))
jumpRequested = true;
}
void FixedUpdate()
{
// Apply physics in FixedUpdate (legacy Input; see unity-input)
float h = Input.GetAxis("Horizontal");
float v = Input.GetAxis("Vertical");
Vector3 move = new Vector3(h, 0f, v) * moveSpeed;
rb.linearVelocity = new Vector3(move.x, rb.linearVelocity.y, move.z);
if (jumpRequested)
{
rb.AddForce(Vector3.up * jumpForce, ForceMode.Impulse);
jumpRequested = false;
}
}
}
// Cache collider reference in Awake() -- avoid GetComponent in hot paths
private CapsuleCollider capsule;
void Awake() => capsule = GetComponent<CapsuleCollider>();
public bool IsGrounded()
{
float radius = 0.3f;
return Physics.SphereCast(transform.position, radius, Vector3.down,
out _, (capsule.height / 2f) - radius + 0.1f,
LayerMask.GetMask("Ground"));
}
public class HoverPad : MonoBehaviour
{
public float hoverForce = 12f;
void OnTriggerStay(Collider other)
{
if (other.attachedRigidbody != null)
other.attachedRigidbody.AddForce(Vector3.up * hoverForce, ForceMode.Acceleration);
}
}
private readonly RaycastHit[] hits = new RaycastHit[10];
void DetectEnemies()
{
int count = Physics.RaycastNonAlloc(transform.position, transform.forward,
hits, 50f, LayerMask.GetMask("Enemies"));
for (int i = 0; i < count; i++)
Debug.Log("Hit: " + hits[i].collider.name);
}
// BAD: Forces applied at variable frame rate cause inconsistent behavior
void Update()
{
rb.AddForce(Vector3.forward * 10f); // WRONG
}
// GOOD: Use FixedUpdate for physics
void FixedUpdate()
{
rb.AddForce(Vector3.forward * 10f); // CORRECT
}
// BAD: Bypasses physics, breaks collision detection
transform.position += Vector3.forward * speed * Time.deltaTime; // WRONG
// GOOD: Use Rigidbody methods
rb.MovePosition(rb.position + Vector3.forward * speed * Time.fixedDeltaTime); // CORRECT
// Or apply forces:
rb.AddForce(Vector3.forward * speed);
MeshCollider on a simple box-shaped object is wasteful. Use BoxCollider instead -- much cheaper and equally accurate for simple shapes. Reserve MeshCollider for complex concave geometry.
// BAD: GC allocation every collision
void OnCollisionEnter(Collision other) {
var enemies = GameObject.FindGameObjectsWithTag("Enemy"); // WRONG
}
// GOOD: Cache in Start(), reuse in callbacks
Physics.Raycast(origin, direction, out hit); // BAD: hits everything
Physics.Raycast(origin, direction, out hit, 100f,
LayerMask.GetMask("Ground", "Obstacles")); // GOOD: filtered
| Category | Key Members |
|---|---|
| Physics (static) | Raycast, RaycastAll, RaycastNonAlloc, SphereCast, BoxCast, CapsuleCast, Linecast, OverlapSphere, OverlapBox, CheckSphere, CheckBox, ClosestPoint, ComputePenetration, Simulate, SyncTransforms |
| Physics properties | gravity, defaultContactOffset, bounceThreshold, sleepThreshold, defaultSolverIterations, queriesHitTriggers, queriesHitBackfaces, simulationMode |
| Rigidbody | mass, linearDamping, angularDamping, useGravity, isKinematic, linearVelocity, angularVelocity, interpolation, collisionDetectionMode, AddForce(), AddTorque(), MovePosition(), MoveRotation(), Sleep(), WakeUp() |
| Callbacks | OnCollisionEnter(Collision), OnCollisionStay, OnCollisionExit, OnTriggerEnter(Collider), OnTriggerStay, OnTriggerExit |
See references/physics3d-api.md for full method signatures, parameters, and overloads.
See references/physics2d-api.md for the complete 2D physics API.
For non-physics-driven character movement (FPS controllers, NPCs), use CharacterController instead of Rigidbody. It handles slopes, steps, and collision sliding without physics simulation. See references/character-controller.md for full API, patterns, and CharacterController vs Rigidbody comparison.