# 6. Physics 2D - Pitfalls and the Physics Core 2D API Part of [[Physics 2D]]. Previous: [[5. Physics 2D Kinematic Platformer with Slide]]. 3D counterpart: [[7. Physics Pitfalls]]. > [!note] Verification status > `Physics2D.simulationMode` / `SimulationMode2D` (`FixedUpdate`, `Update`, `Script`), `useSubStepping`, `maxSubStepCount` checked against the 6000.5 reference. The low-level API is documented as **`UnityEngine.LowLevelPhysics2D`** in the 6000.3 Manual ("New in Unity 6.3") and as the **"Physics Core 2D API", namespace `Unity.U2D.Physics`** in the 6000.5 Manual (both checked 2026-11-07) - i.e. it was renamed between those versions. **Code not compiled in Unity.** ## 6.1 Pitfalls table | Symptom | Cause | Fix | |---|---|---| | `OnCollisionEnter` never fires on a sprite | 3D callback on 2D colliders | Use `OnCollisionEnter2D(Collision2D)` / `OnTriggerEnter2D(Collider2D)` | | Raycast misses a BoxCollider2D | `Physics.Raycast` (3D) used | `Physics2D.Raycast` - 2D and 3D worlds never see each other | | Changing Project Settings > Physics layer matrix does nothing | That's the 3D matrix | Project Settings > **Physics 2D** | | Character catches on tile seams | Many box colliders | Composite Collider 2D (Outlines) - Part 2 | | Fast bullet passes through thin walls | Discrete detection | `CollisionDetectionMode2D.Continuous` on the bullet, or cast ahead with `Rigidbody2D.Cast` | | Everything feels floaty / slow | Sprites imported at 100 PPU but world scale is huge (e.g. 10 m tall character) | Keep characters ~1-2 units tall; gravity is in units/s^2 | | Kinematic player doesn't get collision callbacks with walls | Kinematic vs static contacts off | Enable **Use Full Kinematic Contacts** | | Kinematic player not carried by a moving platform | Kinematic bodies ignore friction | Add platform velocity in your controller | | Box falls asleep on a platform and floats when the platform moves away | Sleeping body | Move the platform with `MovePosition` (wakes contacts) or `WakeUp()` riders | | Queries hit the caster itself | Ray starts inside own collider | `Physics2D.queriesStartInColliders = false` or use `Rigidbody2D.Cast` / `Collider2D.Cast` | | Jitter when camera follows a body | No interpolation | `Interpolate` on the followed body; follow in LateUpdate or via [[Cinemachine 3]] | | Overlap query with Z filtering behaves oddly | 2D colliders at different Z still *collide*; Z only matters for depth filtering | Don't use Z for gameplay separation; use layers | | Physics runs at 50 Hz on a 120 Hz display and looks choppy | Default fixed timestep | Interpolation, or `Physics2D.simulationMode = SimulationMode2D.Update` for simple games (variable step = less deterministic) | ## 6.2 Simulation mode and sub-stepping `Physics2D.simulationMode` decides **when** 2D physics steps: - `FixedUpdate` (default) - fixed timestep, deterministic-ish, use with interpolation. - `Update` - once per frame with `Time.deltaTime`. Smooth, simple, but frame-rate-dependent results. - `Script` - you call `Physics2D.Simulate(dt)` yourself (rollback netcode, replays, deterministic lockstep, turn-based "simulate until rest"). `Physics2D.useSubStepping` + `maxSubStepCount` + `minSubStepFPS` let the step split itself when the frame rate drops, instead of tunneling. ```csharp using UnityEngine; /// Steps 2D physics manually: normal speed, slow-mo, and single-step debugging. public class ManualPhysics2DStepper : MonoBehaviour { [SerializeField, Range(0f, 2f)] float timeScale = 1f; [SerializeField] float fixedStep = 1f / 60f; [SerializeField] bool paused; float _accumulator; void OnEnable() => Physics2D.simulationMode = SimulationMode2D.Script; void OnDisable() => Physics2D.simulationMode = SimulationMode2D.FixedUpdate; void Update() { if (paused) return; _accumulator += Time.unscaledDeltaTime * timeScale; int guard = 0; while (_accumulator >= fixedStep && guard++ < 8) { Physics2D.Simulate(fixedStep); _accumulator -= fixedStep; } } [ContextMenu("Step Once")] void StepOnce() => Physics2D.Simulate(fixedStep); } ``` > [!warning] Code not compiled in Unity > Not compiled or play-tested. In Script mode, `FixedUpdate` still runs on its own schedule - controllers that apply movement in `FixedUpdate` (Part 5) won't line up with your manual steps; call their logic from this loop instead. This is the Game Loop pattern from [[Game Programming Patterns (Review)]] made explicit. ## 6.3 The Physics Core 2D API (Box2D v3) - when to look at it Unity 6.3 added low-level 2D physics APIs built on **Box2D v3**: multithreaded, more deterministic, debug drawing in Editor and runtime, up to 64 collision layers, struct-based objects usable from jobs. In 6000.3 the namespace is `UnityEngine.LowLevelPhysics2D`; the 6000.5 Manual documents it as the **Physics Core 2D API** in `Unity.U2D.Physics`. It **does not interact** with Rigidbody2D/Collider2D - it's a separate world. Use it when you need thousands of bodies (bullet-hell, sand, crowds), deterministic replays, multiple isolated worlds, or 2D physics on a 3D plane. Stay with components for normal gameplay - everything in Parts 1-5. Minimal 6000.5 example, following the Manual's "Create an object" page: ```csharp using UnityEngine; using Unity.U2D.Physics; // 6000.5 namespace; in 6000.3 use UnityEngine.LowLevelPhysics2D public class PhysicsCoreCircle : MonoBehaviour { // Definitions show up in the Inspector; set the body type to Dynamic there. public PhysicsBodyDefinition bodyDefinition = PhysicsBodyDefinition.defaultDefinition; public PhysicsShapeDefinition shapeDefinition = PhysicsShapeDefinition.defaultDefinition; public float radius = 0.5f; void Start() { PhysicsWorld world = PhysicsWorld.defaultWorld; PhysicsBody body = world.CreateBody(bodyDefinition); CircleGeometry circle = new CircleGeometry { radius = radius }; body.CreateShape(circle, shapeDefinition); // Shapes are drawn by the API's debug renderer; no sprite or GameObject needed. } } ``` > [!warning] Code not compiled in Unity > Not compiled. Mirrors the 6000.5 Manual snippet; object destruction/ownership rules are on the Manual's "Destroy" page - read it before creating objects every Start. Official samples: https://github.com/Unity-Technologies/PhysicsExamples2D ## Docs - `Physics2D.simulationMode`: https://docs.unity3d.com/6000.5/Documentation/ScriptReference/Physics2D-simulationMode.html - `SimulationMode2D`: https://docs.unity3d.com/6000.5/Documentation/ScriptReference/SimulationMode2D.html - Physics 2D settings: https://docs.unity.com/en-us/engine/6000.5/manual/unity-editor/editor-settings-reference/comp-manager-group/class-physics2dsettings - New in Unity 6.3 (low-level 2D physics): https://docs.unity.com/en-us/engine/6000.3/manual/whats-new/unity63 - Physics Core 2D API intro (6000.5): https://docs.unity.com/en-us/engine/6000.5/manual/unity2d/2d-physics-api/get-started/introduction - Create an object (6000.5): https://docs.unity.com/en-us/engine/6000.5/manual/unity2d/2d-physics-api/create-objects/physics-object - Create a world (6000.3, LowLevelPhysics2D): https://docs.unity.com/en-us/engine/6000.3/manual/unity2d/2d-physics-api/create-objects/world