From 51e1252fb962fd53b8e856fac62d34ec092db226 Mon Sep 17 00:00:00 2001 From: Tom Smeding Date: Sun, 18 Feb 2024 13:58:20 +0100 Subject: Angled bounce-back from pad --- game.js | 89 +++++++++++++++++++++++++++++++++++++++++++---------------------- 1 file changed, 59 insertions(+), 30 deletions(-) diff --git a/game.js b/game.js index 08baa51..e76bee7 100644 --- a/game.js +++ b/game.js @@ -18,10 +18,11 @@ function redraw() { redrawFast(); } -function calcBallX() {return bdltX + bdW * (flip ? 1 - ballX : ballX);} -function calcBallY() {return bdltY + bdH * ballY;} -function calcPadY() {return bdltY + bdH * ((flip ? pad2Pos : padPos) - padHeight / 2);} -function calcPad2Y() {return bdltY + bdH * ((flip ? padPos : pad2Pos) - padHeight / 2);} +function calcFlippedX(x) {return bdltX + bdW * (flip ? 1 - x : x);} +function calcY(y) {return bdltY + bdH * y;} + +function calcLeftPadY() {return calcY((flip ? pad2Pos : padPos) - padHeight / 2);} +function calcRightPadY() {return calcY((flip ? padPos : pad2Pos) - padHeight / 2);} function redrawFast() { ctx.strokeStyle = "#ffffff"; @@ -37,9 +38,9 @@ function redrawFast() { if (playing) { ctx.fillStyle = "#ffffff"; ctx.beginPath(); - ctx.arc(calcBallX(), calcBallY(), ballRadius * bdW, 0, 2 * Math.PI); - ctx.rect(bdltX + bdW * padX, calcPadY(), bdW * padWidth, bdH * padHeight); - ctx.rect(bdltX + bdW * (1 - padX - padWidth), calcPad2Y(), bdW * padWidth, bdH * padHeight); + ctx.arc(calcFlippedX(ballX), calcY(ballY), ballRadius * bdW, 0, 2 * Math.PI); + ctx.rect(bdltX + bdW * padX, calcLeftPadY(), bdW * padWidth, bdH * padHeight); + ctx.rect(bdltX + bdW * (1 - padX - padWidth), calcRightPadY(), bdW * padWidth, bdH * padHeight); ctx.fill(); } } @@ -48,13 +49,13 @@ function undrawElements() { ctx.fillStyle = "#000000"; ctx.beginPath(); ctx.rect( - calcBallX() - ballRadius * bdW - 2, calcBallY() - ballRadius * bdW - 2, + calcFlippedX(ballX) - ballRadius * bdW - 2, calcY(ballY) - ballRadius * bdW - 2, 2 * ballRadius * bdW + 4, 2 * ballRadius * bdW + 4); ctx.rect( - bdltX + bdW * padX - 2, calcPadY() - 2, + bdltX + bdW * padX - 2, calcLeftPadY() - 2, bdW * padWidth + 4, bdH * padHeight + 4); ctx.rect( - bdltX + bdW * (1 - padX - padWidth) - 2, calcPad2Y() - 2, + bdltX + bdW * (1 - padX - padWidth) - 2, calcRightPadY() - 2, bdW * padWidth + 4, bdH * padHeight + 4); ctx.fill(); } @@ -231,6 +232,16 @@ function initPhysics() { padVel = pad2Vel = 0; } +// -1 <= frac <= 1 +function padHitAdjustCurve(frac) { + // ✨ + const a = 0.8, b = 0.12; + const alpha = b / a**3; + const beta = (1 - alpha) / (1 - a)**2; + const x = Math.abs(frac); + return Math.sign(frac) * (alpha * x**3 + (x > a) * beta * (x - a)**2); +} + // Returns {bounce, otherBounce, ourSide : Bool} function advancePhysics(deltaT) { const ret = {bounce: false, otherBounce: false, ourSide: false}; @@ -240,33 +251,51 @@ function advancePhysics(deltaT) { const newpadPos = padPos + padVel * deltaT; const newpad2Pos = pad2Pos + pad2Vel * deltaT; - let t; if (newballY <= ballRadius) { - t = (ballRadius - ballY) / ballVY; + const t = (ballRadius - ballY) / ballVY; + newballY = ballY + ballVY * t + (-ballVY) * (deltaT - t); + ballVY = -ballVY; } else if (newballY >= 1 - ballRadius) { - t = (1 - ballRadius - ballY) / ballVY; - } else { - t = -1; - } - if (t >= 0) { - newballY = ballY + ballVY * (2 * t - deltaT); + const t = (1 - ballRadius - ballY) / ballVY; + newballY = ballY + ballVY * t + (-ballVY) * (deltaT - t); ballVY = -ballVY; } - if (newballX <= padX + padWidth + ballRadius && - Math.abs(newballY - padPos) < padHeight / 2 + ballRadius) { - t = (padX + padWidth + ballRadius - ballX) / ballVX; + function bounceLeft(x, y, vx, vy, pady) { + if (x + vx * deltaT > padX + padWidth + ballRadius) return null; + + const t = (padX + padWidth + ballRadius - x) / vx; + if (Math.abs((y + t * vy) - pady) >= padHeight / 2 + ballRadius) return null; + + const frac = ((y + t * vy) - pady) / (padHeight / 2 + ballRadius); + const angAdj = Math.PI / 4 * padHitAdjustCurve(frac); + const curAng = Math.atan2(vy, -vx); // normal outgoing angle + const newAng = Math.max(-0.45 * Math.PI, Math.min(0.45 * Math.PI, curAng + angAdj)); + const speed = Math.hypot(vx, vy); + const newvx = speed * Math.cos(newAng); + const newvy = speed * Math.sin(newAng); + const newx = x + vx * t + newvx * (deltaT - t); + const newy = y + vy * t + newvy * (deltaT - t); + + return [newx, newy, newvx, newvy]; + } + + let bounce = bounceLeft(ballX, ballY, ballVX, ballVY, padPos); + if (bounce != null) { + newballX = bounce[0]; + newballY = bounce[1]; + ballVX = bounce[2]; + ballVY = bounce[3]; ret.bounce = true; - } else if (newballX >= 1 - (padX + padWidth + ballRadius) && - Math.abs(newballY - pad2Pos) < padHeight / 2 + ballRadius) { - t = (1 - (padX + padWidth + ballRadius) - ballX) / ballVX; - ret.otherBounce = true; - } else { - t = -1; } - if (t >= 0) { - newballX = ballX + ballVX * (2 * t - deltaT); - ballVX = -ballVX; + + bounce = bounceLeft(1 - ballX, ballY, -ballVX, ballVY, pad2Pos); + if (bounce != null) { + newballX = 1 - bounce[0]; + newballY = bounce[1]; + ballVX = -bounce[2]; + ballVY = bounce[3]; + ret.otherBounce = true; } // If the ball has come to our side, send a ballvec -- cgit v1.2.3-54-g00ecf