• News
  • Games
  • About
  • Cover image

    Understanding Physics Collision filtering with Phaser and MatterJs

    Uncover the power of collision filtering and the binary magic that lies beneath. Learn how to elegantly manage collisions using binary numbers, categories, and masks, all while gaining valuable insights applicable not only to Phaser but also to other game engines.

    Simplifying Physics Collisions with Phaser and MatterJs

    In my basketball game, I decided to switch from a side-view to a front-view perspective using Phaser with MatterJs, a 2D physics engine. For this small-scoped game, I needed to ensure that specific game objects, like the net and basketball hoop edges, didn't collide even though they visually overlapped.

    MatterJs provides collision groups and filters to specify which objects should collide. Initially, I attempted to change the collision group of the objects. However, it became apparent that the category/mask rules also play a crucial role.

    Collision Group, Mask, and Category in Depth

    To clarify, if two bodies have different values for their collision group, the category/mask rules come into play. These rules state that two bodies A and B will collide if each includes the other's category in its mask.

    However, MatterJs uses binary numbers to determine collisions, making it powerful but potentially tricky to handle. Using numbers like 1 or 3 in their decimal form might cause confusion when dealing with multiple filters.

    The Initial Problem

    The first hurdle was preventing specific game objects, like the net and basketball hoop edges, from colliding. Visually, they sat right on top of each other, but I didn't want them to interact physically.

    blog post image

    MatterJs employs collision groups and filters to determine which objects should or shouldn't collide. Initially, I thought changing the collision group of the objects would be enough.

    collisionGroupOnly.js

    // "this" is a Scene.
    const net = this.matter.add.rectangle(10,10,10,10,{
      collisionFilter: {
        group: 1,
      },
    }
    
    // Changing group is not enough to prevent them from colliding
    const hoopEdge = this.matter.add.rectangle(10,10,10,10,{
      collisionFilter: {
        group: 2, 
      },
    }
    

    However, I quickly realized that changing the group alone wasn't sufficient. According to MatterJs's documentation on filtering, it's crucial to understand the following rules:.

    • If the two bodies have the same non-zero value of collisionFilter.group, they will always collide if the value is positive, and they will never collide if the value is negative.
    • If the two bodies have different values of collisionFilter.group or if one (or both) of the bodies has a value of 0, then the category/mask rules apply as follows:

    Changing the groups mean the second rule applies, and we need to follow the category/mask rule.

    Again, from the documentation:

    Using the category/mask rules, two bodies A and B collide if each includes the other's category in its mask, i.e. (categoryA & maskB) !== 0 and (categoryB & maskA) !== 0 are both true.

    Knowing this, we can change the mask and category for our objects like the following example:

    collisionGroupPlusFilter

    // "this" is a Scene.
    const net = matter.add.rectangle(10,10,10,10,{
      collisionFilter: {
        group: 1,
        mask: 2, // 2 is different from the hoopEdge's category (1), meaning no collision
        category: 2,
      },
    }
    
    const hoopEdge = matter.add.rectangle(10,10,10,10,{
      collisionFilter: {
        group: 2,
        mask: 1, // 1 is different from the net's category (2), meaning no collision
        category: 1,
      },
    }
    

    Problem solved. But there's a catch, and an important one too. MatterJs uses the bits in the binary number to determine whether objects should collide or not. This is extremely important and useful, because we're able to filter more than just 1 type of object, but we have to utilize the magic of binary numbers. Using numbers like above (in their decimal form,) will cause you a lot of headache if you need more than just 1 filter.

    Binary Magic

    In binary (we can write a binary number in JavaScript by using 0b), we can represent 1, 2, 3 as 01, 10, 11 respectively:

    binaryNumbers.js

    const one = 0b01;
    const two = 0b10;
    const three = 0b11;