Physics
The @shell/physics package provides a small ECS-native 2D physics stack. It includes linear and angular velocity and force integration, gravity, circle and axis-aligned box colliders, collision response, overlap queries, raycasts, collision events, distance and spring joints, and a patrol.
Register PhysicsPlugin to install the complete stack:
import { PhysicsPlugin } from '@shell/physics';
world.addPlugin(PhysicsPlugin);Configure gravity and the spatial hash cell size when needed:
world.addPlugin( PhysicsPlugin.config({ gravity: { x: 0, y: 500 }, cellSize: 32, }),);| Option | Type | Default | Description |
|---|---|---|---|
gravity | { x: number; y: number } | { x: 0, y: 9.81 } | Acceleration applied to dynamic rigid bodies |
cellSize | number | 64 | Cell size used by the collider spatial hash |
Choose a cellSize near the typical collider size. Smaller cells reduce the number of broad-phase candidates but make large colliders occupy more cells.
Create a body
Section titled “Create a body”The aggregate package provides bundles for the three common body types. DynamicBody includes linear
and angular motion, force and torque accumulators, and a dynamic RigidBody. KinematicBody
includes motion components with a kinematic RigidBody, while StaticBody contains only Position
and a static RigidBody.
import { Define } from '@shell/ecs';import { CircleCollider, DynamicBody } from '@shell/physics';import { Position } from '@shell/transform';
const PlayerPrefab = Define.prefab() .with(DynamicBody) .with(Position, { x: 100, y: 100, z: 0 }) .with(CircleCollider, { radius: 16 });
const player = world.get('commands').spawn(PlayerPrefab).root;Body bundles do not choose a collider shape. Compose them with CircleCollider or BoxCollider as
needed. Adding a collider also adds the required CollisionLayer component with layer: 1 and
mask: 1 unless the entity already has one.
Apply forces and impulses
Section titled “Apply forces and impulses”PhysicsPlugin exposes the physicsForces resource as the primary gameplay API for changing a
dynamic body’s motion:
const physicsForces = world.get('physicsForces');
physicsForces.applyForce(player, { x: 600, y: 0 });physicsForces.applyImpulse(player, { x: 0, y: -200 });physicsForces.applyTorque(player, 30);physicsForces.applyAngularImpulse(player, Math.PI / 2);applyForce requires Force, applyImpulse requires Velocity, applyTorque requires Torque,
and applyAngularImpulse requires AngularVelocity. Every target also needs RigidBody.
Calls are queued until fixed-step force integration. Multiple calls accumulate, each call is
consumed once, and static or kinematic bodies are ignored. Call applyForce or applyTorque again
each fixed step for persistent effects.
What the plugin installs
Section titled “What the plugin installs”PhysicsPlugin combines five plugins:
| Plugin | Provides |
|---|---|
CorePlugin | Rigid bodies, gravity, force and torque accumulation, the physicsForces resource, and force integration |
KinematicPlugin | Linear and angular velocity integration |
CollidersPlugin | Collider indexing, collision detection and response, overlap queries, and raycasts |
JointsPlugin | Distance and spring joints between two entities |
PatrolPlugin | A walk back and forth on a line |
The aggregate package re-exports the public APIs from @shell/physics-core, @shell/kinematics, @shell/colliders, @shell/joints, @shell/patrol, and @shell/physics-debug. Import from @shell/physics unless you intentionally install only part of the stack.
Both CorePlugin and the aggregate PhysicsPlugin bind physicsForces as a PhysicsForces instance. See Bodies & Forces for detailed behavior and component requirements.
Fixed-step pipeline
Section titled “Fixed-step pipeline”Physics runs in fixed schedules. The system sets enforce this order:
clear force and torque -> run gameplay force producers -> drain queued forces and impulses -> integrate linear and angular forces -> set the speed of patrollers -> integrate position and rotation -> solve joints -> rebuild the spatial index -> detect collisions -> resolve collisionsUse PhysicsSet to place gameplay systems at the correct point in this pipeline. Force-producing systems belong in PhysicsSet.Forces, after force and torque are cleared. Queued PhysicsForces calls are drained once after those producers and before force integration.
Topics
Section titled “Topics”- Bodies & Forces - Configure rigid bodies, velocity, gravity, forces, torque, and impulses
- Colliders - Add shapes, collision layers, and collision response
- Spatial Queries - Find nearby colliders and cast rays
- Collision Events - React when contacts start, remain active, or stop
- Joints - Hold two entities at a distance with a rope, a rod, or a spring
- Patrol - Walk an entity back and forth on a line
- Debug Visualization - Render collider outlines and joint lines with gizmos
Limitations
Section titled “Limitations”- Physics operates in 2D using
Position.x,Position.y, and rotation aroundRotation.z. - Box colliders remain axis-aligned. Rotation is ignored by collisions, overlap queries, and raycasts.
- Collision response is intentionally simple. It applies linear restitution and positional correction, but does not generate angular impulses and does not currently use
RigidBody.friction.