How to Use Planck.js Friction Joints for Top-Down Physics
Building a top-down 2D physics environment requires simulating
friction that acts on an object’s entire surface area, unlike
side-scrolling games where gravity pushes objects onto a platform. In
planck.js (a JavaScript port of Box2D), this is efficiently
achieved using a FrictionJoint. This article provides a
quick guide on how to configure rigid bodies, bind them with friction
joints to a static ground body, and tune maximum force and torque to
create realistic top-down movement and sliding mechanics.
Setting Up the Ground and Moving Bodies
In a top-down perspective, gravity is typically set to zero because the camera looks down on the playing field. Because of this, bodies will float infinitely when forces are applied unless simulated surface friction is introduced.
To set up the structure:
- Create a static ground body: This body doesn’t move and serves as the anchor point for your friction joints.
- Create your dynamic bodies: These represent players, enemies, or obstacles that move across the screen.
// Initialize a world with zero gravity
const world = planck.World({
gravity: planck.Vec2(0, 0)
});
// Create a static ground body at the origin
const ground = world.createBody();Configuring the Friction Joint
A FrictionJoint in planck.js works by
connecting a dynamic body to the static ground body. It actively resists
both linear translation (sliding) and angular rotation (spinning) to
simulate the drag of a surface.
When creating the joint, you must define the local or world anchors and then set the maximum friction constraints:
// Create a dynamic box body
const box = world.createBody({
type: 'dynamic',
position: planck.Vec2(0, 0)
});
box.createFixture(planck.Box(1.0, 1.0));
// Bind the box to the ground using a FrictionJoint
const frictionJoint = world.createJoint(planck.FrictionJoint({
bodyA: ground,
bodyB: box,
localAnchorA: planck.Vec2(0, 0),
localAnchorB: planck.Vec2(0, 0)
}));Tuning Mass, Force, and Torque
Once the joint is established, it will not apply any friction until
you define its limits. You must explicitly set maxForce
(resistance to moving) and maxTorque (resistance to
spinning).
- maxForce: Controls how quickly the object comes to a stop linearly. Higher values simulate rough terrain like grass or mud, while lower values simulate slick surfaces like ice.
- maxTorque: Controls how quickly an object stops rotating after a collision or angular impulse.
// Define the friction limits based on your object's mass
const objectMass = box.getMass();
const gravitySimulation = 9.8; // Simulated gravity constant
const frictionCoefficient = 0.5; // Surface grip factor
// Calculate and apply maximum force and torque limits
frictionJoint.setMaxForce(objectMass * gravitySimulation * frictionCoefficient);
frictionJoint.setMaxTorque(objectMass * frictionCoefficient * 0.5);By linking every dynamic body to the static ground with individual
FrictionJoint components, you create a modular
architecture. This structure handles complex, multi-object top-down
physics smoothly without manually calculating and subtracting velocity
vectors inside your game loop.