Skip to content

Physics

The physics system provides 2D rigid body simulation powered by a standalone Box2D WASM module. The editor and build pipeline handle loading automatically — you only need to enable it in settings and add components to entities.

Setup

Enable physics in the editor: Settings → Physics → Enable Physics.

Configure the simulation parameters in the same panel:

SettingDefaultDescription
Gravity X0Horizontal gravity
Gravity Y-9.81Vertical gravity (negative = downward)
Fixed Timestep1/60Physics step interval in seconds
Sub-Step Count4Sub-steps per physics step for accuracy

Coordinate Conversion (PPU)

Collider dimensions (halfExtents, radius, halfHeight) are specified in physics units (meters). The Canvas component’s pixelsPerUnit (default 100) determines the conversion ratio between pixel coordinates and physics coordinates.

Position conversion is handled automatically — entity positions (in pixels) are divided by PPU when sent to Box2D, and multiplied by PPU when read back. Physics API values (force, velocity, impulse, gravity) also operate in physics units. You do not need to perform any manual conversion.

Components

Add physics components to entities in the scene editor alongside LocalTransform.

RigidBody

Every physics entity needs a RigidBody component. It defines the body type and physical properties.

PropertyTypeDefaultDescription
bodyTypenumber2 (Dynamic)0=Static, 1=Kinematic, 2=Dynamic
gravityScalenumber1.0Gravity multiplier
linearDampingnumber0.0Linear velocity damping
angularDampingnumber0.0Angular velocity damping
fixedRotationbooleanfalseLock rotation
bulletbooleanfalseCCD for fast objects
enabledbooleantrueEnable physics body

Colliders

Each entity also needs a collider shape. Choose one:

  • BoxColliderhalfExtents (Vec2), offset (Vec2), density, friction, restitution, isSensor
  • CircleColliderradius, offset (Vec2), density, friction, restitution, isSensor
  • CapsuleColliderradius, halfHeight, offset (Vec2), density, friction, restitution, isSensor

See Components for the full property tables.

Body Types

Static (0)

Does not move. Used for walls, floors, and platforms.

rigidBody.bodyType = 0;

Kinematic (1)

Moved by code (transform), not by physics forces. Other bodies collide with it but don’t push it. Used for moving platforms and elevators.

rigidBody.bodyType = 1;

Dynamic (2)

Fully simulated. Responds to gravity, forces, and collisions. Used for players, projectiles, and physics objects.

rigidBody.bodyType = 2;

Collision Events

Read collision and sensor events from the PhysicsEvents resource:

import { defineSystem, addSystem, Res } from 'esengine';
import { PhysicsEvents } from 'esengine/physics';
addSystem(defineSystem(
[Res(PhysicsEvents)],
(events) => {
for (const e of events.collisionEnters) {
// e.entityA, e.entityB, e.normalX, e.normalY, e.contactX, e.contactY
}
for (const e of events.collisionExits) {
// e.entityA, e.entityB
}
for (const e of events.sensorEnters) {
// e.sensorEntity, e.visitorEntity
}
for (const e of events.sensorExits) {
// e.sensorEntity, e.visitorEntity
}
}
));

Example: Simple Platformer

Set up a ground, player, and coin sensor in the editor, then handle events in code:

import { defineSystem, addSystem, Res, Input, Query, Mut, LocalTransform } from 'esengine';
import { RigidBody, PhysicsEvents } from 'esengine/physics';
import { Player, Coin } from './components';
addSystem(defineSystem(
[Res(PhysicsEvents)],
(events) => {
for (const e of events.sensorEnters) {
// Coin collected
}
}
));

Editor setup:

  • Ground: LocalTransform + RigidBody (bodyType=0 Static) + BoxCollider (halfExtents={x:10, y:0.5}) + Sprite
  • Player: LocalTransform + RigidBody (bodyType=2 Dynamic, fixedRotation=true) + BoxCollider + Sprite + Player tag
  • Coin: LocalTransform + RigidBody (bodyType=0 Static) + CircleCollider (isSensor=true) + Sprite + Coin tag

Physics API (Advanced)

The Physics class provides runtime force/velocity control. It is automatically registered as a PhysicsAPI resource when the physics plugin loads, so you can access it from any system via Res(PhysicsAPI):

import { defineSystem, addSystem, Res } from 'esengine';
import { PhysicsAPI } from 'esengine/physics';
addSystem(defineSystem(
[Res(PhysicsAPI)],
(physics) => {
physics.applyForce(entity, { x: 0, y: 10 });
}
));
MethodDescription
applyForce(entity, force)Apply a continuous force (Vec2)
applyImpulse(entity, impulse)Apply an instant impulse (Vec2)
setLinearVelocity(entity, velocity)Set linear velocity directly
getLinearVelocity(entity)Get current linear velocity (Vec2)
setAngularVelocity(entity, omega)Set angular velocity (number)
getAngularVelocity(entity)Get current angular velocity
applyTorque(entity, torque)Apply rotational torque
applyAngularImpulse(entity, impulse)Apply instant angular impulse
setGravity(gravity)Change world gravity at runtime
getGravity()Get current world gravity (Vec2)

Next Steps

  • Components — full physics component property tables
  • Systems — creating systems to handle physics logic
  • Custom Draw — debug visualization of colliders