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
Contact Hertz30Contact stiffness (cycles/sec). Higher = less overlap but more jitter. Range: 1–500
Contact Damping Ratio10Contact bounciness damping. Lower = faster overlap recovery but more energetic. Range: 0.1–100
Contact Speed3Max overlap resolution speed (m/s). Range: 1–100

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.

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;

Components

Add physics components to entities in the scene editor alongside Transform. Every physics entity needs a RigidBody and at least one collider.

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, Transform } 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: Transform + RigidBody (bodyType=0 Static) + BoxCollider (halfExtents={x:10, y:0.5}) + Sprite
  • Player: Transform + RigidBody (bodyType=2 Dynamic, fixedRotation=true) + BoxCollider + Sprite + Player tag
  • Coin: Transform + RigidBody (bodyType=0 Static) + CircleCollider (isSensor=true) + Sprite + Coin tag

Next Steps

  • RigidBody — rigid body properties
  • Colliders — collider shapes overview
  • Events — collision and sensor events
  • API — runtime force and velocity control
  • Debug Draw — debug visualization