打砖块游戏实现

AI 智能总结

简介

打砖块(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 行彩色砖块和生命值系统提供了经典的街机游戏体验。

留言评论

期待你的想法

评论加载中