Skip to main content

Physics

game.ts
const renderLayers = {
foreground: 1 << 0,
};

const gravity = new Vector2(0, -300);
// Kept low enough that even the lightest shape (the narrow plank, ~225 mass)
// hit dead-center stays under ~2,400px/s - the speed at which a body can
// cross the 40px-thick boundary walls in a single physics step and tunnel
// through them. That failure was more likely to surface in fullscreen, where
// shapes have more open space to build up speed before reaching a wall,
// making the explosion look far stronger than in the windowed view.
const explosionForce = 1_000_000;
const explosionRadius = 600;

export const createPhysicsGame = async (): Promise<Game> => {
const { game, world, renderContext, time } = createGame('demo-game');

createCamera(world, {
isStatic: true,
cullingMask: renderLayers.foreground,
verticalWorldUnits: DEMO_VERTICAL_WORLD_UNITS,
});

const physicsWorld = new PhysicsWorld({ gravity });

await createBoundaries(world, renderContext, renderLayers.foreground);
await spawnShapes(world, renderContext, renderLayers.foreground);

world.addSystem(createCameraEcsSystem(time));
world.addSystem(createRenderEcsSystem(renderContext));
world.addSystem(createPhysicsSyncEcsSystem(physicsWorld, time));

// The camera is static at the world origin with a zoom of 1 (see
// `createCamera` above), so screen coordinates can be converted to world
// coordinates directly, once scaled by the camera's pixels-per-unit.
renderContext.canvas.addEventListener('mousedown', (event: MouseEvent) => {
const canvasBounds = renderContext.canvas.getBoundingClientRect();

const screenPosition = new Vector2(
event.clientX - canvasBounds.left,
event.clientY - canvasBounds.top,
);

const pixelsPerUnit = calculatePixelsPerUnit(
renderContext.height,
DEMO_VERTICAL_WORLD_UNITS,
);

const worldPosition = screenToWorldSpace(
screenPosition,
Vector2.zero,
1,
renderContext.width,
renderContext.height,
pixelsPerUnit,
);

physicsWorld.applyExplosiveForce(
worldPosition,
explosionForce,
explosionRadius,
);
});

return game;
};

This demo showcases the engine's built-in 2D physics: rigid bodies, gravity, collision detection and resolution. A pile of circles and squares falls into a walled area, bouncing and colliding with the floor, walls and each other until they settle. Click anywhere on the canvas to trigger an explosion that blasts nearby shapes away from the click point.