Skip to content

Instantly share code, notes, and snippets.

@jakeklassen
Last active June 13, 2020 01:20
Show Gist options
  • Save jakeklassen/42f9a9ff5342353d6dad0b45c61996b9 to your computer and use it in GitHub Desktop.
Save jakeklassen/42f9a9ff5342353d6dad0b45c61996b9 to your computer and use it in GitHub Desktop.
Breakout game
// @ts-check
const canvas = document.querySelector("canvas");
const ctx = canvas.getContext("2d");
// Utility Functions
const clamp = (value, min, max) => Math.min(Math.max(value, min), max);
// https://developer.mozilla.org/kab/docs/Games/Techniques/2D_collision_detection
// Axis-Aligned Bounding Box - no rotation
const intersects = (rect1, rect2) =>
rect1.left < rect2.right &&
rect1.right > rect2.left &&
rect1.top < rect2.bottom &&
rect1.bottom > rect2.top;
const rectangleMixin = {
get left() {
return this.x;
},
get right() {
return this.x + this.width;
},
get top() {
return this.y;
},
get bottom() {
return this.y + this.height;
},
};
const mouse = {
x: 0,
};
const ball = {
x: canvas.width / 2,
y: canvas.height / 2,
width: 12,
height: 12,
vx: 300,
vy: 180,
};
Object.setPrototypeOf(ball, rectangleMixin);
const paddle = {
x: canvas.width / 2 - 104 / 2,
y: canvas.height - 32,
width: 104,
height: 16,
};
Object.setPrototypeOf(paddle, rectangleMixin);
let dt = 0;
let last = performance.now();
function frame(hrt) {
dt = (hrt - last) / 1000;
// Position paddle based on mouse
paddle.x = mouse.x;
let ballCollisionHandled = false;
ball.x += ball.vx * dt;
if (intersects(ball, paddle)) {
ballCollisionHandled = true;
const closestSide =
Math.abs(paddle.right - ball.x) < Math.abs(paddle.left - ball.right)
? "right"
: "left";
if (ball.vx > 0) {
if (closestSide === "right") {
ball.x = paddle.right;
} else {
ball.x = paddle.left - ball.width;
ball.vx = -ball.vx;
}
} else if (ball.vx < 0) {
if (closestSide === "left") {
ball.x = paddle.left - ball.width;
} else {
ball.x = paddle.right;
ball.vx = -ball.vx;
}
}
}
ball.y += ball.vy * dt;
if (!ballCollisionHandled && intersects(ball, paddle)) {
ball.y = paddle.top - ball.height;
ball.vy = -ball.vy;
}
if (ball.right > canvas.width) {
ball.x = canvas.width - ball.width;
ball.vx = -ball.vx;
} else if (ball.x < 0) {
ball.x = 0;
ball.vx = -ball.vx;
}
if (ball.bottom > canvas.height) {
ball.y = canvas.height - ball.height;
ball.vy = -ball.vy;
} else if (ball.y < 0) {
ball.y = 0;
ball.vy = -ball.vy;
}
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.fillStyle = "white";
ctx.fillRect(paddle.x, paddle.y, paddle.width, paddle.height);
ctx.fillRect(ball.x, ball.y, ball.width, ball.height);
last = hrt;
requestAnimationFrame(frame);
}
function mouseMoveHandler(e) {
mouse.x += e.movementX;
mouse.x = clamp(mouse.x, 0, canvas.width - paddle.width);
}
function pointerLockChange() {
if (document.pointerLockElement === canvas) {
document.addEventListener("mousemove", mouseMoveHandler, false);
} else {
document.removeEventListener("mousemove", mouseMoveHandler, false);
}
}
canvas.addEventListener("click", () => canvas.requestPointerLock(), false);
document.addEventListener("pointerlockchange", pointerLockChange, false);
requestAnimationFrame(frame);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment