aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTom Smeding <tom@tomsmeding.com>2024-02-18 13:58:20 +0100
committerTom Smeding <tom@tomsmeding.com>2024-02-18 14:03:16 +0100
commit51e1252fb962fd53b8e856fac62d34ec092db226 (patch)
tree439e3eb80cc27f9be8c39f5e092e2d5a7a67fd19
parent76f9e9d173c61c41e3072934aafe20612838c4df (diff)
Angled bounce-back from pad
-rw-r--r--game.js89
1 files 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