Collision filtering

When we want two objects to pass each other, we can define them as never colliding with each other or even never colliding with anything. This is done by collision filtering. Each fixture can have the following three parameters defined:

  • Its own category
  • Other categories that it collides with
  • Whether or not to collide with bodies in a specific group

When creating a FixtureDef object, there is a constructor that allows us to specify all the parameters:

    FixtureDef fixture = PhysicsFactory.createFixtureDef(density, elasticity, friction, sensor, category, categoryMask, groupIndex);

Category is a number of the type short. This means that it is 16 bits long, and therefore, there are 16 categories. The other two parameters are of the type short as well.

Category

A fixture should belong to a single category only and will collide with every fixture in a category specified in category mask. A category has a number from 0 to 15. A fixture's category or categories are then specified by a single 16-bit number. A 16-bit number in the binary format consists of 16 zeroes and ones. We can index them from 0 to 15. If the fixture belongs to a category N, then the Nth bit will be 1. If it doesn't belong to the category, then the Nth bit will be 0.

Tip

A fixture can belong to multiple categories, but this only increases complexity.

There are 16 categories, but we will omit bits 5 to 16 and show just a simplified 4-bit example. We can imagine that there are 12 more zeroes prefixed to these numbers. Usually, the categories and masks are written as decimal numbers. The decimal number is simply the binary number with the base 10. We will see in the following example what this means:

Category

Binary

Decimal

0

0000

0

1

0001

1

2

0010

2

3

0100

4

4

1000

8

Now, if we want a fixture to belong to category 4, we will pass 8 to the constructor as its category.

The category mask

The category mask specifies with which other categories the fixture collides. It can collide with multiple categories. Specifying multiple categories using a single number can be done by adding the categories together. For example, if we want the fixture to collide with categories 1, 2, and 4, we will pass 1 + 2 + 8 = 11. It might be easier to imagine it in the binary representation, as follows:

  • 0001 +
  • 0010 +
  • 1000 =
  • 1011

It is important that each number is on a new line.

10112 (base 2, binary) is 1110 (base 10, decimal). When two fixtures A and B collide, a simple check is calculated using a logical conjunction (the AND operator): categoryA AND maskB must not be 0 and at the same time categoryB AND maskA must not be 0. Here are the possible results of the AND operator:

  • 0 AND 0 = 0
  • 0 AND 1 = 0
  • 1 AND 0 = 0
  • 1 AND 1 = 1

So if a fixture is defined to collide with the category mask 1101 (=13), it will collide with any fixture that belongs to category 1, 3, or 4 (1 + 4 + 8 = 13). A few examples of collisions are as follows:

 

A

B

C

Category mask

1100

1110

1111

Detected contact with category

0010

0010

0100

Result (AND)

0000

0010

0100

Collision happened?

No

Yes

Yes

Note

For correct functionality of collisions, the masks and categories must be defined reflexively. Therefore, if a fixture belongs to category A and its mask defines that it collides with category B, fixtures in category B should define the mask that includes category A.

Example of categories and masks

The theory might be a bit difficult to grasp; let's see a real-world example in our game.

We are temporarily going to change the populate() method in GameScene to add our new objects. First, let's define some categories, as follows:

  public static final short CATEGORY_BOX_1 = 1;
  public static final short CATEGORY_BOX_2 = 2;
  public static final short CATEGORY_CIRCLE = 4;
  public static final short CATEGORY_PLATFORM = 8;

Then, we define masks, as follows:

  public static final short MASK_ALL = 
      CATEGORY_BOX_1 +
      CATEGORY_BOX_2 +
      CATEGORY_CIRCLE + 
      CATEGORY_PLATFORM;

  public static final short MASK_BOXES = 
      CATEGORY_BOX_1 +
      CATEGORY_BOX_2 +
      CATEGORY_PLATFORM;
  
  public static final short MASK_CIRCLE = 
      CATEGORY_CIRCLE +
      CATEGORY_PLATFORM;  

MASK_ALL means that the fixture collides with every other category. MASK_BOXES defines the fixture that collides with the platform and both boxes, and MASK_CIRCLE defines the fixture that collides with other circles and platforms.

Next, we temporarily stop the camera movement and remove the touch listener so that we can actually see a static scene with our new objects, as follows:

  @Override
  public void populate() { 
    createBackground();
    createPlayer();
    //camera.setChaseEntity(player);
    createHUD();
    
    addPlatform(240, 100, false);
    addPlatform(340, 400, false);
    addEnemy(140, 400);
     
    engine.enableAccelerationSensor(activity, this);
    registerUpdateHandler(physicsWorld);  
    
    //physicsWorld.setContactListener(new MyContactListener(player));
    
    ...
  }

Finally, just after the commented line in the populate() method, we can add the new code, as follows:

    FixtureDef boxFixture1 = PhysicsFactory.createFixtureDef(1f, 0f, 2f, false,
        CATEGORY_BOX_1, MASK_BOXES, (short) 0);
    PhysicsFactory.createBoxBody(physicsWorld, 100, 300, 50, 25, BodyType.DynamicBody, boxFixture1);  
    FixtureDef boxFixture2 = PhysicsFactory.createFixtureDef(1f, 0f, 2f, false,
        CATEGORY_BOX_2, MASK_BOXES, (short) 0);
    PhysicsFactory.createBoxBody(physicsWorld, 130, 350, 50, 25, BodyType.DynamicBody, boxFixture2);  
    
    FixtureDef circleFixture = PhysicsFactory.createFixtureDef(1f, 0f, 2f, false,
        CATEGORY_CIRCLE, MASK_CIRCLE, (short) 0);
    Body circle = PhysicsFactory.createCircleBody(physicsWorld, 100, 440, 25, BodyType.DynamicBody, circleFixture);  
    circle.setFixedRotation(true);
   
    FixtureDef platformFixture = PhysicsFactory.createFixtureDef(1f, 0f, 2f, false,
        CATEGORY_PLATFORM, MASK_ALL, (short) 0);
    PhysicsFactory.createBoxBody(physicsWorld, 100, 250, 150, 15, BodyType.StaticBody, platformFixture);  

This code defines four bodies; each of them belongs to a separate category. Right now, the circle and boxes don't collide, but the boxes collide with each other, as shown in the following screenshot:

Example of categories and masks

Thanks to the category definitions, we can very easily turn off collisions for the rectangles by removing the categories from MASK_BOXES, as follows:

  public static final short MASK_ALL = 
      CATEGORY_BOX_1 +
      CATEGORY_BOX_2 +
      CATEGORY_CIRCLE + 
      CATEGORY_PLATFORM;

  public static final short MASK_BOXES = 
      CATEGORY_PLATFORM;
  
  public static final short MASK_CIRCLE = 
      CATEGORY_CIRCLE +
      CATEGORY_PLATFORM;  

The result looks as expected. The rectangular boxes now overlap too, as shown in the following screenshot:

Example of categories and masks

Note

The difference between collision filtering and sensors is that sensors don't influence their environment and other bodies but still detect collision and generate events for contact listener, whereas filtered collisions behave as if no collision happened at all.

Group index

The last parameter we can define when creating a fixture is group index. Group index is a positive or a negative number of a group. Members of the same group will either always collide with each other if the group number is positive, or never collide with each other if it is negative. Groups are different from categories and they have precedence over categories when deciding whether two objects collide or not.

In our example, if we defined both boxes and the circle in group 1, they would always collide. If we defined them with a negative group index, let's say -5, they would never collide. Group index 0 has no effect.

Here's an example how to make the boxes collide again without changing their categories or category masks. The change in the group index is highlighted in the following code:

    FixtureDef boxFixture1 = PhysicsFactory.createFixtureDef(1f, 0f, 2f, false,
        CATEGORY_BOX_1, MASK_BOXES, (short) 1);
    PhysicsFactory.createBoxBody(physicsWorld, 100, 300, 50, 20, BodyType.DynamicBody, boxFixture1);  
    FixtureDef boxFixture2 = PhysicsFactory.createFixtureDef(1f, 0f, 2f, false,
        CATEGORY_BOX_2, MASK_BOXES, (short) 1);
    PhysicsFactory.createBoxBody(physicsWorld, 130, 350, 50, 20, BodyType.DynamicBody, boxFixture2);  
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.216.88.54