Simulating Ground Friction in Matter.js

Simulating ground friction variation across materials like grass, mud, and ice in Matter.js requires configuring body friction properties and managing collision interactions dynamically. This guide explains how Matter.js calculates contact friction, how to assign appropriate surface properties to different terrain types, and how to use sensor zones and collision events to handle complex effects such as viscous drag in mud.

Understanding Contact Friction in Matter.js

Matter.js provides three primary properties to control body resistance:

When two bodies make contact, Matter.js calculates the resulting friction using the minimum value of the two bodies by default:

\[\text{Effective Friction} = \min(\text{bodyA.friction}, \text{bodyB.friction})\]

Because of this rule, ensure that any moving object (such as a vehicle or character) has its friction set sufficiently high (e.g., 0.8 or 1.0). If the moving body has a friction of 0.05, it will slide smoothly even over high-friction grass because the engine resolves to the lower value.

Setting Up Terrain Segments

To create varied terrain, break your ground into distinct segments or blocks and assign tailored friction values to each surface type.

const { Bodies } = Matter;

// Ice: Extremely low kinetic and static friction
const iceGround = Bodies.rectangle(200, 500, 400, 40, {
  isStatic: true,
  friction: 0.002,
  frictionStatic: 0.005,
  render: { fillStyle: '#a5f2f3' }
});

// Grass: Moderate kinetic friction and standard grip
const grassGround = Bodies.rectangle(600, 500, 400, 40, {
  isStatic: true,
  friction: 0.15,
  frictionStatic: 0.5,
  render: { fillStyle: '#5dbb63' }
});

// Mud base: High kinetic friction to resist rolling and sliding
const mudGround = Bodies.rectangle(1000, 500, 400, 40, {
  isStatic: true,
  friction: 0.9,
  frictionStatic: 1.0,
  render: { fillStyle: '#654321' }
});

Simulating Mud Viscosity with Sensor Zones

Surface friction alone is often insufficient for mud, as real mud creates thick, viscous drag rather than mere surface roughness. You can simulate depth and fluid resistance by layering a sensor body over the mud surface to adjust the moving object's frictionAir.

1. Create a Mud Sensor

const mudZone = Bodies.rectangle(1000, 470, 400, 60, {
  isStatic: true,
  isSensor: true, // Allows objects to pass through without physical collision
  label: 'mud_zone',
  render: { fillStyle: 'rgba(101, 67, 33, 0.4)' }
});

2. Detect Contact and Adjust Drag

Use collision events to detect when a body enters or exits the mud zone, temporarily raising its frictionAir:

const DEFAULT_AIR_FRICTION = 0.01;
const MUD_AIR_FRICTION = 0.15;

Matter.Events.on(engine, 'collisionStart', (event) => {
  event.pairs.forEach((pair) => {
    const { bodyA, bodyB } = pair;
    
    if (bodyA.label === 'mud_zone' && !bodyB.isStatic) {
      bodyB.frictionAir = MUD_AIR_FRICTION;
    } else if (bodyB.label === 'mud_zone' && !bodyA.isStatic) {
      bodyA.frictionAir = MUD_AIR_FRICTION;
    }
  });
});

Matter.Events.on(engine, 'collisionEnd', (event) => {
  event.pairs.forEach((pair) => {
    const { bodyA, bodyB } = pair;
    
    if (bodyA.label === 'mud_zone' && !bodyB.isStatic) {
      bodyB.frictionAir = DEFAULT_AIR_FRICTION;
    } else if (bodyB.label === 'mud_zone' && !bodyA.isStatic) {
      bodyA.frictionAir = DEFAULT_AIR_FRICTION;
    }
  });
});

Summary of Friction Settings

Terrain friction frictionStatic Implementation Method
Ice 0.001 - 0.01 0.005 - 0.02 Solid static body
Grass 0.1 - 0.2 0.4 - 0.6 Solid static body
Mud 0.8 - 1.0 1.0 Solid static body + isSensor zone modifying frictionAir