Skip to content

Physics and collisions ​

Physics lets objects collide, fall, slide, trigger areas, and respond to forces. Opal's 2D physics runtime is Rapier when available, with a shared inspector and Flow events on top.

Core components ​

Physics usually starts with two components:

ComponentPurposeWhen to add it
Collider 2DDefines the collision shapeAlways — without it nothing collides
Rigid Body 2DDefines how it moves in the physics worldWhen it should fall, be pushed, or be kinematic

An object can have a collider without a rigid body when it should act as a static obstacle or sensor. Moving physical objects usually need both.

Body types ​

Body typeMoves howUse for
DynamicGravity, collisions, impulses, forcesCrates, balls, loose enemies, ragdolls
Fixed / StaticNever moves — infinite massWalls, floors, platforms
KinematicMoves only when you set position/velocityPlatforms on a track, player controllers that still collide

Fixed walls are cheapest. Kinematic platforms are the middle ground — script-driven but still participate in contacts.

Collider shapes ​

ShapeGood forTip
BoxPlatforms, walls, rectangular propsEnlarge 1–2 px beyond the sprite for forgiving jumps
CircleBalls, round pickups, radial trigger zonesCheapest continuous collision
CapsuleCharacters and enemiesRounded bottom avoids snagging on tile seams
Polygon / customIrregular objectsKeep vertex count low — simple beats pixel-perfect

A visually detailed sprite usually plays better with a simple collider than one that traces every pixel.

Click Edit Shape or press C in Arrange to tune the shape directly. Square handles resize shapes or move polygon vertices; polygon edge diamonds insert new vertices, and Alt-click removes them. The center crosshair moves the collider without moving the artwork. Hold Shift for centered box/capsule resizing. Escape cancels the current drag, and each completed gesture has one undo step. The Tools menu contains fitting, centering, and matching actions. See the physics authoring tools for the full controls.

Use Solid, Ice, and Rubber contact presets to try surface responses. Rigid Body 2D provides Dynamic, Upright, Floating, Platform, and Fixed motion presets. The selected body's viewport guide shows its launch direction, speed, spin, and constraints before you press Play.

Pouring and floating liquid ​

Choose Help → Fluid Playground… to import the experimental liquid example as a new scene. Press Play to try the moving gate, add water, and reset the tank. The crate floats while the denser weight sinks; the scene also works in an exported game.

To make your own, build a container with solid colliders and place a Fluid 2D object inside. Its rectangle sets the initial water fill, so the walls must be separate objects. Add a Fluid Emitter 2D to a nozzle and select that Fluid 2D object as its Fluid Domain. A Fluid Drain 2D removes water inside its rectangle. Several emitters can feed the same domain.

The hierarchy create menu includes Water Fill, Empty Fluid Domain, Emitter, Drain and Probe presets. A Fluid Probe detects entering and leaving water and exposes local flow for gameplay.

Start with the example's small particle count. Smaller Particle Spacing and higher Maximum Iterations cost more time. Show Particles and Show Velocity help inspect the simulation. This experimental version supports solid rigid colliders; soft-body fluid coupling and networked liquid are not available. See Experimental liquid for controls, actions, and limits.

Sensors and triggers ​

A sensor detects overlap without physically blocking movement. Flip Sensor on the Collider 2D.

Use sensors for:

  • Collectible pickup radius
  • Door or portal trigger
  • Enemy sight range
  • Damage zones
  • UI-like world interaction areas

Sensor events are handled in Object Flow or Scene Flow — pick the scope that owns the logic (one coin vs the whole level).

Collision events in Flow ​

Physics events drive graphs. Attach the needed Collider 2D and Rigid Body 2D before expecting events to fire.

PatternFlow eventExample
Collision startsOn Collision StartPlayer touches hazard → health.damage
Collision endsOn Collision EndPlayer leaves ladder / water → stop climb / splash sound
Sensor overlapCollision Start / Collision End with Sensor enabledPickup coin, open prompt, trigger dialog
Hit responseCondition + actionPlay sound, spawn VFX, destroy projectile

For sticky arrows / bolts, add Projectile on top of a dynamic Rigid Body 2D (enable Continuous Collision) and Collider 2D — it aligns to velocity and can weld into what it hits. See Built-in reference — Projectile.

Physics tuning ​

FieldWhat it changesStart here
Gravity ScaleHow strongly global gravity pulls this body1.0 default, 0 for floaters
FrictionHow much surfaces slow sliding0.7 default, 0 ice, 1.0 sandpaper
RestitutionBounce — 0 dead stop, 1 perfect bounce0.0 for characters, 0.6 for balls
Density / Additional MassHow strongly forces/impulses move itDensity is mass-per-area on the Collider; Additional Mass on the Rigid Body adds extra on top — small tweaks there are cheapest

Change one field at a time and re-test in Play mode.

Four tuning recipes ​

Recipe 1 · Pushable crateDynamic body
Add to crateSetting
Rigid Body 2DBody Type Dynamic, Gravity Scale 1.0
Collider 2DShape Box, Sensor off, Friction 0.7 (default)
(optional)Additional Mass 2 so the player can still shove it

Push it with a Top-Down controller or a Projectile impulse. If it slides forever, raise friction to 0.8. If the player can't push it, lower mass or raise controller push force.

Recipe 2 · Sensor coin that vanishesSensor + Flow
Add to coinSetting
Collider 2DShape Circle, Sensor on, Radius = sprite + 6 px
No rigid bodyStatic trigger — no gravity needed

Object Flow on the coin:

text
On Sensor Enter (other is player) → Add Counter score +1 → Play Audio "coin.wav" → Spawn VFX sparkle → Destroy

If it never fires, the player needs its own Collider 2D (even when moved by a controller, the body still needs a shape).

Recipe 3 · Moving platformKinematic body
Add to platformSetting
Rigid Body 2DBody Type Kinematic
Collider 2DShape Box, Sensor off

Drive it from Flow or a Behavior Component: use a Kinematic body and move it with a Behavior Component's onTick: { drift: { x: "80", y: "0" } } (or a Flow loop that sets velocity/position each frame); reverse direction at endpoints. Characters with Dynamic bodies will ride it. Set the platform Collision Groups so enemies fall through if you want. A true one-way (jump-through) platform is a separate setup — this recipe is a moving solid platform.

Recipe 4 · Sticky projectile (arrow / bolt)Projectile + CCD
Add to arrowSetting
Rigid Body 2DBody Type Dynamic, Continuous Collision on
Collider 2DShape Box thin, Sensor off
ProjectileArm Delay 0.08, Ignore Owner on

Flow on the arrow: On Spawn → Apply Impulse forward. On hit the Projectile welds into the target (creating a Joint) and stops simulating. If it never welds: CCD was off (fast arrow tunnels), Arm Delay not elapsed, or Ignore Owner filtered the target.

Connect objects with joints ​

Build mechanisms from objects with Rigid Body 2D, Collider 2D, and Joint 2D components. Group the objects under a parent and save them as a Prefab to reuse the mechanism with fresh joint references.

Select two objects in the hierarchy or on stage, then open Connect objects… in the inspector. Choose Hinge, Limited hinge, Fixed, or Distance. The last selected object owns the new joint. Missing dynamic bodies are added in the same undo step. Add colliders to objects that should hit the world. If the active object already has a joint, make the unconnected object active first.

Hinges place the anchor at the active object's center. Limited hinges and fixed connections meet between the two centers; distance connections retain separate anchors. Limited hinges start with up to 60° of movement either side of the current relative angle, clipped at −180°/180°. At an exact half turn, limits start unrestricted to keep the initial pose stable. Tune these in Joint 2D.

The optional Joint tools disclosure contains presets and Align target anchor. Presets preserve targets, anchors, and break settings. Align matches the anchors without moving either object. Drag aligned hinge or fixed anchors together on stage; Shift-drag moves one independently and Escape cancels. Distance endpoints move independently. Break Separation measures pixels, not impact force. Use Break Joint for gameplay-driven detachment.

For a powered hinge, set Motor Speed and Motor Force on Joint 2D, or use Set Motor. Zero speed with positive force brakes; zero force releases it. For input-driven control, use Add Component → Joint Motor Controller and configure its inputs. Speed is relative to the target body; reverse its sign to change direction. To control it from game logic, choose Controlled By → Flow / Script, then use Set Drive or GetComponent<JointMotorController>().SetDrive(value). Values range from -1 through 0 to 1, including partial analog input.

Character movement — controllers over manual forces ​

For platformers or top-down games, prefer a controller component — it packages gravity, ground detection, acceleration, and collision so you don't hand-build them in graphs.

ControllerUse when
Side-Scroller Controller2.5D run along X with jump
Platformer ControllerGrounded jumps, coyote time, ledges
Top-Down Controller8-way walk, no gravity

Use graphs to set intent (direction, jump, attack, interact). Let the controller handle movement math. For touch, see Input and controls — virtualInput patterns.

Debugging collisions ​

If collisions are not working, run this in order:

  1. Both objects have appropriate colliders (at least one not a sensor if you want a block).
  2. Moving physical objects have a Rigid Body 2D.
  3. Sensor vs blocker matches the event you listen for.
  4. Collider size and offset match the sprite — select the object and toggle C (collider shape editor).
  5. Collision groups aren't filtering each other out.
  6. Flow overlay in Play mode — does On Collision Start flash? If not, the physics pair never contacted.

For sticky-projectile bugs specifically: CCD on, Arm Delay elapsed, Ignore Owner correct — see Troubleshooting.