Add Pong game
This commit is contained in:
+146
@@ -0,0 +1,146 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<title>Pong</title>
|
||||||
|
<style>
|
||||||
|
* { margin: 0; padding: 0; box-sizing: border-box; }
|
||||||
|
body { background: #111; display: flex; flex-direction: column; align-items: center; justify-content: center; height: 100vh; font-family: monospace; color: #fff; }
|
||||||
|
canvas { border: 2px solid #fff; display: block; }
|
||||||
|
#info { margin-top: 12px; font-size: 14px; color: #aaa; }
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<canvas id="c" width="800" height="500"></canvas>
|
||||||
|
<div id="info">W/S — Left Paddle | ↑/↓ — Right Paddle | Space — Pause</div>
|
||||||
|
<script>
|
||||||
|
const canvas = document.getElementById('c');
|
||||||
|
const ctx = canvas.getContext('2d');
|
||||||
|
const W = canvas.width, H = canvas.height;
|
||||||
|
|
||||||
|
const PADDLE_W = 12, PADDLE_H = 90, SPEED = 5, BALL_SIZE = 10;
|
||||||
|
|
||||||
|
const state = {
|
||||||
|
paused: false,
|
||||||
|
scores: [0, 0],
|
||||||
|
left: { y: H/2 - PADDLE_H/2, dy: 0 },
|
||||||
|
right: { y: H/2 - PADDLE_H/2, dy: 0 },
|
||||||
|
ball: { x: W/2, y: H/2, dx: 4, dy: 3 },
|
||||||
|
};
|
||||||
|
|
||||||
|
const keys = {};
|
||||||
|
document.addEventListener('keydown', e => {
|
||||||
|
keys[e.key] = true;
|
||||||
|
if (e.key === ' ') { e.preventDefault(); state.paused = !state.paused; }
|
||||||
|
});
|
||||||
|
document.addEventListener('keyup', e => keys[e.key] = false);
|
||||||
|
|
||||||
|
function resetBall(dir = 1) {
|
||||||
|
state.ball = { x: W/2, y: H/2, dx: 4 * dir, dy: (Math.random() * 4 + 2) * (Math.random() < 0.5 ? 1 : -1) };
|
||||||
|
}
|
||||||
|
|
||||||
|
function update() {
|
||||||
|
if (state.paused) return;
|
||||||
|
|
||||||
|
// Paddle movement
|
||||||
|
if (keys['w'] || keys['W']) state.left.y -= SPEED;
|
||||||
|
if (keys['s'] || keys['S']) state.left.y += SPEED;
|
||||||
|
if (keys['ArrowUp']) state.right.y -= SPEED;
|
||||||
|
if (keys['ArrowDown']) state.right.y += SPEED;
|
||||||
|
|
||||||
|
state.left.y = Math.max(0, Math.min(H - PADDLE_H, state.left.y));
|
||||||
|
state.right.y = Math.max(0, Math.min(H - PADDLE_H, state.right.y));
|
||||||
|
|
||||||
|
// Ball movement
|
||||||
|
const b = state.ball;
|
||||||
|
b.x += b.dx;
|
||||||
|
b.y += b.dy;
|
||||||
|
|
||||||
|
// Top/bottom bounce
|
||||||
|
if (b.y - BALL_SIZE/2 <= 0) { b.y = BALL_SIZE/2; b.dy *= -1; }
|
||||||
|
if (b.y + BALL_SIZE/2 >= H) { b.y = H - BALL_SIZE/2; b.dy *= -1; }
|
||||||
|
|
||||||
|
// Left paddle collision
|
||||||
|
if (b.x - BALL_SIZE/2 <= PADDLE_W + 10 &&
|
||||||
|
b.y >= state.left.y && b.y <= state.left.y + PADDLE_H && b.dx < 0) {
|
||||||
|
b.dx *= -1.05;
|
||||||
|
b.dy += (b.y - (state.left.y + PADDLE_H/2)) * 0.1;
|
||||||
|
b.x = PADDLE_W + 10 + BALL_SIZE/2;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Right paddle collision
|
||||||
|
if (b.x + BALL_SIZE/2 >= W - PADDLE_W - 10 &&
|
||||||
|
b.y >= state.right.y && b.y <= state.right.y + PADDLE_H && b.dx > 0) {
|
||||||
|
b.dx *= -1.05;
|
||||||
|
b.dy += (b.y - (state.right.y + PADDLE_H/2)) * 0.1;
|
||||||
|
b.x = W - PADDLE_W - 10 - BALL_SIZE/2;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cap speed
|
||||||
|
b.dx = Math.max(-12, Math.min(12, b.dx));
|
||||||
|
b.dy = Math.max(-10, Math.min(10, b.dy));
|
||||||
|
|
||||||
|
// Score
|
||||||
|
if (b.x < 0) { state.scores[1]++; resetBall(1); }
|
||||||
|
if (b.x > W) { state.scores[0]++; resetBall(-1); }
|
||||||
|
}
|
||||||
|
|
||||||
|
function draw() {
|
||||||
|
ctx.clearRect(0, 0, W, H);
|
||||||
|
|
||||||
|
// Background
|
||||||
|
ctx.fillStyle = '#111';
|
||||||
|
ctx.fillRect(0, 0, W, H);
|
||||||
|
|
||||||
|
// Center line
|
||||||
|
ctx.setLineDash([10, 10]);
|
||||||
|
ctx.strokeStyle = '#333';
|
||||||
|
ctx.lineWidth = 2;
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.moveTo(W/2, 0);
|
||||||
|
ctx.lineTo(W/2, H);
|
||||||
|
ctx.stroke();
|
||||||
|
ctx.setLineDash([]);
|
||||||
|
|
||||||
|
// Scores
|
||||||
|
ctx.fillStyle = '#fff';
|
||||||
|
ctx.font = 'bold 48px monospace';
|
||||||
|
ctx.textAlign = 'center';
|
||||||
|
ctx.fillText(state.scores[0], W/2 - 80, 60);
|
||||||
|
ctx.fillText(state.scores[1], W/2 + 80, 60);
|
||||||
|
|
||||||
|
// Paddles
|
||||||
|
ctx.fillStyle = '#fff';
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.roundRect(10, state.left.y, PADDLE_W, PADDLE_H, 4);
|
||||||
|
ctx.fill();
|
||||||
|
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.roundRect(W - PADDLE_W - 10, state.right.y, PADDLE_W, PADDLE_H, 4);
|
||||||
|
ctx.fill();
|
||||||
|
|
||||||
|
// Ball
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.arc(state.ball.x, state.ball.y, BALL_SIZE/2, 0, Math.PI * 2);
|
||||||
|
ctx.fill();
|
||||||
|
|
||||||
|
// Pause overlay
|
||||||
|
if (state.paused) {
|
||||||
|
ctx.fillStyle = 'rgba(0,0,0,0.5)';
|
||||||
|
ctx.fillRect(0, 0, W, H);
|
||||||
|
ctx.fillStyle = '#fff';
|
||||||
|
ctx.font = 'bold 36px monospace';
|
||||||
|
ctx.fillText('PAUSED', W/2, H/2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function loop() {
|
||||||
|
update();
|
||||||
|
draw();
|
||||||
|
requestAnimationFrame(loop);
|
||||||
|
}
|
||||||
|
|
||||||
|
loop();
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
Reference in New Issue
Block a user