How to Use BeginContact in Planck.js?

This article provides a practical guide on how to utilize the BeginContact event in planck.js to detect the exact moment two fixtures start colliding. You will learn how to set up a contact listener, access the fixtures involved in the collision, and implement custom logic like scoring or triggering visual effects when objects touch.


Setting Up the Contact Listener

In planck.js (a 2D physics engine based on Box2D), collision handling is managed by registering listener functions on the physics world object. The BeginContact event fires during the physics step immediately after two fixtures overlap for the first time.

To start listening for these collisions, you need to call world.on('begin-contact', callback).

// Initialize your planck.js world
const world = planck.World();

// Register the BeginContact listener
world.on('begin-contact', function(contact) {
    // Collision handling logic goes here
});

Extracting Fixtures and Bodies

The contact object passed into your callback function contains all the data regarding the collision. To find out which specific objects collided, you must extract the fixtures, and optionally, the rigid bodies they are attached to.

world.on('begin-contact', function(contact) {
    const fixtureA = contact.getFixtureA();
    const fixtureB = contact.getFixtureB();

    const bodyA = fixtureA.getBody();
    const bodyB = fixtureB.getBody();
});

Identifying Specific Game Objects

Because BeginContact triggers for every collision in the world, you need a way to filter and identify the interacting objects. The most common method is using the setUserData() and getUserData() methods on fixtures or bodies to store identifiers.

1. Assigning Labels during Creation

// Player Body
const playerBody = world.createBody();
const playerFixture = playerBody.createFixture(planck.Circle(1.0));
playerFixture.setUserData({ type: 'player' });

// Coin Body
const coinBody = world.createBody();
const coinFixture = coinBody.createFixture(planck.Box(0.5, 0.5));
coinFixture.setUserData({ type: 'coin' });

2. Filtering inside the Listener

world.on('begin-contact', function(contact) {
    const dataA = contact.getFixtureA().getUserData();
    const dataB = contact.getFixtureB().getUserData();

    // Check if one is a player and the other is a coin
    if ((dataA?.type === 'player' && dataB?.type === 'coin') ||
        (dataB?.type === 'player' && dataA?.type === 'coin')) {
        
        console.log('Player collected a coin!');
        // Trigger gameplay logic here (e.g., increase score)
    }
});

Important Considerations