使用 JavaScript 和 HTML5 Canvas 构建渐进式网页游戏


近年来,Web 平台发展显著,使开发者能够创建更强大、更具交互性的应用程序。随着 HTML5 和 JavaScript 的引入,开发者现在不仅可以构建网站,还可以构建直接在浏览器中运行的游戏。

在本文中,我们将探讨使用 JavaScript 和 HTML5 Canvas 构建渐进式网页游戏的过程,并以“打砖块”游戏的实际示例进行说明。

什么是渐进式网页游戏?

渐进式网页游戏是基于 Web 的游戏,它们利用现代 Web 技术提供丰富且沉浸式的游戏体验。它们使用标准 Web 技术(如 HTML、CSS 和 JavaScript)构建,因此可在不同的平台和设备上访问。渐进式网页游戏的一个关键特性是它们能够离线工作并为用户提供类似应用程序的体验。

设置游戏

首先,我们需要一个 canvas 元素来渲染游戏图形。canvas 元素提供了一个绘图表面,我们可以使用 JavaScript 在其上创建动态和交互式图形。以下是我们游戏画布的 HTML 标记:

<canvas id="gameCanvas" width="480" height="320"></canvas>

在上面的代码中,我们定义了一个 id 为“gameCanvas”的 canvas 元素,并指定了画布的宽度和高度。您可以根据自己的喜好调整尺寸。

绘制游戏元素

现在我们已经设置好画布,让我们继续绘制游戏元素。“打砖块”游戏示例中,我们有三个主要元素:球、球拍和砖块。我们将使用 JavaScript 和 HTML5 Canvas API 在画布上绘制这些元素。

球将在画布上表示为一个圆圈。我们定义它的位置 (x, y)、半径和移动 (dx, dy)。我们使用 context.arc() 方法在画布上绘制球。以下是绘制球的代码:

function drawBall() {
   context.beginPath();
   context.arc(x, y, ballRadius, 0, Math.PI * 2);
   context.fillStyle = "#0095DD";
   context.fill();
   context.closePath();
}

球拍

球拍是一个水平移动的矩形。我们定义它的位置 (paddleX)、宽度和高度。我们使用 context.rect() 方法在画布上绘制球拍。以下是绘制球拍的代码:

function drawPaddle() {
   context.beginPath();
   context.rect(paddleX, canvas.height - paddleHeight, paddleWidth, paddleHeight);
   context.fillStyle = "#0095DD";
   context.fill();
   context.closePath();
}

砖块

砖块表示为按行和列排列的矩形。我们定义它们的位置、宽度、高度和状态(它们是活动状态还是已被破坏)。我们使用嵌套循环遍历砖块数组并在画布上绘制活动砖块。以下是绘制砖块的代码:

function drawBricks() {
   for (let c = 0; c < brickColumnCount; c++) {
      for (let r = 0; r < brickRowCount; r++) {
         if (bricks[c][r].status === 1) {
            const brickX = c * (brickWidth + brickPadding) + brickOffsetLeft;
            const brickY = r * (brickHeight + brickPadding) + brickOffsetTop;
            bricks[c][r].x = brickX;
            bricks[c][r].y = brickY;
            context.beginPath();
            context.rect(brickX, brickY, brickWidth, brickHeight);
            context.fillStyle = "#0095DD";
            context.fill();
            context.closePath();
         }
      }
   }
}

以下是游戏的完整代码。

示例

index.html

<!DOCTYPE html>
<html>
<head>
   <title>Brick Breaker Game</title>
   <style>
      canvas {
         border: 1px solid #000;
         display: block;
         margin: 0 auto;
      }
   </style>
</head>
<body>
   <canvas id="gameCanvas" width="480" height="320"></canvas>
   <script>
      const canvas = document.getElementById("gameCanvas");
      const context = canvas.getContext("2d");

      const ballRadius = 10;
      let x = canvas.width / 2;
      let y = canvas.height - 30;
      let dx = 2;
      let dy = -2;

      const paddleHeight = 10;
      const paddleWidth = 75;
      let paddleX = (canvas.width - paddleWidth) / 2;
      let rightPressed = false;
      let leftPressed = false;

      const brickRowCount = 3;
      const brickColumnCount = 5;
      const brickWidth = 75;
      const brickHeight = 20;
      const brickPadding = 10;
      const brickOffsetTop = 30;
      const brickOffsetLeft = 30;
      const bricks = [];
      for (let c = 0; c < brickColumnCount; c++) {
         bricks[c] = [];
         for (let r = 0; r < brickRowCount; r++) {
            bricks[c][r] = { x: 0, y: 0, status: 1 };
         }
      }

      let score = 0;

      function drawBall() {
         context.beginPath();
         context.arc(x, y, ballRadius, 0, Math.PI * 2);
         context.fillStyle = "#0095DD";
         context.fill();
         context.closePath();
      }

      function drawPaddle() {
         context.beginPath();
         context.rect(paddleX, canvas.height - paddleHeight, paddleWidth, paddleHeight);
         context.fillStyle = "#0095DD";
         context.fill();
         context.closePath();
      }

      function drawBricks() {
         for (let c = 0; c < brickColumnCount; c++) {
            for (let r = 0; r < brickRowCount; r++) {
               if (bricks[c][r].status === 1) {
                  const brickX = c * (brickWidth + brickPadding) + brickOffsetLeft;
                  const brickY = r * (brickHeight + brickPadding) + brickOffsetTop;
                  bricks[c][r].x = brickX;
                  bricks[c][r].y = brickY;
                  context.beginPath();
                  context.rect(brickX, brickY, brickWidth, brickHeight);
                  context.fillStyle = "#0095DD";
                  context.fill();
                  context.closePath();
               }
            }
         }
      }

      function drawScore() {
         context.font = "16px Arial";
         context.fillStyle = "#0095DD";
         context.fillText("Score: " + score, 8, 20);
      }

      function collisionDetection() {
         for (let c = 0; c < brickColumnCount; c++) {
            for (let r = 0; r < brickRowCount; r++) {
               const brick = bricks[c][r];
               if (brick.status === 1) {
                  if (
                     x > brick.x &&
                     x < brick.x + brickWidth &&
                     y > brick.y &&
                     y < brick.y + brickHeight
                  ) {
                     dy = -dy;
                     brick.status = 0;
                     score++;
                     if (score === brickRowCount * brickColumnCount) {
                        alert("Congratulations! You win!");
                        document.location.reload();
                     }
                  }
               }
            }
         }
      }

      function draw() {
         context.clearRect(0, 0, canvas.width, canvas.height);
         drawBricks();
         drawBall();
         drawPaddle();
         drawScore();
         collisionDetection();

         if (x + dx > canvas.width - ballRadius || x + dx < ballRadius) {
            dx = -dx;
         }
         if (y + dy < ballRadius) {
            dy = -dy;
         } else if (y + dy > canvas.height - ballRadius) {
            if (x > paddleX && x < paddleX + paddleWidth) {
               dy = -dy;
            } else {
               alert("Game Over");
               document.location.reload();
            }
         }

         if (rightPressed && paddleX < canvas.width - paddleWidth) {
            paddleX += 7;
         } else if (leftPressed && paddleX > 0) {
            paddleX -= 7;
         }

         x += dx;
         y += dy;

         requestAnimationFrame(draw);
      }

      function keyDownHandler(e) {
         if (e.key === "Right" || e.key === "ArrowRight") {
            rightPressed = true;
         } else if (e.key === "Left" || e.key === "ArrowLeft") {
            leftPressed = true;
         }
      }

      function keyUpHandler(e) {
         if (e.key === "Right" || e.key === "ArrowRight") {
            rightPressed = false;
         } else if (e.key === "Left" || e.key === "ArrowLeft") {
            leftPressed = false;
         }
      }

      document.addEventListener("keydown", keyDownHandler, false);
      document.addEventListener("keyup", keyUpHandler, false);

      draw();
   </script>
</body>
</html>

当您在浏览器中打开上述代码时,您应该看到类似于以下所示的输出。

当您错失与球拍接触球的机会时,您应该看到类似于以下所示的输出。

结论

使用 JavaScript 和 HTML5 Canvas 构建渐进式网页游戏为开发者打开了一个充满可能性的世界。通过利用 Web 技术的强大功能,我们可以创建引人入胜且互动性强的游戏,用户可以在不同的平台和设备上访问这些游戏。在本文中,我们以“打砖块”游戏为例,探讨了构建过程,包括设置、绘制游戏元素、实现游戏逻辑和用户交互。

更新于:2023年7月25日

593 次浏览

启动您的 职业生涯

完成课程获得认证

开始
广告