Box2D Tutorial: Collision filtering

Jul 1, 2011 by     36 Comments    Posted under: Tutorials

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, 0×001, 0×002 and 0×004 ? 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: 0×1, 0×2, 0×4, 0×8, 0×10, 0×20, 0×40, 0×80…). 16 bits means that there are 16 categories possible, from 0×0001 to 0×8000.

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, ~0×0001 = 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 0×0000 (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 !

36 Comments + Add Comment

  • 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 ?

  • 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!

  • 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.

  • 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?

  • 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.

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

  • Great tutorial, it helped explain by example how to actually use categories/masks so I found it very helpful. Thanks!

  • Hi! :)

    Thanks for all your work. I have been reading your tutorials and seeing your tools, they all look great. I’m setting up for android development using libgdx. So far I have reading a lot about loading sprites, animations (even your tween engine) but I haven’t read a good tutorial about box2d. Do you have (and I would be very thankful) a beginner’s tutorial to understand how to work with box2d in libgdx? (A simple example should work, between 2 objects or something like that).

    Another question: Would the output of physics body editor work if I change the resolution of my game? I read your tutorial avoid Logic vs render, and the first comment, seeing the output of the editor I’m concerned that I would have to do a body for every asset and every resolution (If I have an asset in 20×20, another for 32×32 and another for 48×48).

    Thank you in advance :D

    • I wouldn’t be consistent with my own tutorials and ideals if the body editor wouldn’t be flexible enough :)
      Actually, since you’re designing models (for collision detection purpose) in the editor, the output is normalized so the sizes of your assets don’t matter. Indeed, the assets you use are visual guides to draw your models, but the shapes will be saved in a 100×100 square box, whatever the size of the asset. That’s why the loader I made for libgdx (FixtureAtlas) takes the desired width and height of your models in parameter (in meters or whatever your models unit), in order to correctly size them. In the incoming big update of the tool, this behavior will be more clear since I’ll draw the output square box on the screen of the editor.

      About box2d itself, I personally learned it by reading its manual. It is really well written, and if you read it in order, from top to bottom, you should understand every features of the engine :)

    • I see, thanks for the explanation, it seems that wouldn’t be a problem then. About the manual, is funny how I found a lot of different tutorial but not the manual itself (silly google! :D)

      I have another question (or request for that matter): Is there a tutorial about projection for libgdx (or maybe may you do one)? I read the tutorial about texture filters (“Know your texture filters!”) and a lot of documentation for ortographic projection, viewport and so on. But for me, in my little example (which is almost the same as the texture filter tutorial) it just doesn’t work. I created a textureatlas (background, 855 * 480) but in low res devices only a portion is displayed. I then set the projection to the batcher, matching the current resolution, but doesn´t do anything (Maybe it was changed with recent libgdx updates). Then I tried the camera solution (setting the ortographic projection of the camera class, update and then setting the projection to the batcher as “camera.combined”), but the image is zoomed instead of minified. Please, if you know how to solve this, I would appreciate it a lot (even a piece of code would do the job, it doesn’t need to be a whole tutorial, but it would also help as it seems a lot of people has the same problem)

      Thanks for your help.

    • Send me your little code by mail (aurelien.ribon [d] gmail [d] com), and I’ll be happy to fix it for you and tell you what was missing ;)

  • I sent my test project :) Thank you very much for all your help. After this I can start to develop my game without concerns :>

    I will keep watching my inbox for your response.

  • Thanks your article helped me so much. I have played a box2dweb game which use this technique to make it very cool. Try it here : http://pixsansar.com/cool-box2dweb-games

  • Thanks a lot for the tutorials and for the excellent tools you make for libgdx ((-;

  • [...] 例えば 味方同士はぶつからない 敵同士はぶつからない 味方と敵はぶつかる といったようなことが可能です。 (参考サイトhttp://www.aurelienribon.com/blog/2011/07/box2d-tutorial-collision-filtering/) [...]

  • Brilliant work!

  • Thank you for publishing a solid HowTo! It helped me in the development of Presto: You Are The Wand. A Magical Adventure Game For Wizards.

    • Cheers pal. I do apraecipte the writing.

    • Vicente Alves / Como já dizia Ainstein, Deus não joga dados tudo tem o seu porque, toda ação tem uma reação tudo é relativo e por ai vai…Gostei deste comentário ou não: 6

    • were being lured in by some people who went out to the deep sea to feed them. This made up for our hunting for seal presence mission at Rhosolli Bay in our trip to Wales. We’re satisfied now to get a clear view of

    • A great triangle connection for Beyond the Applause – An introduction to program/education/artistic directors for the following: private middle and High schools; theatre, dance, and visual arts after school programs and summer camp directors for performing and visual arts.As an education and career “director” for students connecting me to directors and coaches is a great way to educate them about my business as well as provide parents with the added bonus/benefit for signing up with their programs.Great food for thought questions!Chris Holzwarth

    • Bonjour Cécile,La formation Montessori pour le niveau maternelle aura lieu comme d’habitude à Paris en juillet 2012.Vous pouvez déjà vous y inscrire.Envoyez-nous un cv et une lettre de motivation.Bonne journéeML

    • Hey, You had me check my Gmail account but I found nothing in the Spam box . I’m waiting for over 17 hours now. Over that time I managed to survive the end of the world, but not to get my license…

  • If my scene have multiply bodies and i just want two bodies not to collide, will this work?
    i mean if i place that two bodies in same group and i will not specify group for others will it work?

  • thanks, very userful

  • Thanks, this post is very useful.

  • I’ve assigned -1 to an object and it refuses to collide with everything.

    var CATEGORY_CEILING:Number = 0×0001;
    var CATEGORY_OBJECT:Number = 0×0002;
    var CATEGORY_SCENERY:Number = 0×0004;

    var MASK_CEILING:Number = CATEGORY_OBJECT | CATEGORY_SCENERY;
    var MASK_OBJECT:Number = CATEGORY_CEILING | CATEGORY_SCENERY;
    var MASK_SCENERY:Number = -1;

    I then have the following three elements.

    topwallFixtureDef.filter.maskBits = MASK_CEILING;
    fixtureDef.filter.maskBits = MASK_OBJECT;
    fixtureDef.filter.maskBits = MASK_SCENERY;

    Here are fixture definition for each

    // user ceiling
    var topwallFixtureDef:b2FixtureDef = new b2FixtureDef();
    topwallFixtureDef.shape = topwallShape;
    topwallFixtureDef.filter.maskBits = MASK_CEILING;
    var topwallFixture:b2Fixture = topwallBody.CreateFixture(topwallFixtureDef);

    // circle
    fixtureDef.shape = circShape;
    fixtureDef.filter.maskBits = MASK_OBJECT;
    body.CreateFixture(fixtureDef);

    // player
    var BoxFixtureDef:b2FixtureDef = new b2FixtureDef();
    BoxFixtureDef.shape = dynamicBox;
    BoxFixtureDef.filter.maskBits = MASK_SCENERY;
    body.CreateFixture(BoxFixtureDef);

    My goal was to have the circles collide with everything except the ceiling.
    Have the player collide with everything (circles and ceiling)

    But no matter what order I change these around I can’t make it happen.

  • [...] ideally, you configure group/mask for your lights and objects. Using this, you can tell your point light to ignore those branches. See a great write-up here. [...]

  • Definitely consider that that you stated.

    Your favourite justification seemed to be on the internet the simplest
    factor to take note of. I say to you, I definitely
    get annoyed at the same time as other people think about concerns that they just don’t understand about.

    You controlled to hit the nail upon the top as
    neatly as defined out the whole thing with no need side-effects ,
    folks can take a signal. Will probably be again to get more.
    Thanks

  • Ansonsten kann ich Dir für den Verkauf von Shirts im deutschsprachigen Raum Teezily
    empfehlen.

  • First off I want to say fantastic blog! I had a quick question which I’d like to ask if you do
    not mind. I was curious to find out how you center yourself and clear your thoughts before
    writing. I have had a difficult time clearing my mind in getting my ideas out.
    I truly do take pleasure in writing but it just seems like the first 10 to 15 minutes
    are usually lost just trying to figure out how to begin. Any suggestions or tips?
    Thank you!

  • I am really enjoying the theme/design of your web site.

    Do you ever run into any browser compatibility problems?
    A small number of my blog visitors have complained about my site not
    working correctly in Explorer but looks great in Opera. Do you have any advice to help fix this issue?

  • There’s certainly a lot to know about this issue. I like all of the points you made.

  • It is setup to not be hostile for mobile phones, while delivering the challenge that modern entries to the category are expected to provide.

  • Similarly, there’s no problem Equipment Region did something in 2015 that no additional mobile game developer
    did: being a western company, it successfully released an authentic sport internationally to the top grossing charts, including in those complicated-but-highly-rewarding Japan markets, China, Japan and South Korea.

  • excellent points altogether, you simply won a new reader.
    What may you suggest about your post that you just made a few days in the past?
    Any sure?

Got anything to say? Go ahead and leave a comment!

XHTML: You can use these tags: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>