How to Make Crumbling Floor Tiles in Matter.js

This article explains how to create interactive crumbling floor tiles using the Matter.js 2D physics engine. You will learn how to detect when a player stands on a tile, initiate a collapse countdown, convert the static tile into a dynamic falling body, and clean up the object once it leaves the simulation.

1. Define the Tile Body

In Matter.js, terrain is typically created with isStatic: true so it resists gravity and collisions. To make a tile crumble later, initialize it as a static body with custom properties to track its trigger state and timer.

const { Bodies, Composite } = Matter;

function createCrumblingTile(x, y, width, height, dropDelay = 1000) {
  const tile = Bodies.rectangle(x, y, width, height, {
    isStatic: true,
    label: 'crumblingTile',
    render: {
      fillStyle: '#c29a64'
    }
  });

  // Custom metadata for tracking state
  tile.dropDelay = dropDelay;
  tile.isTriggered = false;

  return tile;
}

2. Detect Player Contact

Use Matter.js collision events to determine when the player lands on top of the tile. Attach a listener to the collisionStart event on your engine instance.

Matter.Events.on(engine, 'collisionStart', (event) => {
  event.pairs.forEach((pair) => {
    const { bodyA, bodyB } = pair;

    const tile = [bodyA, bodyB].find(b => b.label === 'crumblingTile');
    const player = [bodyA, bodyB].find(b => b.label === 'player');

    if (tile && player && !tile.isTriggered) {
      // Ensure the player is landing from above
      if (player.position.y < tile.position.y) {
        triggerTileCrumble(tile);
      }
    }
  });
});

3. Handle the Crumble and Drop Phase

Once triggered, lock the tile state so multiple collision events do not restart the timer. When the designated duration expires, use Matter.Body.setStatic(tile, false) to enable gravity, causing the tile to fall away.

function triggerTileCrumble(tile) {
  tile.isTriggered = true;

  // Optional: visually indicate that the tile is about to fall
  tile.render.fillStyle = '#8b5a2b';

  setTimeout(() => {
    // Make the tile dynamic so it falls
    Matter.Body.setStatic(tile, false);

    // Disable collision with the player after it starts falling (optional)
    tile.collisionFilter.group = -1;

    // Schedule cleanup
    removeTileAfterDelay(tile, 3000);
  }, tile.dropDelay);
}

4. Remove the Tile from Memory

Dynamic bodies falling indefinitely will degrade performance. Once the tile has fallen off-screen, remove it from the Matter.js composite world.

function removeTileAfterDelay(tile, delay) {
  setTimeout(() => {
    Composite.remove(engine.world, tile);
  }, delay);
}

5. Optional: Adding a Shake Effect

To improve gameplay feedback, you can apply a subtle position offset to the tile inside your main update loop while tile.isTriggered is active and tile.isStatic is still true.

Matter.Events.on(engine, 'beforeUpdate', () => {
  engine.world.bodies.forEach((body) => {
    if (body.label === 'crumblingTile' && body.isTriggered && body.isStatic) {
      const shakeOffset = (Math.random() - 0.5) * 2;
      Matter.Body.setPosition(body, {
        x: body.position.x + shakeOffset,
        y: body.position.y
      });
    }
  });
});

By coupling isStatic: true bodies with custom timers and the setStatic method upon collision, you can implement responsive crumbling platform mechanics suitable for 2D platformers and puzzle games.