简介
打砖块(Breakout)是雅达利时代的经典街机游戏,玩家操控底部挡板反弹小球,消除顶部的彩色砖块。游戏厅版本实现了鼠标和键盘双操控模式、碰撞物理、6 行彩色砖块和 3 条生命系统。
游戏参数
1var W = 480, H = 540; // 画布尺寸
2var paddle = { x: 190, y: 500, w: 100, h: 14 }; // 挡板
3var paddleSpeed = 7; // 挡板移动速度
4var ball = { x: 240, y: 480, r: 8, vx: 4, vy: -4 }; // 小球
5var bricks = []; // 砖块数组
6var lives = 3; // 生命值
数据结构
砖块布局
6 行 x 8 列,每行使用不同颜色:
1var brickRow = 6, brickCol = 8;
2var brickW = 52, brickH = 20;
3var brickPad = 6; // 砖块间距
4var brickOffTop = 50; // 顶部偏移
5var brickOffLeft = 16; // 左侧偏移
6var colors = ['#ef4444', '#f97316', '#eab308', '#22c55e', '#06b6d4', '#6366f1'];
7
8function buildBricks() {
9 bricks = [];
10 for (var r = 0; r < brickRow; r++)
11 for (var c = 0; c < brickCol; c++) {
12 bricks.push({
13 x: c * (brickW + brickPad) + brickOffLeft,
14 y: r * (brickH + brickPad) + brickOffTop,
15 w: brickW, h: brickH,
16 alive: true,
17 color: colors[r]
18 });
19 }
20}
核心逻辑
挡板控制
支持键盘方向键和鼠标两种操控方式:
1var rightPressed = false, leftPressed = false;
2
3// 键盘控制
4document.addEventListener('keydown', function(e) {
5 if (e.key === 'ArrowRight') rightPressed = true;
6 if (e.key === 'ArrowLeft') leftPressed = true;
7});
8document.addEventListener('keyup', function(e) {
9 if (e.key === 'ArrowRight') rightPressed = false;
10 if (e.key === 'ArrowLeft') leftPressed = false;
11});
12
13// 鼠标控制
14canvas.addEventListener('mousemove', function(e) {
15 var rect = canvas.getBoundingClientRect();
16 var scaleX = W / rect.width;
17 paddle.x = (e.clientX - rect.left) * scaleX - paddle.w / 2;
18 // 边界限制
19 if (paddle.x < 0) paddle.x = 0;
20 if (paddle.x + paddle.w > W) paddle.x = W - paddle.w;
21});
小球物理
每帧更新小球位置,处理边界和碰撞反弹:
1function updateBall() {
2 // 位置更新
3 ball.x += ball.vx;
4 ball.y += ball.vy;
5
6 // 左右墙壁反弹
7 if (ball.x - ball.r < 0 || ball.x + ball.r > W) {
8 ball.vx = -ball.vx;
9 }
10
11 // 顶部墙壁反弹
12 if (ball.y - ball.r < 0) {
13 ball.vy = -ball.vy;
14 }
15
16 // 底部出界 = 失去一条命
17 if (ball.y + ball.r > H) {
18 lives--;
19 if (lives <= 0) gameOver();
20 else resetBall();
21 }
22}
挡板碰撞
小球碰到挡板时,反弹角度根据碰撞位置变化,创造可控的击球体验:
1function checkPaddleCollision() {
2 if (ball.y + ball.r > paddle.y &&
3 ball.y - ball.r < paddle.y + paddle.h &&
4 ball.x > paddle.x &&
5 ball.x < paddle.x + paddle.w) {
6
7 // 垂直反弹
8 ball.vy = -Math.abs(ball.vy);
9
10 // 水平速度根据碰撞位置调整
11 var hitPos = (ball.x - paddle.x) / paddle.w; // 0~1
12 ball.vx = (hitPos - 0.5) * 8; // -4 ~ +4
13
14 // 防止小球卡在挡板内
15 ball.y = paddle.y - ball.r;
16 }
17}
砖块碰撞
检测小球与所有活跃砖块的碰撞,碰撞后砖块消失、小球反弹:
1function checkBrickCollision() {
2 for (var i = bricks.length - 1; i >= 0; i--) {
3 var b = bricks[i];
4 if (!b.alive) continue;
5
6 // AABB 碰撞检测
7 if (ball.x + ball.r > b.x &&
8 ball.x - ball.r < b.x + b.w &&
9 ball.y + ball.r > b.y &&
10 ball.y - ball.r < b.y + b.h) {
11
12 b.alive = false; // 消除砖块
13 ball.vy = -ball.vy; // 反弹
14 score += 10; // 加分
15 break; // 每帧只处理一次碰撞
16 }
17 }
18}
胜利判定
所有砖块都被消除时通关:
1function checkWin() {
2 if (bricks.every(function(b) { return !b.alive; })) {
3 playing = false;
4 drawWinScreen();
5 }
6}
Canvas 渲染
砖块
每块砖由两种颜色叠加,产生立体效果:
1function drawBricks() {
2 bricks.forEach(function(b) {
3 if (!b.alive) return;
4 // 砖块主体
5 ctx.fillStyle = b.color;
6 ctx.fillRect(b.x, b.y, b.w, b.h);
7 // 顶部高光
8 ctx.fillStyle = 'rgba(255, 255, 255, 0.15)';
9 ctx.fillRect(b.x, b.y, b.w, b.h / 2);
10 });
11}
挡板
1function drawPaddle() {
2 // 主体
3 ctx.fillStyle = '#e2e8f0';
4 ctx.fillRect(paddle.x, paddle.y, paddle.w, paddle.h);
5 // 高光
6 ctx.fillStyle = 'rgba(255, 255, 255, 0.3)';
7 ctx.fillRect(paddle.x, paddle.y, paddle.w, paddle.h / 3);
8}
小球
1function drawBall() {
2 // 主体
3 ctx.fillStyle = '#fbbf24';
4 ctx.beginPath();
5 ctx.arc(ball.x, ball.y, ball.r, 0, Math.PI * 2);
6 ctx.fill();
7 // 高光
8 ctx.fillStyle = 'rgba(255, 255, 255, 0.3)';
9 ctx.beginPath();
10 ctx.arc(ball.x - 2, ball.y - 2, ball.r / 3, 0, Math.PI * 2);
11 ctx.fill();
12}
生命值系统
使用心形 emoji 显示剩余生命:
1function updateLives() {
2 var hearts = '';
3 for (var i = 0; i < 3; i++) {
4 hearts += i < lives ? '❤️' : '🖤';
5 }
6 document.getElementById('abrk-lives').textContent = hearts;
7}
游戏循环
1function gameLoop() {
2 if (!playing) return;
3
4 // 键盘移动挡板
5 if (rightPressed && paddle.x + paddle.w < W) paddle.x += paddleSpeed;
6 if (leftPressed && paddle.x > 0) paddle.x -= paddleSpeed;
7
8 updateBall();
9 checkPaddleCollision();
10 checkBrickCollision();
11 checkWin();
12
13 ctx.fillStyle = '#1a1a2e';
14 ctx.fillRect(0, 0, W, H);
15 drawBricks();
16 drawPaddle();
17 drawBall();
18 drawScore();
19
20 animFrameId = requestAnimationFrame(gameLoop);
21}
通关与失败
通关后显示绿色通关提示:
1function drawWinScreen() {
2 ctx.fillStyle = 'rgba(0, 0, 0, 0.5)';
3 ctx.fillRect(0, 0, W, H);
4 ctx.fillStyle = '#22c55e';
5 ctx.font = 'bold 28px sans-serif';
6 ctx.textAlign = 'center';
7 ctx.fillText('恭喜通关!', W / 2, H / 2 - 20);
8 ctx.fillStyle = '#fff';
9 ctx.font = '18px sans-serif';
10 ctx.fillText('得分: ' + score + ' 按空格键继续', W / 2, H / 2 + 25);
11}
最高分持久化
1if (score > best) {
2 best = score;
3 localStorage.setItem('arcade-breakout-best', best);
4}
总结
打砖块的核心在于碰撞检测(球与墙壁、球与挡板、球与砖块)和反弹物理。挡板碰撞点的水平偏移量映射到小球水平速度,让玩家可以通过挡板位置控制击球方向。6 行彩色砖块和生命值系统提供了经典的街机游戏体验。
留言评论
期待你的想法评论加载中