White landing ring: closed-form prediction kills the jitter
predictLanding stepped the trajectory in 10ms chunks and returned the first point past ground level — an overshoot of up to ~25cm that varied per frame, which is what actually jittered (the earlier pixel-snap only addressed sub-pixel shimmer). The landing point of a parabola has an exact solution; computing it directly makes the prediction identical every frame. Measured: max frame-to-frame movement 5.5mm, was ~25cm. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
0bf6937121
commit
f2bf21f252
1 file changed
+7
-9
@@ -579,14 +579,12 @@ function update(dt) {
|
|||||||
if (phase === 'over') { overT -= dt; if (overT <= 0) setupPoint(); }
|
if (phase === 'over') { overT -= dt; if (overT <= 0) setupPoint(); }
|
||||||
}
|
}
|
||||||
|
|
||||||
function predictLanding() { // first-bounce point of the ball's current trajectory
|
function predictLanding() { // first-bounce point, closed form — exact, so the ring never jitters
|
||||||
let { x, y, z, vx, vy, vz } = ball;
|
const g = ball.g || TUNE.gravity;
|
||||||
const g = ball.g || TUNE.gravity, dt = 0.01;
|
const disc = ball.vy * ball.vy + 2 * g * ball.y;
|
||||||
for (let i = 0; i < 300; i++) {
|
if (disc < 0) return null;
|
||||||
vy -= g * dt; x += vx * dt; y += vy * dt; z += vz * dt;
|
const t = (ball.vy + Math.sqrt(disc)) / g; // time until y = 0
|
||||||
if (y <= 0) return { x, z };
|
return { x: ball.x + ball.vx * t, z: ball.z + ball.vz * t };
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------------- render ----
|
// ---------------------------------------------------------------- render ----
|
||||||
@@ -739,7 +737,7 @@ function render() {
|
|||||||
// server idly bouncing the ball while waiting
|
// server idly bouncing the ball while waiting
|
||||||
if (phase === 'pretoss') {
|
if (phase === 'pretoss') {
|
||||||
const sv = score.server === 'P' ? P : O;
|
const sv = score.server === 'P' ? P : O;
|
||||||
const bx = sv.x - 0.5 * (sv.side === 'P' ? 1 : -1), by = Math.abs(Math.sin(idleT * 3.6)) * 0.7; // floor to the reaching hand
|
const bx = sv.x - 0.5 * (sv.side === 'P' ? 1 : -1), by = Math.abs(Math.sin(idleT * 3.6)) * 0.55; // floor to the reaching hand
|
||||||
const sh = proj(bx, 0, sv.z);
|
const sh = proj(bx, 0, sv.z);
|
||||||
ctx.fillStyle = 'rgba(0,0,0,0.3)';
|
ctx.fillStyle = 'rgba(0,0,0,0.3)';
|
||||||
ctx.beginPath(); ctx.ellipse(sh.x, sh.y, Math.max(1.5, sh.s * 0.08), Math.max(1, sh.s * 0.035), 0, 0, 7); ctx.fill();
|
ctx.beginPath(); ctx.ellipse(sh.x, sh.y, Math.max(1.5, sh.s * 0.08), Math.max(1, sh.s * 0.035), 0, 0, 7); ctx.fill();
|
||||||
|
|||||||
Reference in new issue
Block a user