canvas -- 分享一个贪吃蛇小游戏1

203 阅读2分钟

先上代码

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>贪吃蛇</title>
  </head>
  <style>
    canvas {
      border: 1px solid #000;
    }
  </style>
  <body>
    <canvas id="canvas" width="800" height="800"></canvas>
  </body>
  <script>
    const canvas = document.getElementById("canvas");
    const ctx = canvas.getContext("2d");
    let isEatFood = false;
    draw();
    function draw() {
      class Rect {
        constructor(x, y, width, height, color) {
          this.x = x;
          this.y = y;
          this.width = width;
          this.height = height;
          this.color = color;
        }
        draw() {
          ctx.beginPath();
          ctx.fillStyle = this.color;
          ctx.fillRect(this.x, this.y, this.width, this.height);
          ctx.strokeRect(this.x, this.y, this.width, this.height);
        }
      }

      class Snake {
        constructor(length = 0) {
          this.length = length;
          this.head = new Rect(
            canvas.width / 2,
            canvas.height / 2,
            40,
            40,
            "red"
          );
          this.body = [];
          let x = this.head.x - 40;
          let y = this.head.y;
          for (let i = 0; i < this.length; i++) {
            const rect = new Rect(x, y, 40, 40, "yellow");
            this.body.push(rect);
            x -= 40;
          }
        }
        // 画
        drawSnake = function () {
          if (this.isHit(this)) {
            clearInterval(timer);
            const con = confirm(
              `共吃了${this.body.length - this.length}个食物,要重新开始吗`
            );
            if (con) {
              draw();
            }
            return;
          }

          // 绘制蛇头
          this.head.draw();
          // 绘制蛇身
          for (let i = 0; i < this.body.length; i++) {
            this.body[i].draw();
          }
        };
        // 动
        moveSnake() {
          const rect = new Rect(
            this.head.x,
            this.head.y,
            this.head.width,
            this.head.height,
            "yellow"
          );
          this.body.unshift(rect);
          //        |  整体
          isEatFood = food && this.head.x === food.x && this.head.y === food.y;
          if (!isEatFood) {
            this.body.pop();
          } else {
            // 吃到了 本次不去尾
            // 再画一个食物
            food = randomFood(this);
            food.draw();
            // 重定义有没有吃到的状态
            isEatFood = false;
          }

          switch (this.direction) {
            case 0:
              this.head.x -= this.head.width; // x轴新加身体起始位置
              break;
            case 1:
              this.head.y -= this.head.height;
              break;
            case 2:
              this.head.x += this.head.width;
              break;
            case 3:
              this.head.y += this.head.height;
              break;
          }
        }
        // 游戏出局定义 -- 触碰边界 || 撞到自己
        isHit(snake) {
          const head = snake.head;
          const xLimit = head.x < 0 || head.x >= canvas.width;
          const yLimit = head.y < 0 || head.y >= canvas.height;
          const hitSelf = snake.body.find(
            ({ x, y }) => head.x === x && head.y === y
          );
          return xLimit || yLimit || hitSelf; // 是否出局
        }
      }
      // 随机投食
      function randomFood(snake) {
        let isInSnake = true;
        let rect;
        while (isInSnake) {
          const x = Math.round((Math.random() * (canvas.width - 40)) / 40) * 40;
          const y =
            Math.round((Math.random() * (canvas.height - 40)) / 40) * 40;
          console.log(x, y);
          // 保证是40的倍数啊
          rect = new Rect(x, y, 40, 40, "blue");
          // 判断食物是否与蛇头蛇身重叠
          if (
            (snake.head.x === x && snake.head.y === y) ||
            snake.body.find((item) => item.x === x && item.y === y)
          ) {
            isInSnake = true; // 重画
            continue;
          } else {
            isInSnake = false;
          }
        }
        return rect;
      }
      document.onkeydown = function (e) {
        // 键盘事件
        e = e || window.event;
        // 左37  上38  右39  下40
        switch (e.keyCode) {
          case 37:
            // 三元表达式,防止右移动时按左,下面同理(贪吃蛇可不能直接掉头)
            snake.direction = snake.direction === 2 ? 2 : 0;
            snake.moveSnake();
            break;
          case 38:
            snake.direction = snake.direction === 3 ? 3 : 1;
            break;
          case 39:
            snake.direction = snake.direction === 0 ? 0 : 2;
            break;
          case 40:
            snake.direction = snake.direction === 1 ? 1 : 3;
            break;
        }
      };

      const snake = new Snake(3);
      // 默认direction为2,也就是右
      snake.direction = 2;
      snake.drawSnake();
      var food = randomFood(snake);
      food.draw();
      function animate() {
        // 先清空
        ctx.clearRect(0, 0, canvas.width, canvas.height);
        // 移动
        snake.moveSnake();
        // 再画
        snake.drawSnake();
        food.draw();
      }

      var timer = setInterval(() => {
        animate();
      }, 100);
    }
  </script>
</html>

截图如下:

image.png

第零步 -- 准备工作先做好~

canvas初始化:我们的画布

<!DOCTYPE html>
<html lang="en">
  <head>
    <title>贪吃蛇</title>
  </head>
  <style>
    canvas {
      border: 1px solid #000;
    }
  </style>
  <body>
    <canvas id="canvas" width="800" height="800"></canvas>
  </body>
  <script>
    const canvas = document.getElementById("canvas");
    const ctx = canvas.getContext("2d");
  </script>
</html>

下面,我们就开始画起来吧~ 要玩贪吃蛇,首先我们要有一条蛇

第一步 -- 画蛇

蛇是由蛇头和蛇身组成的,而蛇头和蛇身都是由小方块组成的,只是颜色有所不同,所以要画蛇,就要先画小方块

      class Rect {
        constructor(x, y, width, height, color) {
          this.x = x;
          this.y = y;
          this.width = width;
          this.height = height;
          this.color = color;
        }
        draw() {
          ctx.beginPath();
          ctx.fillStyle = this.color;
          ctx.fillRect(this.x, this.y, this.width, this.height);
          ctx.strokeRect(this.x, this.y, this.width, this.height);
        }
      }

然后就可以根据起始位置,宽高和颜色来拼蛇了

<!DOCTYPE html>
<html lang="en">
  <head>
    <title>贪吃蛇</title>
  </head>
  <style>
    canvas {
      border: 1px solid #000;
    }
  </style>
  <body>
    <canvas id="canvas" width="800" height="800"></canvas>
  </body>
  <script>
    const canvas = document.getElementById("canvas");
    const ctx = canvas.getContext("2d");
    let isEatFood = false;
    draw();
    function draw() {
      class Rect {
        constructor(x, y, width, height, color) {
          this.x = x;
          this.y = y;
          this.width = width;
          this.height = height;
          this.color = color;
        }
        draw() {
          ctx.beginPath();
          ctx.fillStyle = this.color;
          ctx.fillRect(this.x, this.y, this.width, this.height);
          ctx.strokeRect(this.x, this.y, this.width, this.height);
        }
      }

      class Snake {
        constructor(length = 0) {
          this.length = length;
          this.head = new Rect(
            canvas.width / 2,
            canvas.height / 2,
            40,
            40,
            "blue"
          );
          this.body = [];
          let x = this.head.x - 40;
          let y = this.head.y;
          for (let i = 0; i < this.length; i++) {
            const rect = new Rect(x, y, 40, 40, "green");
            this.body.push(rect);
            x -= 40;
          }
        }
        // 画
        drawSnake = function () {
          // 绘制蛇头
          this.head.draw();
          // 绘制蛇身
          for (let i = 0; i < this.body.length; i++) {
            this.body[i].draw();
          }
        };
      }

      const snake = new Snake(3);
      snake.drawSnake();
    }
  </script>
</html>

这样,一只静止的小蛇就出现啦!

第二步 -- 利用定时器,让蛇动起来

让我们梳理一下规则

  • 1、一次移动一个方格的距离 -- 头部加一,尾部减一来形成这样的视觉误差
  • 2、蛇一开始就会默认向右移动
  • 3、通过方向键盘事件,往不同方向移动
  • 4、不能向反方向移动
<!DOCTYPE html>
<html lang="en">
  <head>
    <title>贪吃蛇</title>
  </head>
  <style>
    canvas {
      border: 1px solid #000;
    }
  </style>
  <body>
    <canvas id="canvas" width="800" height="800"></canvas>
  </body>
  <script>
    const canvas = document.getElementById("canvas");
    const ctx = canvas.getContext("2d");
    draw();
    function draw() {
      class Rect {
        constructor(x, y, width, height, color) {
          this.x = x;
          this.y = y;
          this.width = width;
          this.height = height;
          this.color = color;
        }
        draw() {
          ctx.beginPath();
          ctx.fillStyle = this.color;
          ctx.fillRect(this.x, this.y, this.width, this.height);
          ctx.strokeRect(this.x, this.y, this.width, this.height);
        }
      }

      class Snake {
        constructor(length = 0) {
          this.length = length;
          this.head = new Rect(
            canvas.width / 2,
            canvas.height / 2,
            40,
            40,
            "blue"
          );
          this.body = [];
          let x = this.head.x - 40;
          let y = this.head.y;
          for (let i = 0; i < this.length; i++) {
            const rect = new Rect(x, y, 40, 40, "green");
            this.body.push(rect);
            x -= 40;
          }
        }
        // 画
        drawSnake = function () {
          // 绘制蛇头
          this.head.draw();
          // 绘制蛇身
          for (let i = 0; i < this.body.length; i++) {
            this.body[i].draw();
          }
        };
        // 动
        moveSnake() {
          const rect = new Rect(
            this.head.x,
            this.head.y,
            this.head.width,
            this.head.height,
            "green"
          );
          this.body.unshift(rect);
          this.body.pop();

          switch (this.direction) {
            case 0:
              this.head.x -= this.head.width; // x轴新加身体起始位置
              break;
            case 1:
              this.head.y -= this.head.height;
              break;
            case 2:
              this.head.x += this.head.width;
              break;
            case 3:
              this.head.y += this.head.height;
              break;
          }
        }
      }

      document.onkeydown = function (e) {
        // 键盘事件
        e = e || window.event;
        // 左37  上38  右39  下40
        switch (e.keyCode) {
          case 37:
            // 三元表达式,防止右移动时按左,下面同理(贪吃蛇可不能直接掉头)
            snake.direction = snake.direction === 2 ? 2 : 0;
            snake.moveSnake();
            break;
          case 38:
            snake.direction = snake.direction === 3 ? 3 : 1;
            break;
          case 39:
            snake.direction = snake.direction === 0 ? 0 : 2;
            break;
          case 40:
            snake.direction = snake.direction === 1 ? 1 : 3;
            break;
        }
      };

      const snake = new Snake(3);
      // 默认direction为2,也就是右
      snake.direction = 2;
      snake.drawSnake();
      function animate() {
        // 先清空
        ctx.clearRect(0, 0, canvas.width, canvas.height);
        // 移动
        snake.moveSnake();
        // 再画
        snake.drawSnake();
      }

      var timer = setInterval(() => {
        animate();
      }, 100);
    }
  </script>
</html>

第三步 -- 画出食物

蛇动起来是为了什么呢,当然是为了吃鸭! 所以接下来,我们就要开始游戏奖励-投食了 梳理一下投食的规则:

  1. 食物要在画布上
  2. 食物不可以和蛇重叠
  3. 食物的本质也是一个不同颜色的小方块 // 随机投食
      function randomFood(snake) {
        let isInSnake = true;
        let rect;
        while (isInSnake) {
          const x = Math.round((Math.random() * (canvas.width - 40)) / 40) * 40;
          const y =
            Math.round((Math.random() * (canvas.height - 40)) / 40) * 40;
          console.log(x, y);
          // 保证是40的倍数啊
          rect = new Rect(x, y, 40, 40, "red");
          // 判断食物是否与蛇头蛇身重叠
          if (
            (snake.head.x === x && snake.head.y === y) ||
            snake.body.find((item) => item.x === x && item.y === y)
          ) {
            isInSnake = true; // 重画
            continue;
          } else {
            isInSnake = false;
          }
        }
        return rect;
      }
      var food = randomFood(snake);
      food.draw();
       function animate() {
        // 先清空
        ctx.clearRect(0, 0, canvas.width, canvas.height);
        // 移动
        snake.moveSnake();
        // 再画
        snake.drawSnake();
        food.draw();
      }

到这里,一个贪吃蛇游戏的所有元素就全了~

明天我们开始梳理贪吃蛇的规则和补全游戏提示。