Cut them with the laser plane instead of painting over them

The beam is a real plane now, holding TUNE.beamY (1.15 m) all the way out instead
of sloping to the ground. That one change is what makes the rest possible: sloping,
it lay flat on the grass across the whole far half, so there was nothing to cut
exactly where the crowd stands.

One flat translucent triangle, apex at the player's CHEST, opening out down the
court with the alpha falling away. It was four nested wedges composited 'lighter'
and that layering WAS the banding -- additive light blowing out to white where the
layers overlapped. Flat alpha also means a body under the plane is tinted rather
than brightened, which is what lets the cut read at all.

Bodies are sliced by it: the part above the plane is redrawn clipped so it sits on
top, the part below stays tinted underneath, and it happens while AIMING as well as
on the shot. Measured, the plane takes a shambler at 64% of its height, a brute at
43% and the roller man at 36% -- bigger bodies visibly take it lower.

The cut line follows the SPRITE rather than a rectangle. A fillRect spanned the
body's bounding width, so it crossed the gaps between the legs and under a held-out
arm and read as a bar lying behind him; the line is the sprite itself, redrawn
clipped to a thin band and flattened to one colour by ctx.filter. It thins with
distance on s squared, because this camera's scale only spans about 1.5x across the
court and a linear falloff was invisible.

Aiming a filter at a specific colour turned out to be a measurement, not a guess:
brightness(0) invert(1) goes to WHITE, and sepia adds almost no chroma to white, so
the first chain rendered a grey. Leaving a mid grey for sepia to tint lands on
(32,154,255) against the laser icon's (29,153,232). The probe fills a rect and reads
the pixel back.

The pass is bounded to the eight nearest bodies above 14 px, because it draws each
one twice more through a filter and runs every frame you are aiming. 0.27 ms/frame
with 20 bodies on desktop, against a 16.7 ms budget.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
wuwiandClaude Opus 5 committed 2026-08-03 15:48:08 +02:00
1 parent da9cca2f23
commit 337def8526
5 files changed
+176 -60

No files matched your search

+9
View File
@@ -243,6 +243,15 @@ export const TUNE = {
beamLen: 34, // m it reaches. Was 26, which stopped at the far baseline and read as the beam
// being CUT OFF in mid-court; the player stands at z~24, so this carries it past
// the baseline and up into the courtside ads, which is where light should stop.
beamY: 1.15, // m — the height the plane HOLDS down the court, and the line it cuts bodies at.
// It used to slope to the ground by about 20 m out, which meant that across the
// whole far half it lay flat on the grass: nothing to cut, and the split would
// have been invisible exactly where the crowd stands. Chest on a shambler, belly
// on a brute, thigh on the roller man — so a bigger body visibly takes it lower.
beamOriginY: 1.25,// m — it leaves the PLAYER at chest height, not the racket head. Emitted from
// the racket (2.2 m) the apex hung in the air above him and read as detached;
// out of his chest, level with the plane it becomes, it reads as HIS shot.
beamRise: 0.14, // fraction of the length it takes to settle from the origin to that height
beamW0: 0.06, // m half-width at the racket — a POINT. At 0.35 it left the racket as a 0.7 m
// bar hanging in the air rather than as something emitted from the strings.
beamW1: 5.0, // ...and at the far end. It is a CONE: the spread is the point of it