Splatter: one gesture always, a serve that clears the net, an aim camera
Three things from the phone test. The drag meant two different things depending on invisible state: aiming from pretoss, but walking the player around while the last ball was still in the air. That "mode between the hits" read as a bug, so it's gone — canAimServe now covers every phase of splatter, and firing over a live ball is allowed again (the old one flies on as a ballistic clone). That was the mid-rally re-toss that used to be the spam vector, and it's safe now because the brake moved into the pull: a rhythm of lazy 30px pulls fires 100 shots in 30s and kills NOTHING (the court fills up instead), while 85 committed full pulls kill 11. hitServe never had doHit's net-clearance loop, so every shot at a shambler hugging the tape died in the net — not balance, a missing loop. Both paths now trade pace for arc until the ball is genuinely over, and the ceiling goes 8 -> 12 tries because a target right behind the net needs a near-vertical drop (measured: clears the tape by 0.20m and connects). The trade is real: the loft arrives slower and the walk-lead was computed for the original pace, so a lob is honestly the less reliable shot. And the camera now rises and leans back a little while the slingshot is drawn. That steeper look is what makes the gap between the net and the bodies behind it readable — the shot that used to be blind. Eased once per frame before anything projects, since one sheared frame would be visible. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
ff973cf6bb
commit
b61004a18d
4 files changed
+42
-7
No files matched your search
@@ -55,9 +55,14 @@ export function fitViewport() { // landscape 480x270, portrait 270x480 with a lo
|
||||
}
|
||||
addEventListener('resize', fitViewport);
|
||||
|
||||
// The aim camera: while the slingshot is drawn the view rises and leans back a little, which
|
||||
// steepens the look down the court. That extra depth is what makes a shambler standing right
|
||||
// behind the net a shot you can actually judge. Eased once per frame in render(), never mid-frame
|
||||
// — every proj() call within one frame must share the same camera or the scene shears.
|
||||
let aimCam = 0;
|
||||
export function proj(x, y, z) {
|
||||
const d = TUNE.camZ - z, s = TUNE.camF / d;
|
||||
return { x: W / 2 + x * s, y: TUNE.horizon + (TUNE.camY - y) * s, s };
|
||||
const d = TUNE.camZ + aimCam * TUNE.aimCamZ - z, s = TUNE.camF / d;
|
||||
return { x: W / 2 + x * s, y: TUNE.horizon + (TUNE.camY + aimCam * TUNE.aimCamY - y) * s, s };
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------- sprites ----
|
||||
@@ -330,6 +335,8 @@ function drawCard(title, sub1, sub2, color) {
|
||||
// ------------------------------------------------------------------ frame ----
|
||||
export function render() {
|
||||
ctx.setTransform(RES, 0, 0, RES, 0, 0); // logical W x H coordinates at RES density
|
||||
// ease the aim camera FIRST, before anything projects — one camera per frame (see proj)
|
||||
aimCam += ((G.aim ? 1 : 0) - aimCam) * 0.15;
|
||||
if (G.shake) { // bomb kick: jitter the whole world (transform is reset before the HUD)
|
||||
const m = G.shake.mag * G.shake.t / 0.35;
|
||||
ctx.translate((Math.random() * 2 - 1) * m, (Math.random() * 2 - 1) * m);
|
||||
|
||||
Reference in new issue
Block a user