Box2D 3.1.0
A 2D physics engine for games
|
Box2D version 3.0 is a full rewrite. You can read some background information here.
Here are the highlights that affect the API:
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.
Version 2.4:
Version 3.0:
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.
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.
Version 2.4:
Version 3.0:
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:
Version 3.0:
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()
.
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:
Version 3.0:
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:
Version 3.0:
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:
Version 3.0:
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
Joints are very similar in v3.0. The lack of C member functions changes initialization.
Version 2.4:
Version 3.0:
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.
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.
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
:
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:
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:
The contact events structure has begin and end events:
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.
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.
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.
In v2.4 sensor events were mixed in with contact events. I have split them up to make user code simpler.
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
.
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.
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.
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.