使用 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 次瀏覽

啟動您的 職業生涯

完成課程獲得認證

開始
廣告