Box2D Documentation

repository·main·Indexed 27 days ago

https://github.com/erincatto/box2d

A high-performance 2D physics engine for games featuring a data-oriented design, multithreading, and SIMD optimizations (SSE2 and Neon). The library supports C17 and C++20, providing tools for collision primitives (circles, capsules, polygons, segments), spatial queries via b2DynamicTree, and a geometric character mover for FPS or platforming games. Includes guides for building with CMake presets across Windows, Linux, and macOS, as well as using the integrated replay viewer for .b2rec files.

Tokens
23.1K
Snippets
60
Records
108
Agent score
94%

What's inside Box2D

  1. Core Concepts of Box2D

    main

    Box2D is a 2D rigid body simulation library written in portable C17. It uses several fundamental objects to simulate physics:

    • Rigid Body (Body): A chunk of matter with constant internal distances. It has 3 degrees of freedom (two translation, one rotation).
    • Shape: Binds collision geometry to a body and defines material properties like density, friction, and restitution. Shapes are used by the broad-phase collision system.
    • Constraint: A physical connection that removes degrees of freedom from bodies.
      • Contact Constraint: Automatically created by Box2D to prevent penetration and simulate friction/restitution.
      • Joint Constraint: Used to hold bodies together (e.g., revolute, prismatic, distance). Joints can include limits, motors, and springs.
    • World: A collection of bodies, shapes, joints, and contacts. Multiple independent worlds can exist simultaneously.
    • Solver: A high-performance sequential solver that advances time and resolves constraints in $O(N)$ time, where $N$ is the number of constraints.
    • Continuous Collision: Algorithms (Time of Impact interpolation and speculative collision) used to prevent 'tunneling' (objects passing through each other during discrete time steps).
  2. Understand Box2D Body Types

    main

    Box2D uses three types of rigid bodies, each with different simulation behaviors:

    • b2_staticBody: Does not move under simulation and has infinite mass. It has zero velocity and does not collide with other static or kinematic bodies.
    • b2_kinematicBody: Moves according to its velocity but does not respond to forces. It behaves as if it has infinite mass and does not collide with other kinematic or static bodies. Use this for animated objects that should not be affected by physics.
    • b2_dynamicBody: Fully simulated; moves according to forces and torques. It can collide with all body types and always has finite, non-zero mass.

    Note: It is more efficient to establish the body type at creation via the body definition than to change it later.

  3. Understand the b2DynamicTree for spatial queries

    main

    The b2DynamicTree is a hierarchical axis-aligned bounding box (AABB) tree used to organize large numbers of shapes efficiently. It operates on b2AABB objects paired with user data integers.

    Key features include:

    • Efficient Ray Casting: Traverses the tree to skip large numbers of shapes, avoiding brute-force checks.
    • Region Queries: Quickly finds all leaf AABBs that overlap a specific query AABB.
    • Self-Balancing: Uses tree rotations to maintain balance even with degenerate input.

    Note: Most users should not interact with b2DynamicTree directly. Instead, use the high-level ray cast and region query functions provided by b2World.

  4. Access Box2D Public API

    main

    The public API is located in the include directory. All public features are supported. The internal implementation is located in the src directory and should not be used directly.

    Public features include:

    • Rigid body simulation
    • Collision routines (overlap and cast queries)
    • Bounding volume hierarchy (dynamic tree) for spatial sorting
    • Math and collision utilities
  5. Explore the Box2D Samples application

    main

    The Box2D samples application serves as a testing framework and demo environment to help you learn the library. It provides a visual way to interact with physics simulations and explore various Box2D usage patterns.

    Key features include:

    • Camera controls: Pan and zoom capabilities.
    • Interaction: Mouse dragging of dynamic bodies.
    • Navigation: A tree view containing many different samples.
    • GUI: Tools for selecting samples, tuning parameters, and toggling debug drawing options.
    • Simulation control: Ability to pause the simulation or perform single-step execution.
    • Performance monitoring: Support for multithreading and real-time performance data.

    Note: The samples application is built using GLFW and imgui. It is a separate demonstration tool and is not part of the core Box2D library. The Box2D library itself is rendering-agnostic; you do not need a renderer to use the core physics engine.

  6. Implement debug drawing with b2DebugDraw

    main

    To visualize the Box2D simulation, implement the function pointers defined in the b2DebugDraw struct. This is the preferred method for rendering the physics world because it accesses necessary data through a stable interface rather than internal structures.

    b2DebugDraw can visualize:

    • Shapes
    • Joints
    • Broad-phase axis-aligned bounding boxes (AABBs)
    • Center of mass
    • Contact points
  7. Configure Box2D Units and Scaling

    main

    Box2D is tuned for MKS (meters-kilogram-second) units. To ensure stable and realistic simulation, follow these guidelines:

    • Object Size: Moving objects should be between 0.1 and 10 meters. Avoid using pixels directly as units, as 200 pixels would be interpreted as a 45-story building.
    • Static Shapes: Can be up to 50 meters long. For larger worlds, split static geometry into multiple bodies.
    • World Size: Works best with worlds smaller than 12 kilometers. Stability may degrade beyond 24 kilometers.
    • Angles: Box2D uses radians, not degrees. Rotation is stored as a complex number, so angles range between $-\pi$ and $\pi$.
    • Scaling: Use a scaling factor to convert Box2D meter units to your rendering engine's pixel coordinates.

    To change the length units globally, call b2SetLengthUnitsPerMeter() at application startup.

  8. Handle degenerate hulls when creating polygons

    main

    When using b2ComputeHull() with potentially degenerate points (coincident or collinear), you must check if the hull was created successfully by verifying the count member. If count == 0, the hull is invalid and attempting to create a polygon from it will trigger an assertion.

    b2Hull questionableHull = b2ComputeHull(randomPoints, 8);
    if (questionableHull.count == 0)
    {
        // handle failure
    }
  9. Manage coordinate systems and units

    main

    It is highly recommended to use MKS (meters, kilograms, and seconds) units and radians for angles within Box2D to ensure simulation stability.

    Keep your entire game world in meters and use your graphics API (like OpenGL) to scale the world into screen space (pixels) using a viewport transformation.

    float lowerX = -25.0f, upperX = 25.0f, lowerY = -5.0f, upperY = 25.0f;
    gluOrtho2D(lowerX, upperX, lowerY, upperY);

    Approach 2: Manual Conversion

    If your game logic must operate in pixels, convert values when passing them to or receiving them from Box2D. Choose a conversion factor based on character size (e.g., 50 pixels per meter).

    Conversion Formulas (Example: 50px/m):

    • Pixels to Meters: meters = 0.02f * pixels
    • Meters to Pixels: pixels = 50.0f * meters

    Approach 3: Experimental Global Setting

    You can attempt to set length units globally using b2SetLengthUnitsPerMeter(), but note that this is experimental and not well tested.

  10. Process Body Movement Events

    main

    Instead of iterating over all bodies to find moved ones, use b2World_GetBodyEvents after calling b2World_Step(). This provides a contiguous array of b2BodyMoveEvent structures, which is more cache-friendly and efficient for updating graphical entities.

    Each event contains the userData and the new transform. It also includes a fellAsleep flag to indicate if the body entered a sleep state during this step.

    b2BodyEvents events = b2World_GetBodyEvents(myWorldId);
    for (int i = 0; i < events.moveCount; ++i)
    {
        const b2BodyMoveEvent* event = events.moveEvents + i;
        MyGameObject* gameObject = event->userData;
        MoveGameObject(gameObject, event->transform);
        if (event->fellAsleep)
        {
            SleepGameObject(gameObject);
        }
    }
  11. Migrate from Box2D 2.4 to 3.0

    main

    Box2D version 3.0 is a complete rewrite from C++ to C. Key architectural changes include:

    • C API: The library now uses C instead of C++.
    • Handles over Pointers: Objects are managed via identifiers (handles) like b2WorldId, b2BodyId, and b2ShapeId rather than direct pointers. These handles should be treated as atomic values and passed by value.
    • Manual Memory Management: Since there are no destructors, you must explicitly destroy worlds, bodies, shapes, and joints using their respective b2Destroy... functions.
    • Initialization: All structures must be initialized. Use the provided b2Default...Def() helper functions (e.g., b2DefaultWorldDef()) to ensure all fields are set to sensible defaults.
    • Multithreading: The engine is designed for multithreading, which has led to the removal of most callbacks in favor of event-based data access after the time step.
    • Solver Change: The engine uses a new sub-stepping solver called Soft Step.
  12. Record a Box2D simulation

    main

    You can record a simulation into a memory buffer to reproduce a run exactly. A recording consists of a world snapshot followed by a log of every world-mutating API call.

    To record, create a b2Recording buffer, start recording on a b2WorldId, and run your simulation. You can start recording before the first step to capture the entire session or mid-session to capture a specific window of time.

    Note: b2World_StartRecording must be called at a step boundary. The recording buffer grows automatically, so you can pass 0 to b2CreateRecording to use a small default capacity.

    b2WorldId worldId = b2CreateWorld( &worldDef );
    
    b2Recording* recording = b2CreateRecording( 0 );   // 0 = small default capacity
    b2World_StartRecording( worldId, recording );       // snapshots the world, then logs calls
    
    // ... create bodies, step the world as usual ...
    
    b2World_StopRecording( worldId );