File: README.md
Box2D
Box2D is a 2D physics engine for games.
Build Status
Features
Collision
- Continuous collision detection
- Contact events
- Convex polygons, capsules, circles, rounded polygons, segments, and chains
- Multiple shapes per body
- Collision filtering
- Ray casts, shape casts, and overlap queries
- Sensor system
Physics
- Robust Soft Step rigid body solver
- Continuous physics for fast translations and rotations
- Island based sleep
- Revolute, prismatic, distance, mouse joint, weld, and wheel joints
- Joint limits, motors, springs, and friction
- Joint and contact forces
- Body movement events and sleep notification
System
- Data-oriented design
- Written in portable C17
- Extensive multithreading and SIMD
- Optimized for large piles of bodies
Samples
- OpenGL with GLFW
- Graphical user interface with imgui
- Many samples to demonstrate features and performance
Building All Platforms
Building with CMake presets
The presets in CMakePresets.json give one build flow on every platform and are picked up automatically by Visual Studio, VS Code, and CLion (open the folder and choose a preset). From the command line:
- Windows:
cmake --preset windowsthencmake --build --preset windows-release - Linux:
cmake --preset linux-releasethencmake --build --preset linux-release - macOS:
cmake --preset macosthencmake --build --preset macos-release
Use the *-debug build presets for a debug build (not recommended for the replay viewer). The presets use the default native toolchain (the installed Visual Studio on Windows, Make on Linux, Xcode on macOS), so no specific compiler version is required.
Building for Visual Studio
- Install Visual Studio
- Run
build_vs2026.batfor Visual Studio 2026, or use thewindowspreset above for other versions - Open and build the generated solution in the
buildfolder
Building for Linux
- Run
build.shfrom a bash shell - Results are in the build sub-folder
Building for Xcode
- mkdir build
- cd build
- cmake -G Xcode ..
- Open
box2d.xcodeproj - Select the samples scheme
- Build and run the samples
Building and installing
- mkdir build
- cd build
- cmake ..
- cmake --build . --config Release
- cmake --install . (might need sudo)
Replay viewer
The samples app doubles as a viewer for Box2D recordings (.b2rec files). Any preset above builds it. Pass a recording on the command line to open it directly:
- Windows:
build\bin\Release\samples.exe path\to\session.b2rec - Linux:
build/bin/samples path/to/session.b2rec - macOS:
build/bin/Release/samples path/to/session.b2rec
On Windows you can also drag a .b2rec file onto samples.exe. The viewer runs from any directory. Without an argument, open a recording from the Replay menu. See docs/recording.md for how to make a recording.
Compatibility
The Box2D library and samples build and run on Windows, Linux, and Mac.
You will need a compiler that supports C17 to build the Box2D library.
You will need a compiler that supports C++20 to build the samples.
Box2D uses SSE2 and Neon SIMD math to improve performance. This can be disabled by defining BOX2D_DISABLE_SIMD.
Documentation
Community
Contributing
Please do not submit pull requests. Instead, please file an issue for bugs or feature requests. For support, please visit the Discord server.
Giving Feedback
Please file an issue or start a chat on discord. You can also use GitHub Discussions.
License
Box2D is developed by Erin Catto and uses the MIT license.
Sponsorship
Support development of Box2D through Github Sponsors.
Please consider starring this repository and subscribing to my YouTube channel.
LLM Usage
LLMs are used in the following areas:
- unit tests
- samples app
- migrating code between Box2D and Box3D
- build configuration
- code reviews
- benchmarking
Elsewhere all code is developed and written by me. I take responsibility for every line of code in Box2D/3D.
External ports, wrappers, and bindings (unsupported)
- Beef bindings - https://github.com/EnokViking/Box2DBeef
- C++ bindings - https://github.com/HolyBlackCat/box2cpp
- WASM - https://github.com/Birch-san/box2d3-wasm
File: docs/character.md
Character mover
Caution:
The character mover feature is new to version 3.1 and should be considered experimental.
Box2D provides a few structures and functions you can use to build a character mover.
These features support a geometric character mover. This is like
a kinematic mover except the character mover is not a rigid body and does
not exist in the simulation world. This is done to achieve features that
would be difficult with a kinematic body.
This type of mover may not be suitable for your game. It is less physical than a rigid body, but
it gives you more control over the movement. It is the type of mover you might find
in a first-person shooter or a game with platforming elements.
The mover is assumed to be a capsule. Using a capsule helps keep movement smooth. The capsule should
have a significant radius. It does not have to be a vertical capsule, but that is likely
to be the easiest setup. There is no explicit handling of rotation. But slow rotation can
work with this system.
Let's review the features. First there are a couple world query functions.
b2World_CastMover() is a custom shape cast that tries to avoid getting stuck when shapes start out touching.
The feature is called encroachment. Since the capsule has a significant radius it can move closer
to a surface it is touching without the inner line segment generating an overlap, which would cause
the shape cast to fail. Due to the internal use of GJK, encroachment has little cost. The idea with encroachment is that
the mover is trying to slide along a surface and we don't want to stop that even if there is some small movement into the surface.
b2World_CollideMover() complements the cast function. This function generates collision planes for touching and/or overlapped surfaces. The character mover is assumed to have a fixed rotation, so it doesn't need contact manifolds or contact points. It just needs collision planes. Each plane is returned with the b2Plane and a b2ShapeId for each shape the mover is touching.
Both mover functions take a b2Pos origin and the mover capsule is relative to it. Near the world origin pass b2Pos_zero and a world capsule. In large world mode pass the mover's vicinity so the character stays precise far from the origin. The returned planes are relative to the same origin and feed b2SolvePlanes() directly.
Once you have some collision planes from b2World_CollideMover(), you can process and filter them to generate an array of b2CollisionPlane. These collision planes can then be sent to b2SolvePlanes() to generate a new position for the mover
that attempts to find the optimal new position given the current position.
These collision planes support soft collision using a pushLimit. This push limit is a distance value. A rigid surface will have a push limit of FLT_MAX. However, you may want some surfaces to have a limited effect on the character. For example, you may want the mover to push through other players or enemies yet still resolve the collision so they are not overlapped. Another example is a door or elevator that could otherwise push the mover through the floor.
Finally after calling b2SolverPlanes() you can call b2ClipVector() to clip your velocity vector so the mover will not keep trying to push into a wall, which could lead to a huge velocity accumulation otherwise.
The Mover sample shows all these functions being used together. It also includes other common character features such as acceleration and friction, jumping, and a pogo stick.
File: docs/collision.md
Collision
Box2D provides geometric types and functions. These include:
- primitives: circles, capsules, segments, and convex polygons
- convex hull and related helper functions
- mass and bounding box computation
- local ray and shape casts
- contact manifolds
- shape distance
- time of impact
- dynamic bounding volume tree
- character movement solver
The collision interface is designed to be usable outside of rigid body simulation.
For example, you can use the dynamic tree for other aspects of your game besides physics.
However, the main purpose of Box2D is to be a rigid body physics
engine. So the collision interface only contains features that are also useful in
the physics simulation.
Shape Primitives
Shape primitives describe collision geometry and may be used independently of
physics simulation. At a minimum, you should understand how to create
primitives that can be later attached to rigid bodies.
Box2D shape primitives support several operations:
- Test a point for overlap with the primitive
- Perform a ray cast against the primitive
- Compute the primitive's bounding box
- Compute the mass properties of the primitive
Circles
Circles have a center and radius. Circles are solid.
b2Circle circle;
circle.center = (b2Vec2){2.0f, 3.0f};
circle.radius = 0.5f;You can also initialize a circle and other structures inline. This is an equivalent circle:
b2Circle circle = {{2.0f, 3.0f}, 0.5f};Capsules
Capsules have two center points and a radius. The center points are the centers of two
semicircles that are connected by a rectangle.
b2Capsule capsule;
capsule.center1 = (b2Vec2){1.0f, 1.0f};
capsule.center2 = (b2Vec2){2.0f, 3.0f};
capsule.radius = 0.25f;Polygons
Box2D polygons are solid convex polygons. A polygon is convex when all
line segments connecting two points in the interior do not cross any
edge of the polygon. Polygons are solid and never hollow. A polygon must
have 3 or more vertices.
Polygons vertices are stored with a counter clockwise winding (CCW). We
must be careful because the notion of CCW is with respect to a
right-handed coordinate system with the z-axis pointing out of the
plane. This might turn out to be clockwise on your screen, depending on
your coordinate system conventions.
The polygon members are public, but you should use initialization
functions to create a polygon. The initialization functions create
normal vectors and perform validation.
Polygons in Box2D have a maximum of 8 vertices, as controlled by #B2_MAX_POLYGON_VERTICES.
If you have more complex shapes, I recommend to use multiple polygons.
There are a few ways to create polygons. You can attempt to create them manually,
but this is not recommended. Instead there are several functions provided to create them.
For example if you need a square or box you can use these functions:
b2Polygon square = b2MakeSquare(0.5f);
b2Polygon box = b2MakeBox(0.5f, 1.0f);The values provided to these functions are extents, which are half-widths or half-heights.
This corresponds with circles and capsules using radii instead of diameters.
Box2D also supports rounded polygons. These are convex polygons with a thick rounded skin.
float radius = 0.25f;
b2Polygon roundedBox = b2MakeRoundedBox(0.5f, 1.0f, radius);If you want a box that is not centered on the body origin, you can use an offset box.
b2Vec2 center = {1.0f, 0.0f};
float angle = b2_pi / 4.0f;
b2Rot rotation = b2MakeRot(angle);
b2Polygon offsetBox = b2MakeOffsetBox(0.5f, 1.0f, center, rotation);If you want a more general convex polygon, you can compute the hull using b2ComputeHull(). Then you can
create a polygon from the hull. You can make this rounded as well.
b2Vec2 points[] = {{-1.0f, 0.0f}, {1.0f, 0.0f}, {0.0f, 1.0f}};
b2Hull hull = b2ComputeHull(points, 3);
float radius = 0.1f;
b2Polygon roundedTriangle = b2MakePolygon(&hull, radius);If you have an automatic process for generating convex polygons, you may feed a degenerate set of points to b2ComputeHull(). You should check that the hull was created successfully before creating the polygon or you will get an assertion.
b2Hull questionableHull = b2ComputeHull(randomPoints, 8);
if (questionableHull.count == 0)
{
// handle failure
}Degenerate points may be coincident and/or collinear. For the hull to be viable, the enclosed area must be sufficiently positive.
Segments
Segments are line segments. Segment
shapes can collide with circles, capsules, and polygons but not with other line segments.
The collision algorithms used by Box2D require that at least
one of two colliding shapes has sufficiently positive area. Segment shapes have no area, so
segment-segment collision is not possible.
b2Segment segment1;
segment1.point1 = (b2Vec2){0.0f, 0.0f};
segment2.point2 = (b2Vec2){1.0f, 0.0f};
// equivalent
b2Segment segment2 = {{0.0f, 0.0f}, {1.0f, 0.0f}};Ghost Collisions
In many cases a game environment is constructed by connecting several
segment shapes end-to-end. This can give rise to an unexpected artifact
when a polygon slides along the chain of segments. In the figure below there is
a box colliding with an internal vertex. These ghost collisions
are caused when the polygon collides with an internal vertex generating
an internal collision normal.
{html: width=30%}
If edge1 did not exist this collision would seem fine. With edge1
present, the internal collision seems like a bug. But normally when
Box2D collides two shapes, it views them in isolation.
b2ChainSegment provides a mechanism for eliminating ghost
collisions by storing the adjacent ghost vertices. Box2D uses these
ghost vertices to prevent internal collisions.
{html: width=30%}
The Box2D algorithm for dealing with ghost collisions only supports
one-sided collision. The front face is to the right when looking from the first
vertex towards the second vertex. This matches the counter-clockwise winding order
used by polygons.
Chain segment
Chain segments use a concept called ghost vertices that Box2D can use to eliminate ghost
collisions.
b2ChainSegment chainSegment = {0};
chainSegment.ghost1 = (b2Vec2){1.7f, 0.0f};
chainSegment.segment = (b2Segment){{1.0f, 0.25f}, {0.0f, 0.0f}};
chainSegment.ghost2 = (b2Vec2){-1.7f, 0.4f};These ghost vertices must align with vertices of neighboring chain segments, making them
tedious and error-prone to setup.
Chain segments are not created directly. Instead, you can create chains of line
segments. See b2ChainDef and b2CreateChain().
Geometric Queries
You can perform a geometric queries on a single shape.
Shape Point Test
You can test a point for overlap with a shape. You provide a transform
for the shape and a world point.
b2Vec2 point = {5.0f, 2.0f};
bool hit = b2PointInCapsule(point, &myCapsule);See also b2PointInCircle() and b2PointInPolygon().
Ray Cast
You can cast a ray at a shape to get the point of first intersection and normal vector.
Caution:
No hit will register if the ray starts inside a convex shape like a circle or polygon. This is
consistent with Box2D treating convex shapes as solid.
b2RayCastInput input = {0};
input.origin = (b2Vec2){0.0f, 0.0f};
input.translation = (b2Vec2){1.0f, 0.0f};
input.maxFraction = 1.0f;
b2CastOutput output = b2RayCastPolygon(&input, &myPolygon);
if (output.hit == true)
{
// do something
}Shape Cast
You can also cast a shape at another shape. This uses an abstract way of describing the moving shape. It is represented as a point cloud with a radius. This implies a convex shape even if the input data is not convex. The internal algorithm (GJK) will essentially only use the convex portion.
b2ShapeCastInput input = {0};
input.points[0] = (b2Vec2){1.0f, 0.0f};
input.points[1] = (b2Vec2){2.0f, -3.0f};
input.radius = 0.2f;
input.translation = (b2Vec2){1.0f, 0.0f};
input.maxFraction = 1.0f;
b2CastOutput output = b2ShapeCastPolygon(&input, &myPolygon);
if (output.hit == true)
{
// do something
}Even more generic, you can use b2ShapeCast() to linearly cast one point cloud at another point cloud. All shape cast functions use this internally.
Distance
b2ShapeDistance() function can be used to compute the distance between two
shapes. The distance function needs both shapes to be converted into ab2DistanceProxy (which are point clouds with radii). There is also some caching used to warm start the
distance function for repeated calls. This can improve performance when the shapes move by small amounts.
Time of Impact
If two shapes are moving fast, they may tunnel through each other in a
single time step.
{html: width=30%}
The b2TimeOfImpact() function is used to determine the time when two moving shapes collide.
This is called the time of impact (TOI). The main purpose of b2TimeOfImpact() is for
tunnel prevention. Box2D uses this internally to prevent moving objects from tunneling through
static shapes.
The b2TimeOfImpact() identifies an initial separating axis and
ensures the shapes do not cross on that axis. This process is repeated
as shapes are moved closer together, until they touch or pass by each other.
The TOI function might miss collisions that are clear at the final positions.
Nevertheless, it is very fast and adequate for tunnel prevention.
{html: width=30%}
{html: width=30%}
It is difficult to put a restriction on the rotation magnitude. There
may be cases where collisions are missed for small rotations. Normally,
these missed rotational collisions should not harm game play. They tend
to be glancing collisions.
The function requires two shapes (converted to b2DistanceProxy) and twob2Sweep structures. The sweep structure defines the initial and final
transforms of the shapes.
You can use fixed rotations to perform a shape cast. In this case, the
time of impact function will not miss any collisions.
Contact Manifolds
Box2D has functions to compute contact points for overlapping shapes. If
we consider circle-circle or circle-polygon, we can only get one contact
point and normal. In the case of polygon-polygon we can get two points.
These points share the same normal vector so Box2D groups them into a
manifold structure. The contact solver takes advantage of this to
improve stacking stability.
Normally you don't need to compute contact manifolds directly, however
you will likely use the results produced in the simulation.
The b2Manifold structure holds a normal vector and up to two contact
points. The contact points store the normal and tangential (friction) impulses
computed in the rigid body simulation.
Dynamic Tree
b2DynamicTree is used by Box2D to organize large numbers of
shapes efficiently. The object does not know directly about shapes. Instead it
operates on axis-aligned bounding boxes (b2AABB) with user data integers.
The dynamic tree is a hierarchical AABB tree. Each internal node in the
tree has two children. A leaf node is a single user AABB. The tree uses
rotations to keep the tree balanced, even in the case of degenerate
input.
The tree structure allows for efficient ray casts and region queries.
For example, you may have hundreds of shapes in your scene. You could
perform a ray cast against the scene in a brute force manner by ray
casting each shape. This would be inefficient because it does not take
advantage of shapes being spread out. Instead, you can maintain a
dynamic tree and perform ray casts against the tree. This traverses the
ray through the tree skipping large numbers of shapes.
A region query uses the tree to find all leaf AABBs that overlap a query
AABB. This is faster than a brute force approach because many shapes can
be skipped.
{html: width=30%}
{html: width=30%}
Normally you will not use the dynamic tree directly. Rather you will go
through the b2World functions for ray casts and region queries. If you plan
to instantiate your own dynamic tree, you can learn how to use it by
looking at how Box2D uses it. Also see the DynamicTree sample.
File: docs/foundation.md
Foundations
Box2D provides minimal base functionality for allocation hooks and vector math. The C interface
allows most runtime data and types to be defined internally in the src folder.
Assertions
Box2D will assert on bad input. This includes things like sending in NaN or infinity for values. It will assert if
you use negative values for things that should only be positive, such as density.
Box2D will also assert if an internal bug is detected. For this reason, it is advisable to build Box2D from source.
The Box2D library compiles in about a second on my computer.
You may wish to capture assertions in your application. In this case you can use b2SetAssertFcn(). This allows you
to override the debugger break and/or perform your own error handling.
Allocation
Box2D uses memory efficiently and minimizes per frame allocations by pooling memory. The engine quickly adapts to the
simulation size. After the first step or two of simulation, there should be no further per frame allocations.
As bodies, shapes, and joints are created and destroyed, their memory will be recycled. Internally all this data is stored in contiguous arrays. When an object is destroyed, the array element will be marked as empty. And when an object is created it will use empty slots in the array using an efficient free list.
Once the internal memory pools are initially filled, the only allocations should be for sleeping islands since their data is copied out of the main simulation. Generally, these allocations should be infrequent.
You can provide a custom allocator using b2SetAllocator() and you can get the number of bytes allocated using b2GetByteCount().
Version
The b2Version structure holds the current version so you can query this
at run-time using b2GetVersion().
b2Version version = b2GetVersion();
printf("Box2D version %d.%d.%d\n", version.major, version.minor, version.revision);Vector Math
Box2D includes a small vector math library including types b2Vec2, b2Rot, b2Transform, and b2AABB. This has been
designed to suit the internal needs of Box2D and the interface. All the
members are exposed, so you may use them freely in your application.
The math library is kept simple to make Box2D easy to port and maintain.
Multithreading {#multi}
Box2D has been highly optimized for multithreading. Multithreading is not required and by default Box2D will run single-threaded. If performance is important for your application, you should consider using the multithreading interface.
Box2D has a built-in task scheduler that creates threads. You can optionally connect your own task scheduler if you want more control.
Multithreading is established for each Box2D world you create and must be hooked up to
the world definition. See b2TaskCallback(), b2EnqueueTaskCallback(), and b2FinishTaskCallback() for more details. Also see b2WorldDef::workerCount, b2WorldDef::enqueueTask, and b2WorldDef::finishTask.
The multithreading design for Box2D is focused on data parallelism. The idea is to use multiple cores to complete the world simulation as fast as possible. Box2D multithreading is not designed for task parallelism. Often in games you may have a render thread and an audio thread that do work in isolation from the main thread. Those are examples of task parallelism.
So when you design your game loop, you should let Box2D go wide and use multiple cores to finish its work quickly, without other threads trying to interact with the Box2D world.
In a multithreaded environment you must be careful to avoid race conditions. Modifying the world while it is simulating will lead to unpredictable behavior and this is never safe. It is also not safe to read data from a Box2D world while it is simulating. Box2D may move data structures to improve cache performance. So it is very likely that you will read garbage data.
Caution:
Do not perform read or write operations on a Box2D world duringb2World_Step()
Caution:
Do not write to the Box2D world from multiple threads
It is safe to do ray-casts, shape-casts, and overlap tests from multiple threads outside of b2World_Step(). Generally, any read-only operation is safe to do multithreaded outside of b2World_Step(). This can be very useful if you have multithreaded game logic.
Multithreading Multiple Worlds
Some applications may wish to create multiple Box2D worlds and simulate them on different threads. This works fine because Box2D has very limited use of globals.
There are a few caveats:
- You will get a race condition if you create or destroy Box2D worlds from multiple threads. You should use a mutex to guard this.
- If you will simulate multiple Box2D worlds simultaneously, then they should probably not use a task system. Otherwise you're likely to get preemption.
- Any callbacks you hook up to Box2D must be thread-safe, such as memory allocators.
- All of the limitations for single world simulation still apply.
File: docs/hello.md
Hello Box2D {#hello}
In the distribution of Box2D is a Hello World unit test written in C. The test
creates a large ground box and a small dynamic box. This code does not
contain any graphics. All you will see is text output in the console of
the box's position over time.
This is a good example of how to get up and running with Box2D.
Creating a World
Every Box2D program begins with the creation of a world object.
The world is the physics hub that manages memory, objects, and simulation.
The world is represented by an opaque handle called b2WorldId.
It is easy to create a Box2D world. First, I create the world definition:
b2WorldDef worldDef = b2DefaultWorldDef();The world definition is a temporary object that you can create on the stack. The functionb2DefaultWorldDef() populates the world definition with default values. This is necessary because C does not have constructors and zero initialization is not appropriate for b2WorldDef.
Now I configure the world gravity vector. Note that Box2D has no concept of up and you may point gravity in any direction you like. Box2D example code uses the positive y-axis as the up direction.
worldDef.gravity = (b2Vec2){0.0f, -10.0f};Now I create the world object.
b2WorldId worldId = b2CreateWorld(&worldDef);The world creation copies all the data it needs out of the world definition, so the world
definition is no longer needed.
So now we have our physics world, let's start adding some stuff to it.
Creating a Ground Box
Bodies are built using the following steps:
- Define a body with position, damping, etc.
- Use the world id to create the body.
- Define shapes with friction, density, etc.
- Create shapes on the body.
For step 1 I create the ground body. For this I need a body
definition. With the body definition I specify the initial position of
the ground body.
b2BodyDef groundBodyDef = b2DefaultBodyDef();
groundBodyDef.position = (b2Vec2){0.0f, -10.0f};For step 2 the body definition and the world id are used to create
the ground body. Again, the definition is fully copied and may leave scope after
the body is created. Bodies are static by default. Static bodies don't collide
with other static bodies and are immovable by the simulation.
b2BodyId groundId = b2CreateBody(worldId, &groundBodyDef);Notice that worldId is passed by value. Ids are small structures that should
be passed by value.
For step 3 I create a ground polygon. I use the b2MakeBox() helper function to
form the ground polygon into a box shape, with the box centered on the
origin of the parent body.
b2Polygon groundBox = b2MakeBox(50.0f, 10.0f);The b2MakeBox() function takes the half-width and
half-height (extents). So in this case the ground box is 100
units wide (x-axis) and 20 units tall (y-axis). Box2D is tuned for
meters, kilograms, and seconds. So you can consider the extents to be in
meters. Box2D generally works best when objects are the size of typical
real world objects. For example, a barrel is about 1 meter tall. Due to
the limitations of floating point arithmetic, using Box2D to model the
movement of glaciers or dust particles might not work well.
I'll finish the ground body in step 4 by creating the shape. For this step
I need to create a shape definition which works fine with the default value.
b2ShapeDef groundShapeDef = b2DefaultShapeDef();
b2CreatePolygonShape(groundId, &groundShapeDef, &groundBox);Box2D does not keep a reference to the shape data. It copies the data into the internal
data structures.
Note that every shape must have a parent body, even shapes that are
static. You may attach multiple shapes to a single parent body.
When you attach a shape, the shape's
coordinates become local to the body. So when the body moves, so does
the shape. A shape's world transform is inherited from the parent
body. A shape does not have a transform independent of the body. So we
don't move a shape around on the body. Moving or modifying a shape that
is on a body is possible with certain functions, but it should not be part
of normal simulation. The reason is simple: a body with
morphing shapes is not a rigid body, but Box2D is a rigid body engine.
Many of the algorithms in Box2D are based on the rigid body model and optimized with
that in mind. If this is violated you may get unexpected behavior.
Creating a Dynamic Body
I can use the same technique to create a
dynamic body. The main difference, besides dimensions, is that I must
establish the dynamic body's mass properties.
First I create the body using CreateBody. By default bodies are static,
so I should set the b2BodyType at creation time to make the body
dynamic. I should also use the body definition to put the body at the
intended position for simulation. Creating a body then moving it afterwards is
very inefficient and may cause lag spikes, especially if many bodies are created at
the origin.
b2BodyDef bodyDef = b2DefaultBodyDef();
bodyDef.type = b2_dynamicBody;
bodyDef.position = (b2Vec2){0.0f, 4.0f};
b2BodyId bodyId = b2CreateBody(worldId, &bodyDef);Caution:
You must set the body type tob2_dynamicBodyif you want the body to
move in response to forces (such as gravity).
Next I create and attach a polygon shape using a shape definition.
First I create another box shape:
b2Polygon dynamicBox = b2MakeBox(1.0f, 1.0f);Next I create a shape definition for the box. Notice that I set
density to 1. The default density is 1, so this is unnecessary. Also,
the friction on the surface material is set to 0.3.
b2ShapeDef shapeDef = b2DefaultShapeDef();
shapeDef.density = 1.0f;
shapeDef.material.friction = 0.3f;Caution:
A dynamic body should have at least one shape with a non-zero density.
Otherwise you will get strange behavior.
Using the shape definition I can now create the shape. This
automatically updates the mass of the body. You can add as many shapes
as you like to a body. Each one contributes to the total mass.
b2CreatePolygonShape(bodyId, &shapeDef, &dynamicBox);That's it for initialization. We are now ready to begin simulating.
Simulating the World
I have initialized the ground box and a dynamic box. Now we are
ready to set Newton loose to do his thing. I just have a couple more
issues to consider.
Box2D uses a computational algorithm called an integrator. Integrators
simulate the physics equations at discrete points of time. This goes
along with the traditional game loop where we essentially have a flip
book of movement on the screen. So we need to pick a time step for
Box2D. Generally physics engines for games like a time step at least as
fast as 60Hz or 1/60 seconds. You can get away with larger time steps,
but you will have to be more careful about setting up your simulation.
It is also not good for the time step to vary from frame to frame. A
variable time step produces variable results, which makes it difficult
to debug. So don't tie the time step to your frame rate. Without further ado,
here is the time step.
float timeStep = 1.0f / 60.0f;In addition to the integrator, Box2D also uses a larger bit of code
called a constraint solver. The constraint solver solves all the
constraints in the simulation, one at a time. A single constraint can be
solved perfectly. However, when Box2D solves one constraint, it slightly
disrupts other constraints. To get a good solution, Box2D needs to iterate
over all constraints a number of times.
Box2D uses sub-stepping as a means of constraint iteration. It lets the
simulation move forward in time by small amounts and each constraint
gets a chance to react to the changes.
The suggested sub-step count for Box2D is 4. You can tune this number
to your liking, just keep in mind that this has a trade-off between
performance and accuracy. Using fewer sub-steps increases performance
but accuracy suffers. Likewise, using
more sub-steps decreases performance but improves the quality of your
simulation. For this example, I will use 4 sub-steps.
int subStepCount = 4;Note that the time step and the sub-step count are related. As the time step
decreases, the size of the sub-steps also decreases. For example, at 60Hz
time step and 4 sub-steps, the sub-steps operate at 240Hz. With 8 sub-steps
the sub-step is 480Hz.
We are now ready to begin the simulation loop. In your game the
simulation loop can be merged with your game loop. In each pass through
your game loop you call b2World_Step(). Just one call is usually enough,
depending on your frame rate and your physics time step. I recommend this article
Fix Your Timestep! to run
your game simulation at a fixed rate.
The Hello World test was designed to be simple, so it has no
graphical output. The code prints out the position and rotation of the
dynamic body. Here is the simulation loop that simulates 90 time steps
for a total of 1.5 seconds of simulated time.
for (int i = 0; i < 90; ++i)
{
b2World_Step(worldId, timeStep, subStepCount);
b2Vec2 position = b2Body_GetPosition(bodyId);
b2Rot rotation = b2Body_GetRotation(bodyId);
printf("%4.2f %4.2f %4.2f\n", position.x, position.y, b2Rot_GetAngle(rotation));
}Notice that the rotation of the body is returned in a b2Rot struct (short for rotation). This
struct holds the rotation in a format that is fast for simulation. You may use b2Rot_GetAngle
to get the rotation in radians.
The output shows the box falling and landing on the ground box. Your
output should look like this:
0.00 4.00 0.00
0.00 3.99 0.00
0.00 3.98 0.00
...
0.00 1.25 0.00
0.00 1.13 0.00
0.00 1.01 0.00Cleanup
When you are done with the simulation, you should destroy the world.
b2DestroyWorld(worldId);This efficiently destroys all bodies, shapes, and joints in the simulation.
File: docs/loose_ends.md
Loose Ends
User Data
Bodies, shapes, and joints allow you to attach user data
as a void*. This is handy when you are examining Box2D data
structures and you want to determine how they relate to the objects in
your game engine.
For example, it is typical to attach an entity pointer to the rigid body
on that entity. This sets up a circular reference. If you have the entity,
you can get the body. If you have the body, you can get the entity.
GameEntity* entity = GameCreateEntity();
b2BodyDef bodyDef = b2DefaultBodyDef();
bodyDef.userData = entity;
entity->bodyId = b2CreateBody(myWorldId, &bodyDef);Here are some examples of cases where you would need the user data:
- Applying damage to an entity using a collision result.
- Playing a scripted event if the player is inside an axis-aligned box.
- Accessing a game structure when Box2D notifies you that a joint is
going to be destroyed.
Keep in mind that user data is optional and you can put anything in it.
However, you should be consistent. For example, if you want to store an
entity pointer on one body, you should keep an entity pointer on all
bodies. Don't store a GameEntity pointer on one body, and a ParticleSystem
pointer on another body. Casting a GameEntity to a ParticleSystem pointer
may lead to a crash.
Pixels and Coordinate Systems
I recommend using MKS (meters, kilograms, and seconds) units and
radians for angles. You may have trouble working with meters because
your game is expressed in terms of pixels. To deal with this in the
sample I have the whole game world in meters and just use an OpenGL
viewport transformation to scale the world into screen space.
You use code like this to scale your graphics.
float lowerX = -25.0f, upperX = 25.0f, lowerY = -5.0f, upperY = 25.0f;
gluOrtho2D(lowerX, upperX, lowerY, upperY);If your game must work in pixel units then you could convert your
length units from pixels to meters when passing values from Box2D.
Likewise you should convert the values received from Box2D from meters
to pixels. This will improve the stability of the physics simulation.
You have to come up with a reasonable conversion factor. I suggest
making this choice based on the size of your characters. Suppose you
have determined to use 50 pixels per meter (because your character is 75
pixels tall). Then you can convert from pixels to meters using these
formulas:
xMeters = 0.02f * xPixels;
yMeters = 0.02f * yPixels;In reverse:
xPixels = 50.0f * xMeters;
yPixels = 50.0f * yMeters;You should consider using MKS units in your game code and just convert
to pixels when you render. This will simplify your game logic and reduce
the chance for errors since the rendering conversion can be isolated to
a small amount of code.
If you use a conversion factor, you should try tweaking it globally to
make sure nothing breaks. You can also try adjusting it to improve
stability.
If this conversion is not possible, you can set the length units used
by Box2D using b2SetLengthUnitsPerMeter(). This is experimental and not
well tested.
Debug Drawing
You can implement the function pointers in b2DebugDraw struct to get detailed
drawing of the Box2D world. Debug draw provides:
- shapes
- joints
- broad-phase axis-aligned bounding boxes (AABBs)
- center of mass
- contact points
This is the preferred method of drawing the Box2D simulation, rather
than accessing the data directly. The reason is that much of the
necessary data is internal and subject to change.
The samples application draws the Box2D world using the b2DebugDraw.
Limitations
Box2D uses several approximations to simulate rigid body physics
efficiently. This brings some limitations.
Here are the current limitations:
- Extreme mass ratios may cause joint stretching and collision overlap.
- Box2D uses soft constraints to improve robustness. This can lead to joint and contact flexing.
- Continuous collision does not handle all situations. For example, general dynamic versus dynamic continuous collision is not handled. Bullets handle this in a limited way. This is done for performance reasons.
- Continuous collision does not handle joints. So you may see joint stretching on fast moving objects. Usually the joints recover after a few time steps.
- Box2D uses the semi-implicit Euler method to solve the equations of motion. It does not reproduce exactly the parabolic motion of projectiles and has only first-order accuracy. However it is fast and has good stability.
- Box2D uses the Gauss-Seidel method to solve constraints and achieve real-time performance. You will not get precisely rigid collisions or pixel perfect accuracy. Increasing the sub-step count will improve accuracy.
File: docs/migration.md
Migration Guide
Caution:
This guide only covers the transition from 2.4 to 3.0. Please see the release notes for future version changes.
Version 2.4 to Version 3.0
Box2D version 3.0 is a full rewrite. You can read some background information here.
Here are the highlights that affect the API:
- moved from C++ to C
- identifiers (handles) instead of pointers
- multithreading support
- fewer callbacks
- more features (such as capsules and shape casts)
- new sub-stepping solver (Soft Step)
- gear and pulley joint removed (temporarily)
However, the scope of what Box2D does has not changed much. It is still a 2D rigid body engine. It is just faster and more robust (hopefully). And hopefully it is easier to work with and port/wrap for other languages/platforms.
I'm going to describe migration by comparing code snippets between 2.4 and 3.0. These should give you and idea of the sort of transformations you need to make to your code to migrate to v3.0. These snippets are written in C and may need some small adjustments to work with C++.
I'm not going to cover all the details of v3.0 in this guide. That is the job of the manual, the doxygen reference, and the samples.
The surface area of the Box2D is smaller in v3.0 because C++ is not good at hiding details. So hopefully you find the new API easier to work with.
Should I upgrade to Version 3?
Since the behavior changed from version 2 to version 3, I recommend to only use version 3 for new projects. Version 2 no longer receives updates, but it is already battle tested. Version 3 is good for projects that need high performance.
Creating a world
Version 2.4:
#include "box2d/box2d.h"
b2Vec2 gravity(0.0f, -10.0f);
b2World world(gravity);Version 3.0:
#include "box2d/box2d.h"
b2Vec2 gravity = {0.0f, -10.0f};
b2WorldDef worldDef = b2DefaultWorldDef();
worldDef.gravity = gravity;
b2WorldId worldId = b2CreateWorld(&worldDef);There is now a required world definition. C does not have constructors, so you need to initialize ALL structures that you pass to Box2D. Box2D provides an initialization helper for almost all structures. For example b2DefaultWorldDef() is used here to initialize b2WorldDef. b2WorldDef provides many options, but the defaults are good enough to get going.
In Version 3.0, Box2D objects are generally hidden and you only have an identifier. This keeps the API small. So when you create a world you just get a b2WorldId which you should treat as an atomic object, like int or float. It is small and should be passed by value.
In Version 3.0 there are also no destructors, so you must destroy the world.
b2DestroyWorld(worldId);
worldId = b2_nullWorldId;This destroys all bodies, shapes, and joints as well. This is quicker than destroying them individually. Just like pointers, it is good practice to nullify identifiers. Box2D provides null values for all identifiers and also macros such as B2_IS_NULL to test if an identifier is null.
Creating a body
Version 2.4:
b2BodyDef bodyDef;
bodyDef.type = b2_dynamicBody;
bodyDef.position.Set(0.0f, 4.0f);
b2Body* body = world.CreateBody(&bodyDef);Version 3.0:
b2BodyDef bodyDef = b2DefaultBodyDef();
bodyDef.type = b2_dynamicBody;
bodyDef.position = (b2Vec2){0.0f, 4.0f};
b2BodyId bodyId = b2CreateBody(worldId, &bodyDef);Body creation is very similar in v3.0. In this case there is a definition initialization function b2DefaultBodyDef(). This can help save a bit of typing in some cases. In v3.0 I recommend getting comfortable with curly brace initialization for initializing vectors. There are no member functions in C. Notice that the body is created using a loose function and providing the b2WorldId as an argument. Basically what you would expect going from C++ to C.
Destroying a body is also similar.
Version 2.4:
world.DestroyBody(body);
body = nullptr;Version 3.0:
b2DestroyBody(bodyId);
bodyId = b2_nullBodyId;Notice there is a little magic here in Version 3.0. b2BodyId knows what world it comes from. So you do not need to provide worldId when destroying the body. Version 3.0 supports up to 128 worlds. This may be increased or be overridden in the future.
Shapes and joints are still destroyed automatically. However, b2DestructionListener is gone. This holds to the theme of fewer callbacks. However, you can now useb2Shape_IsValid() and b2Joint_IsValid().
Creating a shape
Shape creation has been streamlined in Version 3.0. b2Fixture is gone. I feel like it was a confusing concept so I hope you don't miss it.
Version 2.4:
b2PolygonShape box;
box.SetAsBox(1.0f, 1.0f);
b2FixtureDef fixtureDef;
fixtureDef.shape = &box;
fixtureDef.density = 1.0f;
fixtureDef.friction = 0.3f;
b2Fixture* fixture = body->CreateFixture(&fixtureDef);Version 3.0:
b2Polygon box = b2MakeBox(1.0f, 1.0f);
b2ShapeDef shapeDef = b2DefaultShapeDef();
shapeDef.density = 1.0f;
shapeDef.friction = 0.3f;
b2ShapeId shapeId = b2CreatePolygonShape(bodyId, &shapeDef, &box);So basically v2.4 shapes are no longer shapes, they are primitives or geometry with no inheritance (of course). This freed the term shape to be used where fixture was used before. In v3.0 the shape definition is generic and there are different functions for creating each shape type, such as b2CreateCircleShape or b2CreateSegmentShape.
Again notice the structure initialization with b2DefaultShapeDef(). Unfortunately we cannot have meaningful definitions with zero initialization. You must initialize your structures.
Another important change for shapes is that the default density in the shape definition is now 1 instead of 0. Static and kinematic bodies will ignore the density. You can now make an entire game without touching the density.
Destroying shapes is straight forward.
Version 2.4:
body->DestroyFixture(fixture);
fixture = nullptr;Version 3.0:
b2DestroyShape(shapeId);
shapeId = b2_nullShapeId;Chains
In Version 2.4 chains are a type of shape. In Version 3.0 they are a separate concept. This leads to significant simplifications internally. In Version 2.4 all shapes had to support the notion of child shapes. This is gone.
Version 2.4:
b2Vec2 points[5];
points[0].Set(-8.0f, 6.0f);
points[1].Set(-8.0f, 20.0f);
points[2].Set(8.0f, 20.0f);
points[3].Set(8.0f, 6.0f);
points[4].Set(0.0f, -2.0f);
b2ChainShape chain;
chain.CreateLoop(points, 5);
b2FixtureDef fixtureDef;
fixtureDef.shape = &chain;
b2Fixture* chainFixture = body->CreateFixture(&fixtureDef);Version 3.0:
b2Vec2 points[5] = {
{-8.0f, 6.0f},
{-8.0f, 20.0f},
{8.0f, 20.0f},
{8.0f, 6.0f},
{0.0f, -2.0f}
};
b2ChainDef chainDef = b2DefaultChainDef();
chainDef.points = points;
chainDef.count = 5;
chainDef.loop = true;
b2ChainId chainId = b2CreateChain(bodyId, &chainDef);Since chains are their own concept now, they get their own identifier, b2ChainId. You can view chains as macro objects, they create many b2ChainSegment shapes internally. Normally you don't interact with these. However they are returned from queries. You can use b2Shape_GetParentChain() to get the b2ChainId for a chain segment that you get from a query.
DO NOT destroy or modify a
b2ChainSegmentthat belongs to a chain shape directly
Creating a joint
Joints are very similar in v3.0. The lack of C member functions changes initialization.
Version 2.4:
b2RevoluteJointDef jointDef;
jointDef.Initialize(ground, body, b2Vec2(-10.0f, 20.5f));
jointDef.motorSpeed = 1.0f;
jointDef.maxMotorTorque = 100.0f;
jointDef.enableMotor = true;
jointDef.lowerAngle = -0.25f * b2_pi;
jointDef.upperAngle = 0.5f * b2_pi;
jointDef.enableLimit = true;:
b2RevolutionJoint* joint = (b2RevoluteJoint*)world->CreateJoint(&jointDef);Version 3.0:
b2Vec2 pivot = {-10.0f, 20.5f};
b2RevoluteJointDef jointDef = b2DefaultRevoluteJointDef();
jointDef.bodyIdA = groundId;
jointDef.bodyIdB = bodyId;
jointDef.localAnchorA = b2Body_GetLocalPoint(jointDef.bodyIdA, pivot);
jointDef.localAnchorB = b2Body_GetLocalPoint(jointDef.bodyIdB, pivot);
jointDef.motorSpeed = 1.0f;
jointDef.maxMotorTorque = 100.0f;
jointDef.enableMotor = true;
jointDef.lowerAngle = -0.25f * b2_pi;
jointDef.upperAngle = 0.5f * b2_pi;
jointDef.enableLimit = true;
b2JointId jointId = b2CreateRevoluteJoint(worldId, &jointDef);Some of the joints have more options now. Check the code comments and samples for details.
The friction joint has been removed since it is a subset of the motor joint.
The pulley and gear joints have been removed. I'm not satisfied with how they work in 2.4 and plan to implement improved versions in the future.
New solver
There is a new solver that uses sub-stepping called Soft Step. Instead of specifying velocity iterations or position iterations, you now specify the number of sub-steps.
void b2World_Step(b2WorldId worldId, float timeStep, int32_t subStepCount);It is recommended to start with 4 sub-steps and adjust as needed. The sub-stepping only computes contact points once per full time step, so contact events are for the full time step.
With a sub-stepping solver you need to think differently about how you interact with bodies. Externally applied impulses or velocity adjustments no longer exist after the first sub-step. So if you try to control the movement of a body by setting the velocity every time step then you may get unexpected results. You will get more predictable results by applying a force and/or torque. Forces and torques are spread across all time steps.
If you want full control over the movement of a body, considering setting the body type to b2_kinematicBody. Preferably this is done in the b2BodyDef:
b2BodyDef bodyDef = b2DefaultBodyDef();
bodyDef.type = b2_kinematicBody;Contact data
In v2.4 b2ContactListener provided BeginContact, EndContact, PreSolve, and PostSolve. You could also iterate over the contacts associated with a body using b2Body::GetContactList. The latter was rarely used due to how continuous collision worked in v2.4 meant that you could miss some contacts using GetContactList.
In v3.0 there is a strong emphasis on multithreading. Callbacks in multithreading are problematic for a few reasons:
- chance of race conditions in user code
- user code becomes non-deterministic
- uncertain performance impact
Therefore all callbacks except PreSolve have been removed. Instead you can now access all events and contact data after the time step. Version 3.0 no longer uses collision sub-stepping for continuous collision. This means all contacts data are valid at the end of the time step. Just keep in mind that Box2D computes contact points at the beginning of the time step, so the contact points apply to the previous position of the body.
Here is how you access contact data in v3.0:
b2ContactEvents contactEvents = b2World_GetContactEvents(worldId);The contact events structure has begin and end events:
typedef struct b2ContactEvents
{
b2ContactBeginTouchEvent* beginEvents;
b2ContactEndTouchEvent* endEvents;
b2ContactHitEvent* hitEvents;
int beginCount;
int endCount;
int hitCount;
} b2ContactEvents;You can loop through these events after the time step. These events are in deterministic order, even with multithreading. See the sample_events.cpp file for examples.
You may not want Box2D to save all contact events, so you can disable them for a given shape using enableContactEvents on b2ShapeDef.
If you want to access persistent contacts, you can get the data from bodies or shapes.
b2ContactData contactData[10];
int count = b2Body_GetContactData(bodyId, contactData, 10);b2ContactData contactData[10];
int count = b2Shape_GetContactData(shapeId, contactData, 10);This includes contact data for contacts reported in begin events. This data is also in deterministic order.
Pre-solve contact modification is available using a callback.
typedef bool b2PreSolveFcn(b2ShapeId shapeIdA, b2ShapeId shapeIdB, b2Manifold* manifold, void* context);
void b2World_SetPreSolveCallback(b2WorldId worldId, b2PreSolveFcn* fcn, void* context);You can define a pre-solve callback and register that with the world. You can also provide a context variable that will be passed back to your callback. This is not enough to get a pre-solve callback. You also need to enable it on your shape using enablePreSolveEvents in b2ShapeDef. This is false by default.
Pre-solve callbacks are dangerous. You must avoid race conditions and you must understand that behavior may not be deterministic. This is especially true if you have multiple pre-solve callbacks that are sensitive to order.
Sensors
In v2.4 sensor events were mixed in with contact events. I have split them up to make user code simpler.
b2SensorEvents sensorEvents = b2World_GetSensorEvents(b2WorldId worldId);Note that contact data on bodies and shapes have no information about sensors. That data only has touching contacts.
Sensor events are available to all shapes on dynamic bodies except chains. You can disable them using enableSensorEvents on b2ShapeDef.
Queries
Version 2.4 has b2World::QueryAABB and b2World::RayCast. This functionality is largely the same in v3.0, but more features have been added such as precise overlap tests and shape casts.
Another new feature is b2QueryFilter which allows you to filter raycast results before they reach your callback.
This query filter is tested against b2Filter on shapes that the query encounters.
Ray casts now take an origin and translation rather than start and end points. This convention works better with the added shape cast functions.
World iteration
Iterating over all bodies/shapes/joints/contacts in a world is very inefficient and has been removed from Version 3.0. Instead, you should be using b2BodyEvents and b2ContactEvents. Events are efficient and data-oriented.
Library configuration
Version 3.0 offers more library configuration. You can override the allocator and you can intercept assertions by registering global callbacks. These are for expert users and they must be thread safe.
void b2SetAllocator(b2AllocFcn* allocFcn, b2FreeFcn* freeFcn);
void b2SetAssertFcn(b2AssertFcn* assertFcn);File: docs/overview.md
Overview
Box2D is a 2D rigid body simulation library for games. Programmers can
use it in their games to make objects move in realistic ways and make
the game world more interactive. From the game engine's point of view,
a physics engine is a system for procedural animation.
Box2D also provides many collision routines that can be used even when
rigid body simulation is not used. There are functions for overlap and
cast queries. There is also a bounding volume hierarchy (dynamic tree)
that can be used for game specific spatial sorting needs.
Box2D is written in portable C17. Most of the types defined in the
engine begin with the b2 prefix. Hopefully this is sufficient to avoid
name clashing with your application.
Prerequisites
In this manual I'll assume you are familiar with basic physics
concepts, such as mass, force, torque, and impulses. If not, please
first consult Google search and Wikipedia.
Box2D was created as part of a physics tutorial at the Game Developer
Conference. You can get these tutorials from the publications section of
box2d.org.
Since Box2D is written in C, you are expected to be experienced in C
programming. Box2D should not be your first C programming project. You
should be comfortable with compiling, linking, and debugging.
Caution:
Box2D should not be your first C project. Please learn C
programming, compiling, linking, and debugging before working with
Box2D. There are many resources for this online.
Scope
This manual covers the majority of the Box2D API. However, not every
aspect is covered. Please look at the Reference section and samples
application included with Box2D to learn more.
This manual is only updated with new releases. The latest version of
Box2D may be out of sync with this manual.
Caution:
This manual applies to the associated release and not necessarily the
latest version on the main branch.
Feedback and Bugs
Please file bugs and feature requests here:
Box2D Issues
You can help to ensure your issue gets fixed if you provide sufficient
detail. A testbed example that reproduces the problem is ideal. You can
read about the testbed later in this document.
There is also a Discord server and a
subreddit for Box2D. You may also use
GitHub Discussions.
Core Concepts
Box2D works with several fundamental concepts and objects. I briefly
define these objects here and more details are given later in this
document.
rigid body
A chunk of matter that is so strong that the distance between any two
bits of matter on the chunk is constant. They are hard like a diamond.
In the following discussion I use body interchangeably with rigid body.
shape
A shape binds collision geometry to a body and adds material properties such as
density, friction, and restitution. A shape puts collision geometry into the
collision system (broad-phase) so that it can collide with other shapes.
constraint
A constraint is a physical connection that removes degrees of freedom
from bodies. A 2D body has 3 degrees of freedom (two translation
coordinates and one rotation coordinate). If I take a body and pin it
to the wall (like a pendulum) I have constrained the body to the wall.
At this point the body can only rotate about the pin, so the constraint
has removed 2 degrees of freedom.
contact constraint
A special constraint designed to prevent penetration of rigid bodies and
to simulate friction and restitution. You do not create contact
constraints; they are created automatically by Box2D.
joint constraint
This is a constraint used to hold two or more bodies together. Box2D
supports several joint types: revolute, prismatic, distance, and more.
Joints may have limits, motors, and/or springs.
joint limit
A joint limit restricts the range of motion of a joint. For example, the
human elbow only allows a certain range of angles.
joint motor
A joint motor drives the motion of the connected bodies according to the
joint's degrees of freedom. For example, you can use a motor to drive
the rotation of an elbow. Motors have a target speed and a maximum force
or torque. The simulation will apply the force or torque required to
achieve the desired speed.
joint spring
A joint spring has a stiffness and damping. In Box2D spring stiffness is
expressed in terms or Hertz or cycles per second. This lets you configure how
quickly a spring reacts regardless of the body masses. Joint springs also
have a damping ratio to let you specify how quickly the spring will come to
rest.
world
A physics world is a collection of bodies, shapes, joints, and contacts
that interact together. Box2D supports the creation of multiple worlds which
are completely independent.
solver
The physics world has a solver that is used to advance time and to
resolve contact and joint constraints. The Box2D solver is a high
performance sequential solver that operates in order N time, where N is
the number of constraints.
continuous collision
The solver advances bodies in time using discrete time steps. Without
intervention this can lead to tunneling.
Box2D contains specialized algorithms to deal with tunneling. First, the
collision algorithms can interpolate the motion of two bodies to find
the first time of impact (TOI). Second, speculative collision is used to create
contact constraints between bodies before they touch.
events
World simulation leads to the creation of events that are available at the end
of the time step:
- body movement events
- contact begin and end events
- sensor begin and end events
- contact hit events
These events allow your application to react to changes in the simulation.
Modules
Box2D's primary purpose is to provide rigid body simulation. However,
there are math and collision features that may be useful apart from the
rigid body simulation. These are provided in the include directory. Anything
in the include directory is considered public, while everything in the src
directory is consider internal.
Public features are supported and you can get help with these on the Discord
server. Using internal code directly is not supported. However, feel free to
study the code and ask questions. I'm happy to share all the details of how
Box2D works internally.
Units
Box2D works with floating point numbers and tolerances have to be used
to make Box2D perform well. These tolerances have been tuned to work
well with meters-kilogram-second (MKS) units. In particular, Box2D has
been tuned to work well with moving shapes between 0.1 and 10 meters. So
this means objects between soup cans and buses in size should work well.
Static shapes may be up to 50 meters long without trouble. If you have a
large world, you should split it up into multiple static bodies. This will
improve precision and simulation behavior.
Being a 2D physics engine, it is tempting to use pixels as your units.
Unfortunately this will lead to a poor simulation and possibly weird
behavior. An object of length 200 pixels would be seen by Box2D as the
size of a 45 story building.
Caution:
Box2D is tuned for MKS units. Keep the size of moving objects larger than 1cm.
You'll need to use some scaling system when
you render your environment and actors. The Box2D samples application
does this by using an OpenGL viewport transform. Do not use pixel units
unless you understand the implications.
It is best to think of Box2D bodies as moving billboards upon which you
attach your artwork. The billboard may move in a unit system of meters,
but you can convert that to pixel coordinates with a simple scaling
factor. You can then use those pixel coordinates to place your sprites,
etc. You can also account for flipped coordinate axes.
Another limitation to consider is overall world size. If your world units
become larger than 12 kilometers or so, then the lost precision can affect
stability.
Caution:
Box2D works best with world sizes less than 12 kilometers. If you are
careful with your simulation tuning, this can be pushed up to around 24
kilometers, which is much larger than most game worlds.
Box2D uses radians for angles. The body rotation is stored a complex number,
so when you access the angle of a body, it will be between \f$-\pi\f$ and \f$\pi\f$ radians.
Caution:
Box2D uses radians, not degrees.
Changing the length units
Advanced users may change the length unit by calling b2SetLengthUnitsPerMeter()
at application startup. If you keep Box2D in a shared library, you will need
to call this if the shared library is reloaded.
If you change the length units to pixels you will need to decide how many pixels
represent a meter. You will also need to figure out reasonable values for gravity,
density, force, and torque. One of the benefits of using MKS units for physics
simulation is that you can use real world values to get reasonable results.
It is also harder to get support for using Box2D if you change the unit
system, because values are harder to communicate and may become non-intuitive.
Ids and Definitions
Fast memory management plays a central role in the design of the Box2D
interface. When you create a world, body, shape or joint, you will receive
a handle called an id. These ids are opaque and are passed to various functions
to access the underlying data.
These ids provide some safety. If you use an id after it has been freed you will
usually get an assertion. All ids support 64k generations of safety. All ids
also have a corresponding function you can call to check if it is valid.
When you create a world, body, shape, or joint, you need to provide a definition structure.
These definitions contain all the information needed to build the Box2D object. By using
this approach I can prevent construction errors, keep the number of function parameters
small, provide sensible defaults, and reduce the number of accessors.
Here is an example of body creation:
b2BodyDef bodyDef = b2DefaultBodyDef();
bodyDef.position = (b2Vec2){10.0f, 5.0f};
b2BodyId myBodyId = b2CreateBody(myWorldId, &bodyDef);Notice the body definition is initialized by calling b2DefaultBodyDef(). This is needed
because C does not have constructors and zero initialization is generally not suitable for the definitions used in Box2D.
Also notice that the body definition is a temporary object that is fully copied into the internal
body data structures. Definitions should usually be created on the stack as temporaries.
This is how a body is destroyed:
b2DestroyBody(myBodyId);
myBodyId = b2_nullBodyId;Notice that the body id is set to null using the constant b2_nullBodyId. You should treat
ids as opaque data, however you may zero initialize all Box2D ids and they will be considered
null.
Shapes are created in a similar way. For example, here is how a box shape is created:
b2ShapeDef shapeDef = b2DefaultShapeDef();
shapeDef.material.friction = 0.42f;
b2Polygon box = b2MakeBox(0.5f, 0.25f);
b2ShapeId myShapeId = b2CreatePolygonShape(myBodyId, &shapeDef, &box);And the shape may be destroyed as follows:
b2DestroyShape(myShapeId);
myShapeId = b2_nullShapeId;For convenience, Box2D will destroy all shapes on a body when the body is destroyed. You don't need to store the shape id.
There are some macros to assist using ids in logical operations.
bool isNull = B2_IS_NULL(myBodyId);
bool isNonNull = B2_IS_NON_NULL(myJointId);
bool areEqual = B2_ID_EQUALS(myShapeIdA, myShapeIdB);File: docs/reading.md
Further Reading
- Erin Catto's Publications
- Erin Catto's Blog Posts
- Collision Detection in Interactive 3D Environments, Gino van den Bergen, 2004
- Real-Time Collision Detection, Christer Ericson, 2005