Skip to content

Physics (2D)

Opal uses Rapier 2D for rigid-body simulation. Physics runs during Play (and editor preview when enabled), driven by two built-in components:

ComponentIDRole
Rigid Body 2DrigidBody2dMass, velocity, gravity, body type
Collider 2Dcollider2dCollision shape, friction, bounce, sensors
Joint 2Djoint2dHinge / weld / distance between two bodies

Quick setup

Dynamic object (ball, crate, character)

  1. Attach Rigid Body 2D — set Body Type to Dynamic.
  2. Attach Collider 2D — shape matches the sprite (Use Object Size is usually on).
  3. Hit Play. Gravity pulls the object down; collisions fire flow events.

A rigid body without a collider simulates motion but never collides. The inspector shows a warning when this pairing is missing.

Static floor / wall

  1. Attach Collider 2D only.
  2. Opal creates an implicit fixed rigid body automatically.

No Rigid Body 2D is required for immovable geometry.


Body types

TypeBehavior
dynamicSimulated by Rapier. Transform is written back each frame.
fixedImmovable. Thing transform drives the physics body (editor drag, motion, etc.).
kinematicPositionMoved by setting the Thing position; pushes dynamic bodies.
kinematicVelocityMoved by velocity; useful for moving platforms.

Coordinates & gravity

  • Scene positions use screen-style coordinates: +Y is down, same as the stage.
  • Default gravity is (0, 980) — tuned for pixel-scale scenes with lengthUnit: 100.
  • Collider sizes follow object width/height (and scale) when Use Object Size is enabled.

Collision events

Both components expose:

EventWhen
collisionStartContact begins
collisionEndContact ends

Payload: otherId, otherName, started, sensor.

Wire these in Object Flow like any other component event. If both Rigid Body 2D and Collider 2D are attached, each may emit for the same contact — prefer listening on Collider 2D unless you need body-specific logic.

Read-only fields update during play:

  • Rigid Body 2D: vx, vy, speed, sleeping
  • Collider 2D: colliding, contactCount

Common actions

Rigid Body 2D

ActionUse
setVelocityLaunch or steer (x, y)
applyImpulseInstant kick
teleportSnap position and sync the physics body
setBodyTypeSwitch dynamic / fixed / kinematic at runtime
pinToWeld to another body (arrow sticking). Params: targetId, atContactPoint, breakForce (max anchor separation in px; 0 = unbreakable). Fires pinBroke when the weld snaps.
wake / sleepControl sleep state

Collider 2D

ActionUse
setSensorPass-through trigger (no physical response)
setEnabledToggle participation in simulation
generateAutoColliderFit collider from sprite alpha (polygon, box, circle, tight)

Joints (Joint 2D)

Connect two physics bodies with a hinge, weld, or rope/spring. Attach Joint 2D on one object and set Target to the other. Both objects need Rigid Body 2D (and usually Collider 2D).

TypeBehavior
revoluteHinge. Limits are authored in degrees; motors use rad/s.
fixedWeld (no relative motion). Used by pinTo for arrow sticking.
distanceRope (stiffness 0) or spring (stiffness > 0). Rest Length 0 captures the current anchor separation when the joint is created.

Anchors are body-local pixels (0,0 = center). Drag the cyan handles on the stage when a Joint 2D object is selected. Break Separation (field breakForce) is max anchor separation in px before the joint snaps — not a force (Rapier JS does not expose joint impulses). Authored joints fire joint2d/broke; pinTo welds fire rigidBody2d/pinBroke.

Multi-object ragdoll

Place the Ragdoll Humanoid prefab (Library → Prefabs): six sibling parts with revolute joints. Bodies start as kinematicPosition (pose holds). Go limp with setBodyTypedynamic on every part (Tutorials → Ragdoll Switch). See bundled/scenes/demo-joints.json for a pendulum, chain, and arrow→crate pinTo example.

Archery (stick + damage)

The Arrow prefab (builtin_prefab_arrow) is a CCD dynamic body with baked launch velocity. Its Object Flow listens for rigidBody2d/collisionStart (target group Ragdolls), then pinTo + health.damage. Spawn with spawnBlueprint from a Fire control. Acceptance scene: bundled/scenes/demo-archery.json (one hit → go limp + death punch). Tutorials → Archery (Stick + Damage). Combat Action stays on characters with Stats/Team — not on the projectile.

Skeleton ragdoll (skinned mesh)

Author Physics bone flags per bone in Skeleton Studio (stored as skeleton.ragdoll). Add the Ragdoll component; call Activate (or enable Activate on Death with Health). Bone capsules spawn at the current animated pose, jointed with authored angle limits. The skinned mesh follows via inverse-FK writeback — no renderer changes. Deactivate frees bodies and leaves the limp pose frozen.


Character Controllers + Collider 2D

The character-controller family — Platformer Controller, Top-Down Controller, and Side-Scroller Controller — shares one movement engine. Pick the node that matches your game; only the Platformer Controller adds gravity, jumping, and foot probes.

When Collide With World is enabled on any of them, movement uses Rapier shape casts against other Collider 2D objects.

The character's probe shape is derived from its own Collider 2D (box, circle, capsule, or polygon) including offset and auto-collider geometry. Without a collider, object bounds are used as a fallback.

Recommended stack for platformers / top-down characters:

  1. A character controller (e.g. Platformer Controller) — collideWithWorld: true
  2. Collider 2D — edit with the collider tools / auto-collider
  3. Rigid Body 2D (optional) — use kinematicPosition or fixed, not dynamic

World geometry: Collider 2D on floors and walls.

Foot probes (platformer ground feel)

When Collide With World is on and mode is Platformer, gold foot probes control when the character is considered grounded:

FieldPurpose
Show Foot ProbesDraw probes and draggable handles on canvas
Foot SpreadHalf-width from collider center to each foot (0 = collider width)
Foot InsetPull probes inward from spread edge (0 = auto)
Left / Right Foot XExplicit offsets from collider center (set by dragging handles)
Probe DepthDownward ground-check distance
Probe LiftStart probes above the foot row
Foot Row YMove the whole foot row up/down
Ground Check ModeBoth Feet, Leading Foot When Moving, or Either Foot
Leading Foot SpeedHorizontal speed before leading-foot mode kicks in

Drag the gold handles on the selected character in Arrange to tune ledge hang, foot placement, and probe depth. During preview, active probes brighten based on movement direction.


Collider editing (Arrange workspace)

When an object has Collider 2D attached:

  1. A teal overlay shows the collision shape on the stage (amber dashed when Sensor is on).
  2. Use Edit Collider in the inspector (or press C) to enter collider edit mode.
  3. Drag handles to resize primitives, move the offset, or edit polygon vertices. Edge and corner drags anchor the opposite side by default; hold Shift to resize symmetrically from the collider center.
  4. Auto Collider generates a fit from sprite alpha: polygon trace, box fit, circle fit, or tight bounds.

Polygon colliders are convex only (Rapier limitation). Concave silhouettes are approximated via convex hull or box/circle fit.

Toggle Show all colliders in the Collider 2D inspector to faintly draw overlays on every physics object.


Parented objects

Physics uses world pose (position + rotation in scene space). Child objects with physics components simulate at their world transform.

v1 guidance: Avoid putting a dynamic rigid body on a child whose parent moves every frame (e.g. a dragged group). The sim may fight manual parent motion. Prefer:

  • Physics on root objects, or
  • Kinematic/fixed parents with dynamic children that are not parented to moving transforms.

Lifecycle

PhaseWhat happens
Play startpreparePhysics() loads Rapier WASM and builds bodies from scene objects
Each framestepPhysics() syncs transforms → simulates → writes dynamic bodies back
Play end / editorshutdownPhysics() disposes the world

Only custom scene objects (getCustomObjects()) participate — not built-in layout template things.


Shapes

ShapeNotes
boxDefault; uses width × height
circleRadius from smaller object dimension or Radius field
capsulePill shape; Capsule Height + radius
polygonConvex polygon; edit vertices in collider edit mode

Use Offset X/Y to shift the shape relative to the object pivot.


Troubleshooting

SymptomCheck
Object falls through floorFloor needs Collider 2D; dynamic object needs Collider 2D too
No collision eventsSensor colliders don't block; verify enabled on both components
Object doesn't moveBody type must be dynamic; check gravityScale (0 disables gravity)
Jitter when parent movesSee Parented objects above

PathRole
src/game/physics/runtime.jsSimulation loop, sync, events
src/game/physics/backends/rapier.jsRapier world wrapper
src/game/components/built-in/rigid-body-2d.jsRigid body component
src/game/components/built-in/collider-2d.jsCollider component
src/game/components/built-in/joint-2d.jsJoint component
src/game/scene-editor/joint-editor.jsAnchor drag handles
src/game/scene-editor/collider-editor.jsCanvas collider edit mode
src/game/physics/auto-collider.jsAuto-collider generation
src/game/physics/collider-overlay.jsStage overlay drawing
src/editor-play-session.jspreparePhysics when Play starts (editor)
src/app/bootstrap-runtime.js / frame loopWires play session + per-frame stepPhysics
src/player.jsStandalone player steps physics each frame

See also Built-in reference — Physics.

Opal Engine — MIT licensed