# 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: 1. **Force-based** (preferred for realistic motion): ```csharp _rb.AddForce(Vector3.up * jumpForce, ForceMode.VelocityChange); _rb.AddForceAtPosition(force, wheel.position, ForceMode.Force); ``` 2. **Direct velocity set** (preferred for arcade controllers): ```csharp 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 = true` when you want to control rotation manually (most third-person controllers). * Use `rb.interpolation = RigidbodyInterpolation.Interpolate` to smooth out stutter when the physics timestep (50 Hz) is slower than the render rate (60+ Hz). * Set `rb.collisionDetectionMode = CollisionDetectionMode.Continuous` for fast-moving objects to prevent tunnelling. ## Colliders * `BoxCollider`, `SphereCollider`, `CapsuleCollider`, `MeshCollider` are the built-in shapes. * `MeshCollider` should be **convex** unless the mesh is static (mark `Convex` in the inspector) -- rigidbodies require convex colliders. * Set `isTrigger = true` to receive `OnTriggerEnter/Stay/Exit` without physically blocking movement. Use triggers for pickups, zones, audio volumes. ```csharp private void OnTriggerEnter(Collider other) { if (other.CompareTag("Player")) Debug.Log("Player entered the zone"); } ``` ## Raycasting ```csharp // 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(); if (hp != null) hp.TakeDamage(damage); } ``` > Use `QueryTriggerInteraction.Ignore` to skip trigger colliders in physics > queries unless you explicitly want them. ## Layer masks Layer masks let you filter physics queries and collisions: ```csharp 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 `OnTriggerEnter` unless **at least one** of their GameObjects has a Rigidbody. * A Rigidbody marked `IsKinematic` will not respond to forces but **will** push other rigidbodies. * Adjust the collision matrix in `Project Settings > Physics` to skip collision between layers (e.g. Player vs. PlayerBullet).