返回

让 Canvas 妙趣横生:三个引人入胜的小游戏

前端

前言

欢迎来到 Canvas 的奇妙世界!如果你已经读过我之前的文章,那么你已经对这个强大的绘图库有了基本的了解。今天,我们将更进一步,通过三个引人入胜的小游戏,探索 Canvas 的无限可能性。我们将从一个激烈的射击游戏开始,然后跳到一个考验你逻辑思维的益智游戏,最后用一个令人上瘾的街机游戏来结束这段旅程。在每个游戏中,我们将逐步分解代码,提供清晰易懂的解释,并提供代码示例,让你能够亲身体验 Canvas 的魔力。

游戏 1:太空射击

让我们从一场激烈的太空射击游戏开始。你将驾驶一艘宇宙飞船,抵御一波又一波的外星入侵者。游戏采用简单的物理引擎,让飞船和子弹都能真实地移动和碰撞。

// 设定画布尺寸
const canvas = document.getElementById('canvas');
const ctx = canvas.getContext('2d');
canvas.width = 500;
canvas.height = 500;

// 创建飞船
const ship = {
  x: canvas.width / 2,
  y: canvas.height / 2,
  width: 20,
  height: 20,
};

// 创建外星人
const aliens = [];
for (let i = 0; i < 10; i++) {
  aliens.push({
    x: Math.random() * canvas.width,
    y: Math.random() * canvas.height,
    width: 20,
    height: 20,
  });
}

// 游戏循环
function gameLoop() {
  // 清除画布
  ctx.clearRect(0, 0, canvas.width, canvas.height);

  // 绘制飞船
  ctx.fillStyle = 'white';
  ctx.fillRect(ship.x, ship.y, ship.width, ship.height);

  // 绘制外星人
  for (let i = 0; i < aliens.length; i++) {
    const alien = aliens[i];
    ctx.fillStyle = 'red';
    ctx.fillRect(alien.x, alien.y, alien.width, alien.height);
  }

  // 更新飞船和外星人位置
  // ...

  // 检查碰撞
  // ...

  // 继续游戏循环
  requestAnimationFrame(gameLoop);
}

// 启动游戏循环
gameLoop();

游戏 2:15 拼图

接下来,我们将探索一个经典的益智游戏:15 拼图。你的目标是将被打乱的数字块排列成顺序,从 1 到 15。

// 设定画布尺寸
const canvas = document.getElementById('canvas');
const ctx = canvas.getContext('2d');
canvas.width = 400;
canvas.height = 400;

// 创建拼图块
const tiles = [];
for (let i = 0; i < 15; i++) {
  tiles.push({
    x: i % 4 * 100,
    y: Math.floor(i / 4) * 100,
    width: 100,
    height: 100,
    value: i + 1,
  });
}

// 当前空白块
let blankTile = tiles[tiles.length - 1];

// 绘制拼图
function drawPuzzle() {
  // 清除画布
  ctx.clearRect(0, 0, canvas.width, canvas.height);

  // 绘制拼图块
  for (let i = 0; i < tiles.length; i++) {
    const tile = tiles[i];
    ctx.fillStyle = tile.value % 2 ? 'white' : 'black';
    ctx.fillRect(tile.x, tile.y, tile.width, tile.height);

    // 绘制数字
    ctx.fillStyle = 'black';
    ctx.font = '32px Arial';
    ctx.fillText(tile.value, tile.x + 20, tile.y + 50);
  }
}

// 移动拼图块
function moveTile(tile) {
  // 检查是否可以移动
  if (isMovable(tile)) {
    // 交换空白块和当前块的位置
    const temp = blankTile;
    blankTile = tile;
    tile = temp;

    // 更新拼图块数组
    const index = tiles.indexOf(tile);
    tiles[index] = blankTile;
    tiles[tiles.length - 1] = tile;
  }
}

// 检查拼图是否完成
function isPuzzleSolved() {
  // 检查每个拼图块是否在正确位置
  for (let i = 0; i < tiles.length; i++) {
    if (tiles[i].value !== i + 1) {
      return false;
    }
  }

  // 如果所有拼图块都在正确位置,则返回 true
  return true;
}

// 添加事件监听器
canvas.addEventListener('click', function(e) {
  // 获取点击位置
  const x = e.clientX - canvas.offsetLeft;
  const y = e.clientY - canvas.offsetTop;

  // 检查点击的拼图块
  for (let i = 0; i < tiles.length; i++) {
    const tile = tiles[i];
    if (x >= tile.x && x <= tile.x + tile.width && y >= tile.y && y <= tile.y + tile.height) {
      // 移动拼图块
      moveTile(tile);

      // 检查拼图是否完成
      if (isPuzzleSolved()) {
        alert('恭喜!你完成了拼图!');
      }
    }
  }
});

// 启动绘图循环
drawPuzzle();

游戏 3:打砖块

最后,让我们用一个经典的街机游戏——打砖块来结束这场旅程。你的目标是使用球和球拍来打破屏幕上的所有砖块,同时避免球掉落到底部。

// 设定画布尺寸
const canvas = document.getElementById('canvas');
const ctx = canvas.getContext('2d');
canvas.width = 600;
canvas.height = 400;

// 创建球
const ball = {
  x: canvas.width / 2,
  y: canvas.height / 2,
  radius: 10,
  dx: 2,
  dy: -2,
};

// 创建球拍
const paddle = {
  x: canvas.width / 2 - 50,
  y: canvas.height - 20,
  width: 100,
  height: 10,
};

// 创建砖块
const bricks = [];
for (let i = 0; i < 5; i++) {
  for (let j = 0; j < 10; j++) {
    bricks.push({
      x: 50 + i * 60,
      y: 50 + j * 20,
      width: 50,
      height: 20,
    });
  }
}

// 游戏循环
function gameLoop() {
  // 清除画布
  ctx.clearRect(0, 0, canvas.width, canvas.height);

  // 绘制球
  ctx.fillStyle = 'white';
  ctx.beginPath();
  ctx.arc(ball.x, ball.y, ball.radius, 0, Math.PI * 2);
  ctx.fill();

  // 绘制球拍
  ctx.fillStyle = 'white';
  ctx.fillRect(paddle.x, paddle.y, paddle.width, paddle.height);

  // 绘制砖块
  for (let i = 0; i < bricks.length; i++) {
    const brick = bricks[i];
    ctx.fillStyle = 'red';
    ctx.fillRect(brick.x, brick.y, brick.width, brick.height);