2D water

Add water_2d to an entity. Its transform position is the centre of a box, in Y-up world units. The top is position.y + size.y / 2. Rotation and scale do not change the region; change size or shape.

entity Lagoon {
  transform { position (800, 230, 0) }
  water_2d { size (1100, 210, 0) color (0.08,0.62,0.72) depth_color (0.025,0.13,0.29)
    opacity 0.52 distortion 2 foam 3 columns 96 tension 90 damping 5 spread 180 idle 1.5
    density 1 drag 3 flow (18,0,0) layer 30 }
}

The surface is a row of springs. An impact travels through neighbours and loses energy through damping. Idle waves are separate, so setting idle to zero lets the disturbed surface settle completely. Columns are bounded to 8–256. Tension is 1–400, damping 0–40, spread 0–600. Simulation substeps are at most 1/120 second.

For a shaped shore, use shape ["-400,100","400,100","300,-100","-300,-100"] instead of size. The convex hull is used, just like body_2d shapes. Keep a horizontal top edge for an open surface. Polygon sides and bottoms constrain both drawing and submerged volume. Concave pools need separate bodies.

Water draws above things it covers: set its layer higher than swimmers, crates and the background, and lower than foreground banks and HUD. Both Metal and GL use the same strip geometry and material fields. The water material blends from surface colour to depth colour, adds a foam line, and distorts the finished picture inside its silhouette. This is a screen-space refraction approximation, not ray tracing; it cannot reveal hidden art. Nearby enabled light_2d lamps make broken glints along the surface (at most 16 lamps per water body).

Bodies and controls

body_2d { kind "dynamic" size (66,66,0) solid true mass 2 density 0.55 } floats with 55% of its area submerged in density-1 water. Density above 1 sinks. Gravity and buoyancy share the same gravity constant in water; air jump and fall multipliers do not distort floating depth. Exponential drag draws velocity toward flow. Mass sets splash strength; density sets displacement. These are independent tuning fields in this 2D model.

Player bodies swim with left/right and Up or Space / Down. swim_speed controls their speed (0 disables swimming). Top-down bodies use their normal directions with water drag and current; they have no vertical gravity, matching the rest of a top-down world. Ordinary collision walls still stop swimmers and floating boxes.

breath 12 gives a body twelve seconds of air while its head is submerged. Default 0 disables the timer. Scripts receive onMessage(self, 'breath', {remaining: 0, exhausted: 1}) once on exhaustion, and another with exhausted 0 when the head clears the surface. No automatic damage: the game decides what exhaustion does. Animation states can use swimming and submerged parameters. Automatic animation picks swim, then climb, then idle while wet. self.body exposes water (entity id or -1), submerged (0–1), swimming, and breath (seconds left).

Entry and exit send water_enter / water_exit to the moving body with water, speed, mass, x, y. Each transition makes a ripple and a bounded burst of droplets. Movement through the surface makes a wake. There are at most 192 live droplets per water region; they do not allocate scene entities.

Rivers and waterfalls

Flow is a velocity in units per second: flow (120,0,0) is a rightward current. Drag controls how strongly it carries bodies. Use waterfall true flow (0,-280,0) on a tall narrow region for animated falling ribbons, refraction and mist at its foot. A waterfall pushes with flow but has no buoyancy or horizontal foam surface. Put a regular water body below it for the plunge pool.

Agent workflow and sample

water_2d {name: "Lagoon", position: [800,230], fields: "size (1100,210,0) flow (18,0,0)"} creates water. Use entity instead of name to edit it. Edits are undoable and respect entity locks. Normal edit_entity, scene text and script setField also work.

Open Water_Lagoon in the Sandbox project. Walk off the left bank, splash down, dive and surface, push the floating crates, swim past the waterfall and touch the gold marker on the right bank. PlatformBot2D uses real swim keys while submerged. The sample scripts count entries, exits and breath exhaustion in game.state.

Acceptance results, exact commands, measured costs and engine captures are recorded in root STATUS.md and Lab/water. A CPU timing alone is not the complete frame cost: compare water-on/off GPU and CPU timings at the same output size after shaders have warmed up.

The acceptance harness uses two 120 Hz physics ticks per 60 Hz frame. It warms up twenty pairs, then measures 120 pairs with alternating water-on/off order. The report adds the CPU and GPU p95 deltas for the 0.5 ms gate. Paired stills isolate foam, refraction, glints and the waterfall as well as the full water body. The bot test saves its controls as Water_Lagoon.ttrreplay alongside the captures when it runs.

Arcwright Beta 2 (0.4.0) · generated from the engine source (25e375c86)Something wrong or missing? Tell us
Download Arcwright free