Skip to content

Physics ​

willow can attach rigid bodies to nodes. A node with a body moves where the simulation puts it, so a falling crate is just a node you never move yourself.

How it fits together ​

  1. A physics root. Call enablePhysics(config) on a container node. Everything below it shares one isolated world. Physics roots cannot nest.
  2. Bodies. Call setBody(definition) on a node under the root. The body starts at the node's current position and rotation.
  3. Stepping. In a window app the scene steps every physics root once per logic tick. You can also step a root yourself with stepPhysics(dt).
ts
import { Node, PhysicsConfig, PhysicsBox, PhysicsDynamic, PhysicsStatic, PhysicsBody } from "willow";

class Drop {
    static main(): void {
        const world: Node = new Node("world");
        world.enablePhysics(new PhysicsConfig(Vec2{ x: 0.0, y: 900.0 }));

        const floor: Node = new Node("floor");
        floor.setPosition(200, 300);
        world.add(floor);
        floor.setBody(new PhysicsStatic(new PhysicsBox(400, 20)));

        const crate: Node = new Node("crate");
        crate.setPosition(200, 100);
        world.add(crate);
        crate.setBody(new PhysicsDynamic(new PhysicsBox(24, 24), 1, 0.5, 0.1));

        for (let i: int = 0; i < 120; i++) {
            world.stepPhysics(1 / 60);
        }
        console.log("crate rests at y = " + Math.round(crate.y));

        const body: PhysicsBody | null = crate.getBody();
        if (body !== null) {
            body.applyImpulse(Vec2{ x: 0.0, y: -300.0 });
            console.log("velocity after a kick: " + Math.round(body.velocity().y));
        }
    }
}

Output:

text
crate rests at y = 278
velocity after a kick: -300

The floor's top is at 290 (300 minus half its 20 px height), and the crate's center rests half its 24 px height above that. Boxes are centered on the node's position.

This sample runs without a window, which makes physics easy to test. In a window app, add world to engine.root2d, give the nodes sprites, and leave the stepping to the scene.

Bodies ​

DefinitionMoves byConstructor
PhysicsDynamicForces, impulses, gravity and collisions.(shape, mass, friction?, elasticity?, moment?)
PhysicsStaticNever moves. Floors and walls.(shape, friction?, elasticity?)
PhysicsKinematicYour code, through velocity or position. Ignores forces and gravity. Moving platforms.(shape, friction?)

elasticity is bounciness from 0 to 1. A moment of 0 (the default) is worked out from the shape and mass.

Shapes ​

ShapeConstructor
PhysicsCircle(radius, offset?)
PhysicsBox(width, height, cornerRadius?), centered on the node
PhysicsSegment(a, b, radius?), a line between two points
PhysicsPolygon(verts, cornerRadius?), the convex hull of the points

Driving a body ​

getBody() returns the live PhysicsBody (or null). It reads position(), angle(), velocity() and angularVelocity(), and changes them with setPosition, setAngle, setVelocity, setAngularVelocity, applyImpulse (an instant kick) and applyForce (a push until the next step).

setBodyEnabled(false) takes a body out of the simulation and keeps its state; removeBody() detaches it. disablePhysics() on the root removes every body below it.

World settings ​

PhysicsConfig(gravity?, iterations?, sleepTime?): gravity in pixels per second squared (positive y is down), solver iterations per step (0 means the default of 10), and how long a resting body waits before it sleeps (0 never sleeps).

Next ​