Box2D 3.1.0
A 2D physics engine for games
Loading...
Searching...
No Matches
Migration Guide

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.

Creating a world

Version 2.4:

#include "box2d/box2d.h"
b2Vec2 gravity(0.0f, -10.0f);
b2World world(gravity);
2D vector This can be used to represent a point or free vector
Definition math_functions.h:24

Version 3.0:

#include "box2d/box2d.h"
b2Vec2 gravity = {0.0f, -10.0f};
worldDef.gravity = gravity;
b2WorldId worldId = b2CreateWorld(&worldDef);
World id references a world instance. This should be treated as an opaque handle.
Definition id.h:38
b2Vec2 gravity
Gravity vector. Box2D has no up-vector defined.
Definition types.h:67
b2WorldId b2CreateWorld(const b2WorldDef *def)
Create a world for rigid body simulation.
b2WorldDef b2DefaultWorldDef(void)
Use this to initialize your world definition.
World definition used to create a simulation world.
Definition types.h:65

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;
void b2DestroyWorld(b2WorldId worldId)
Destroy a world.

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);
b2Vec2 position
The initial world position of the body.
Definition types.h:153
b2BodyType type
The body type: static, kinematic, or dynamic.
Definition types.h:148
@ b2_dynamicBody
positive mass, velocity determined by forces, moved by solver
Definition types.h:134
A body definition holds all the data needed to construct a rigid body.
Definition types.h:146

Version 3.0:

bodyDef.type = b2_dynamicBody;
bodyDef.position = (b2Vec2){0.0f, 4.0f};
b2BodyId bodyId = b2CreateBody(worldId, &bodyDef);
b2BodyDef b2DefaultBodyDef(void)
Use this to initialize your body definition.
b2BodyId b2CreateBody(b2WorldId worldId, const b2BodyDef *def)
Create a rigid body given a definition.
Body id references a body instance. This should be treated as an opaque handle.
Definition id.h:45

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;
void b2DestroyBody(b2BodyId bodyId)
Destroy a rigid body given an id.

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 use b2Shape_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);
shapeDef.density = 1.0f;
shapeDef.friction = 0.3f;
b2ShapeId shapeId = b2CreatePolygonShape(bodyId, &shapeDef, &box);
b2Polygon b2MakeBox(float hx, float hy)
Make a box (rectangle) polygon, bypassing the need for a convex hull.
A solid convex polygon.
Definition collision.h:129
Shape id references a shape instance. This should be treated as an opaque handle.
Definition id.h:53
float friction
The Coulomb (dry) friction coefficient, usually in the range [0,1].
Definition types.h:313
float density
The density, usually in kg/m^2.
Definition types.h:319
b2ShapeId b2CreatePolygonShape(b2BodyId bodyId, const b2ShapeDef *def, const b2Polygon *polygon)
Create a polygon shape and attach it to a body.
b2ShapeDef b2DefaultShapeDef(void)
Use this to initialize your shape definition.
Used to create a shape.
Definition types.h:308

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;
void b2DestroyShape(b2ShapeId shapeId)
Destroy a shape.

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}
};
chainDef.points = points;
chainDef.count = 5;
chainDef.loop = true;
b2ChainId chainId = b2CreateChain(bodyId, &chainDef);
Chain id references a chain instances. This should be treated as an opaque handle.
Definition id.h:69
const b2Vec2 * points
An array of at least 4 points. These are cloned and may be temporary.
Definition types.h:380
int32_t count
The point count, must be 4 or more.
Definition types.h:383
b2ChainDef b2DefaultChainDef(void)
Use this to initialize your chain definition.
b2ChainId b2CreateChain(b2BodyId bodyId, const b2ChainDef *def)
Chain Shape.
Used to create a chain of line segments.
Definition types.h:375

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 b2ChainSegment that 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:

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);
#define b2_pi
https://en.wikipedia.org/wiki/Pi
Definition math_functions.h:19
float lowerAngle
The lower angle for the joint limit in radians.
Definition types.h:724
bool enableLimit
A flag to enable joint limits.
Definition types.h:721
float motorSpeed
The desired motor speed in radians per second.
Definition types.h:736
float upperAngle
The upper angle for the joint limit in radians.
Definition types.h:727
bool enableMotor
A flag to enable the joint motor.
Definition types.h:730
float maxMotorTorque
The maximum motor torque, typically in newton-meters.
Definition types.h:733
Revolute joint definition.
Definition types.h:694

Version 3.0:

b2Vec2 pivot = {-10.0f, 20.5f};
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);
b2Vec2 b2Body_GetLocalPoint(b2BodyId bodyId, b2Vec2 worldPoint)
Get a local point on a body given a world point.
Joint id references a joint instance. This should be treated as an opaque handle.
Definition id.h:61
b2Vec2 localAnchorB
The local anchor point relative to bodyB's origin.
Definition types.h:705
b2Vec2 localAnchorA
The local anchor point relative to bodyA's origin.
Definition types.h:702
b2BodyId bodyIdA
The first attached body.
Definition types.h:696
b2BodyId bodyIdB
The second attached body.
Definition types.h:699
b2RevoluteJointDef b2DefaultRevoluteJointDef(void)
Use this to initialize your joint definition.
b2JointId b2CreateRevoluteJoint(b2WorldId worldId, const b2RevoluteJointDef *def)
Create a revolute joint.

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);
void b2World_Step(b2WorldId worldId, float timeStep, int subStepCount)
Simulate a world for one time step.

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:

@ b2_kinematicBody
zero mass, velocity set by user, moved by solver
Definition types.h:131

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);
Contact events are buffered in the Box2D world and are available as event arrays after the time step ...
Definition types.h:967
b2ContactEvents b2World_GetContactEvents(b2WorldId worldId)
Get contact events for this current time step. The event data is transient. Do not store a reference ...

The contact events structure has begin and end events:

typedef struct b2ContactEvents
{
int endCount;
int hitCount;
b2ContactBeginTouchEvent * beginEvents
Array of begin touch events.
Definition types.h:969
int32_t endCount
Number of end touch events.
Definition types.h:981
b2ContactHitEvent * hitEvents
Array of hit events.
Definition types.h:975
int32_t beginCount
Number of begin touch events.
Definition types.h:978
b2ContactEndTouchEvent * endEvents
Array of end touch events.
Definition types.h:972
int32_t hitCount
Number of hit events.
Definition types.h:984
A begin touch event is generated when two shapes begin touching.
Definition types.h:926
An end touch event is generated when two shapes stop touching.
Definition types.h:936
A hit touch event is generated when two shapes collide with a speed faster than the hit speed thresho...
Definition types.h:946

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);
int b2Body_GetContactData(b2BodyId bodyId, b2ContactData *contactData, int capacity)
Get the touching contact data for a body.
The contact data for two shapes.
Definition types.h:1021
b2ContactData contactData[10];
int count = b2Shape_GetContactData(shapeId, contactData, 10);
int b2Shape_GetContactData(b2ShapeId shapeId, b2ContactData *contactData, int capacity)
Get the touching contact data for a shape. The provided shapeId will be either shapeIdA or shapeIdB o...

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);
A contact manifold describes the contact points between colliding shapes.
Definition collision.h:513
bool b2PreSolveFcn(b2ShapeId shapeIdA, b2ShapeId shapeIdB, b2Manifold *manifold, void *context)
Prototype for a pre-solve callback.
Definition types.h:1055
void b2World_SetPreSolveCallback(b2WorldId worldId, b2PreSolveFcn *fcn, void *context)
Register the pre-solve callback. This is optional.

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.

Sensor events are buffered in the Box2D world and are available as begin/end overlap event arrays aft...
Definition types.h:910
b2SensorEvents b2World_GetSensorEvents(b2WorldId worldId)
Get sensor events for the current time step. The event data is transient. Do not store a reference to...

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);
void * b2AllocFcn(unsigned int size, int alignment)
Prototype for user allocation function.
Definition base.h:49
void b2SetAllocator(b2AllocFcn *allocFcn, b2FreeFcn *freeFcn)
This allows the user to override the allocation functions.
void b2FreeFcn(void *mem)
Prototype for user free function.
Definition base.h:53
void b2SetAssertFcn(b2AssertFcn *assertFcn)
Override the default assert callback.
int b2AssertFcn(const char *condition, const char *fileName, int lineNumber)
Prototype for the user assert callback. Return 0 to skip the debugger break.
Definition base.h:56