Skip to content
July 1, 2011 / Aurélien Ribon

Box2D Tutorial: Collision filtering

Hello,

Today I want to make a tutorial about collision filtering in the Box2D engine, because it is something that is not that easy to master, and yet it is a very powerful and useful feature.

Note: examples and source code are related to the LibGDX (Android) implementation of the Box2D API, but the concepts are the same for any other implementation, only the syntax may differ.

There are two ways to deal with collision filtering: by using categories and masks, or by using groups. These parameters can be found in the Filter component of a Fixture. Filtering collisions with any of these attributes will assure you that collision checks between two bodies that should not collide with each other will not be evaluated at all, resulting in a gain of speed.

Illustration example

To illustrate collision filtering, let’s take an example: we have three kinds of objects in a simple platformer game: players, monsters, and scenery.

We want the following rules: players should not collide with each others, neither do monsters, but players should collide with monsters (and vice-versa). Of course, players and monsters have to collide with the scenery. Scenery object will collide with each other (to build pyramids of crates for instance).

Filter categories and masks

Categories and masks are the most powerful way to deal with collision filtering, but also the most complicated for newcomers. The idea is to define a category per object type, and to use the masks to filter collisions between these object types.

Therefore, we start be defining three categories:

final short CATEGORY_PLAYER = 0x0001;  // 0000000000000001 in binary
final short CATEGORY_MONSTER = 0x0002; // 0000000000000010 in binary
final short CATEGORY_SCENERY = 0x0004; // 0000000000000100 in binary

Then we can assign these categories to our objects:

player1FixtureDef.filter.categoryBits = CATEGORY_PLAYER;
player2FixtureDef.filter.categoryBits = CATEGORY_PLAYER;

monster1FixtureDef.filter.categoryBits = CATEGORY_MONSTER;
monster2FixtureDef.filter.categoryBits = CATEGORY_MONSTER;
monster3FixtureDef.filter.categoryBits = CATEGORY_MONSTER;
monster4FixtureDef.filter.categoryBits = CATEGORY_MONSTER;

groundFixtureDef.filter.categoryBits = CATEGORY_SCENERY;
crate1FixtureDef.filter.categoryBits = CATEGORY_SCENERY;
crate2FixtureDef.filter.categoryBits = CATEGORY_SCENERY;

Wait, 0x001, 0x002 and 0x004 ? Why not 1, 2 and 3 ? Because Box2D categories (and masks) are bit fields (coded as shorts, thus on 16 bits) ! That means that the possible categories are powers of 2 (in decimal: 1, 2, 4, 8, 16, 32, 64, 128…, or in hexadecimal: 0x1, 0x2, 0x4, 0x8, 0x10, 0x20, 0x40, 0x80…). 16 bits means that there are 16 categories possible, from 0x0001 to 0x8000.

Now, let’s define the filter masks. Masks work as follows:

for each object o1 in the world
    for each object o2 in the world
        isCollisionEnabled = (o1.filter.maskBits & o2.filter.collisionBits) ≠ 0

Can you guess the resulting masks in our game ? They are as follows:

final short MASK_PLAYER = CATEGORY_MONSTER | CATEGORY_SCENERY; // or ~CATEGORY_PLAYER
final short MASK_MONSTER = CATEGORY_PLAYER | CATEGORY_SCENERY; // or ~CATEGORY_MONSTER
final short MASK_SCENERY = -1;

The “~” in front of a category name is a one’s complement in C family languages (including Java). For instance, ~0x0001 = 0xFFFE. In binary, it changes 0 into 1 and vice-versa.

Since the scenery category has to collide with everything, we just set its mask to -1, which also equals to 0xFFFF in a bit field (its due to the representation of signed numbers).

Using a 0xFFFF (or -1) mask will enable collisions with every category. As a contrary, setting a mask to 0x0000 (or 0) will disable collisions with everything !

We can assign these masks to our objects:

player1FixtureDef.filter.maskBits = MASK_PLAYER;
player2FixtureDef.filter.maskBits = MASK_PLAYER;

monster1FixtureDef.filter.maskBits = MASK_MONSTER;
monster2FixtureDef.filter.maskBits = MASK_MONSTER;
monster3FixtureDef.filter.maskBits = MASK_MONSTER;
monster4FixtureDef.filter.maskBits = MASK_MONSTER;

groundFixtureDef.filter.maskBits = MASK_SCENERY;
crate1FixtureDef.filter.maskBits = MASK_SCENERY;
crate2FixtureDef.filter.maskBits = MASK_SCENERY;

That’s all. Just let the magic of Box2D operates.

Filter groups

You can do everything you need with categories and masks, but sometimes you don’t need their bit-field complexity for some tasks. Groups were specifically introduced to quickly disable or enable collision checks between objects that are somehow related. The Box2D manual says:

Collision groups let you specify an integral group index. You can have all fixtures with the same group index always collide (positive index) or never collide (negative index).

Groups should be used instead of categories and masks when you only need to disable collisions between objects of a same category. Therefore, groups can be used in our illustration example, because everything should collide with everything, except players against players and monsters against monsters. We can shorten the previous setup to:

final short GROUP_PLAYER = -1;
final short GROUP_MONSTER = -2;
final short GROUP_SCENERY = 1;

And we assign these groups as follows:

player1FixtureDef.filter.groupIndex = GROUP_PLAYER;
player2FixtureDef.filter.groupIndex = GROUP_PLAYER;

monster1FixtureDef.filter.groupIndex = GROUP_MONSTER;
monster2FixtureDef.filter.groupIndex = GROUP_MONSTER;
monster3FixtureDef.filter.groupIndex = GROUP_MONSTER;
monster4FixtureDef.filter.groupIndex = GROUP_MONSTER;

groundFixtureDef.filter.groupIndex = GROUP_SCENERY;
crate1FixtureDef.filter.groupIndex = GROUP_SCENERY;
crate2FixtureDef.filter.groupIndex = GROUP_SCENERY;

That’s all. That’s two times less code than with categories and masks. However, there is no way to disable collisions between groups. That’s why categories and masks are more powerful than groups.

Conclusion

Collision filters are very valuable tools. Use them with care but use them everywhere. Collisions can also be disabled in the world contact listener, but collisions have to be checked first before a contact is reported, so this kind of filtering won’t remove the computational overhead of the collision check. Go the fixture filter way, always !

Advertisements

6 Comments

Leave a Comment
  1. MrGecko / Jul 11 2011 01:00

    Hello (Is there another more direct way to contact you ?) Aurélien.
    I’ve been searching for recent works using Box2D and I found this blog.

    In the next days I will add the box2d filtering features to my own python engine which uses pygame and pybox2d.

    As far as I know, the default filtering data in pybox2d are :
    groupIndex = 1
    categoryBits = 0
    maskBits = 0xFFFF

    Therefore everything should collide with everything by default, isn’t it ?
    Nothing seems to collide in my case. I even listen to the contact through the ContactListener: nothing happens.
    I have no idea of what’s going wrong and my character just walks through my (static) walls. The characters only freeze for a very short moment when he hits the wall (which doesn’t happens when the wall is set as a dynamic body) but there is no reaction.
    Do you have any idea :) ?

    Oh, and I should take a look at your Box2d editor (especially at the export file format you provide): it looks GREAT. For the moment I use a 3d editor, export the mesh to the .obj file format then parse it myself: what a pain :D.

    A lot of interesting stuff on this website !

    ps: tu parles de multiples de 2, mais il s’agit plutôt de “power of two” non ?

  2. Aurélien Ribon / Jul 11 2011 10:36

    Hello!
    This behavior is strange. By default, the box2d engine makes everything collide with everything. It may be due to the pybox2d engine. Are you sure their is no special flag to check before starting ?

    Box2d editor is coming very soon ;-)
    Export format is just a binary file with more or less a list of floats defining the convex polygons.

    ps: corrected, thanks!

  3. Michiel De Mey / Aug 7 2011 14:22

    Thank you! You explained it really well to me.
    I was able to convert this code to Actionscript 3 with success.

    I just had to change the data type from short to Number.

    I only used the Filter categories and mask method though.

  4. a / Aug 21 2011 10:53

    So you think this is not easy to master and you feel like you should write a tutorial on it, and then you provide nothing more or actually even less than what is in the manual? Really?

  5. Aurélien Ribon / Aug 22 2011 09:11

    Really!
    Come on man, I spend my whole spare-time releasing free and open-source tools to the community. I made a small free guide for a simple box2d topic and you’re not happy? I’ll live with that.

  6. a / Aug 29 2011 14:53

    Come on, have you actually read my comment ? :D

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s