Physics

Rigid bodies

The RigidBody component — body kinds, mass and inertia, forces and impulses, damping, and sleeping.

A rigid body is an entity carrying three components: a Transform (its pose), a RigidBody (its dynamic state), and a Collider (its shape — covered in Colliders & shapes).

The split matters. Pose lives on the Transform — position and rotation — and the PhysicsSystem writes it back every step, so the renderer reads the result for free. RigidBody owns everything that isn’t pose: velocity, mass, inertia, damping, gravity scale, and collision filtering.

import { RigidBody } from "@woosh/meep-engine/src/engine/physics/ecs/RigidBody.js";
import { BodyKind }  from "@woosh/meep-engine/src/engine/physics/ecs/BodyKind.js";

const body = new RigidBody();
body.kind = BodyKind.Dynamic;
body.mass = 2;

new Entity()
    .add(new Transform())
    .add(body)
    .add(collider)        // see Colliders & shapes
    .build(ecd);

Body kinds

BodyKind decides how a body participates in the simulation.

KindMoves?Use for
StaticNever. Infinite mass; stored as immovable level geometry.Level geometry, ground, walls.
DynamicIntegrated under gravity, forces, and contact impulses.Gameplay objects — the common case.
KinematicVelocityDriven by its velocity; pushes dynamic bodies but isn’t pushed back.Moving platforms, elevators.
KinematicPositionDriven by direct pose writes; velocity is derived from the per-step delta.Animation- or code-driven movers.

Mass and inertia

mass is in kilograms and must be positive for dynamic bodies. Static and kinematic bodies behave as if mass were infinite regardless.

Rotation is a separate matter. A body’s inverseInertiaLocal defaults to (0, 0, 0), which locks rotation — the body slides but never tumbles. This is deliberate: a missing inertia tensor gives you a locked body, never a NaN tumble. To let a body spin, set the inverse of its principal moments of inertia:

// Solid box, half-extents (hx, hy, hz):
const k = body.mass / 3;
body.inverseInertiaLocal.set(
    1 / (k * (hy*hy + hz*hz)),
    1 / (k * (hx*hx + hz*hz)),
    1 / (k * (hx*hx + hy*hy)),
);

A zero component locks that axis — handy for keeping a character capsule upright (inverseInertiaLocal.set(0, y, 0)).

Velocity, damping, and gravity

  • linearVelocity / angularVelocity — world-space, m/s and rad/s. Read freely; set through the system (below) so sleeping bodies wake.
  • linearDamping / angularDamping — per-second velocity decay. A little (0.020.05) helps piles settle instead of jittering.
  • gravityScale — multiplier on world gravity. 1 is normal, 0 floats, negative inverts. World gravity defaults to (0, -9.81, 0); change it with physics.setGravity({ x, y, z }).

Forces and impulses

Don’t write the force accumulators directly — go through the PhysicsSystem, which also wakes a sleeping body for you:

physics.applyForce(body, { x: 0, y: 40, z: 0 });          // continuous, this step
physics.applyImpulse(body, { x: 5, y: 0, z: 0 });         // instantaneous Δmomentum
physics.applyImpulseAt(body, transform, impulse, point);  // off-centre → adds spin
physics.applyTorque(body, { x: 0, y: 2, z: 0 });
physics.setLinearVelocity(body, { x: 0, y: 0, z: 0 });

Forces and torques accumulate between fixed steps and are consumed by the integrator each tick.

Sleeping

A body that stays below a small velocity threshold for sleepTimeThreshold seconds (default 0.5) leaves the active set and stops costing solver time. Connected piles sleep together — a whole stack drops out and wakes as one.

  • physics.wake(body) / physics.sleep(body) — force the state.
  • RigidBodyFlags.DisableSleep — keep a body always awake (e.g. one you drive externally).
import { RigidBodyFlags } from "@woosh/meep-engine/src/engine/physics/ecs/RigidBodyFlags.js";
body.setFlag(RigidBodyFlags.DisableSleep);

Where to go next