Splatter: chevrons in perspective, and a smash that actually flies
The chevrons were stepped along the SCREEN line at a fixed size, so they read as a flat sticker over a 3D court. They now step through world space and take their size from their own projection scale — near ones roughly twice the far ones — and they fade IN toward the target so the eye is walked up the lane instead of being handed a uniform stripe. The arrowhead scales with the target's own depth too. (proj is a true projective map, so the screen direction stays constant along the line; only size and alpha needed to vary.) Hard shots now fly hard: serve pace is weighted onto quality (19 base + 22×q, so 21 -> 41 m/s) instead of 21 -> 28. Two things had to give way for it. Serve topspin was a fixed 1 + 0.35q, which meant a fast flat ball could not both clear the tape and land on a target, so the net-loft loop throttled every smash back to walking pace — measured 21.8 m/s whether you drew 60px or 95px. TUNE.serveSpin 1.3 lets it leave flat and DIVE, which is what a smash is. And the loft loop's margin was doHit's quality cushion, which taxed exactly the shots meant to hurt. It's now sized to the real error it exists to cover: the sim samples the net plane once a frame, so a diving ball can test below the tape when the closed form clears it. Margin = descent rate × the dt clamp. A flat drive pays almost nothing, a lob pays what it must. Measured, target 20m out: 24.7 / 32.2 / 41 m/s for weak / mid / full draw. At the net it's 9.6 / 11.2 / 11.6 — still the slow floaty lob, as it should be. Ball-vs-shambler contact gets a crude sweep (radius + half a frame's travel), because a 41 m/s ball steps 0.7m per frame at 60fps and 1.6m at main.js's dt clamp, both wider than a shambler. Verified the full smash connects at both. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
f32cac6a70
commit
f3dcf0b16f
5 files changed
+45
-21
No files matched your search
+10
-2
@@ -195,6 +195,13 @@ export function updateZombies(dt) {
|
||||
}
|
||||
}
|
||||
|
||||
// ponytail: crude swept collision — grow the contact window by half the distance the ball
|
||||
// covers this frame. A full-draw smash moves ~0.7 m per frame (1.6 m on a 25 fps device, where
|
||||
// main.js clamps dt), which is wider than a shambler, so a discrete point test tunnels straight
|
||||
// through bodies. Upgrade to a real segment-vs-circle test if the fudge ever reads as a phantom
|
||||
// hit; at these speeds and radii it doesn't.
|
||||
const ballSweep = ball.active ? Math.hypot(ball.vx, ball.vz) * dt * 0.5 : 0;
|
||||
|
||||
for (let i = zombies.length - 1; i >= 0; i--) {
|
||||
const zb = zombies[i];
|
||||
if (!zb) continue; // a bomb detonation splices several out mid-loop and can gut the array under us
|
||||
@@ -230,7 +237,7 @@ export function updateZombies(dt) {
|
||||
}
|
||||
if (ball.active && ball.lastHit === 'P' && ball.y < 1.9) {
|
||||
const d = Math.hypot(ball.x - zb.x, ball.z - zb.z);
|
||||
if (d < (zb.variant === 'g' ? 0.75 : 0.55)) { // the brute is a bigger target
|
||||
if (d < (zb.variant === 'g' ? 0.75 : 0.55) + ballSweep) { // the brute is a bigger target
|
||||
if (ball.bomb) { // detonate on first contact, whatever the pace
|
||||
explode(ball.x, ball.z); ball.bomb = false;
|
||||
spentBall(); break; // zombies mutated — stop this frame's loop
|
||||
@@ -311,7 +318,8 @@ export function updateZombies(dt) {
|
||||
if (!dead && e.y < 1.9) for (let j = zombies.length - 1; j >= 0; j--) {
|
||||
const zb = zombies[j];
|
||||
if (!zb || zb.variant === 'r') continue; // side balls pass the boss; !zb: kills can sweep mid-loop
|
||||
if (Math.hypot(zb.x - e.x, zb.z - e.z) < (zb.variant === 'g' ? 0.75 : 0.55)) {
|
||||
if (Math.hypot(zb.x - e.x, zb.z - e.z) < (zb.variant === 'g' ? 0.75 : 0.55)
|
||||
+ Math.hypot(e.vx, e.vz) * dt * 0.5) { // same sweep: clones inherit the smash pace
|
||||
if (zb.variant === 's' && e.vz < 0 && !(zb.shieldT > 0)) { zb.shieldT = TUNE.shieldDown; pock(0.9, 1800); } // chair blocks these too
|
||||
else if (e.pow >= TUNE.zSplatQ || zb.variant === 'f') {
|
||||
if (zb.hp > 1) { zb.hp--; splat(0.4); } // wounds brutes AND shamblers
|
||||
|
||||
Reference in new issue
Block a user