从 0 开始写一个贪吃蛇小游戏(三)

1,153 阅读3分钟

「这是我参与2022首次更文挑战的第14天,活动详情查看:2022首次更文挑战」。

前情回顾

  • 在前面两篇文章,我们实现了地图类和食物类相关的一些逻辑
  • 今天我们来实现蛇类 Snake,蛇是本游戏的主角,所以它的属性和方法会相对而言复杂一点

实现蛇类

  • 蛇类实现的功能主要有蛇的移动,吃食物,转弯
  • 老规矩我们先来品鉴蛇类的代码
class Snake {
    constructor() {
      this.data = [
        { x: 6, y: 4, color: '#94dd94' },
        { x: 5, y: 4, color: 'white' },
        { x: 4, y: 4, color: 'white' },
        { x: 3, y: 4, color: 'white' },
        { x: 2, y: 4, color: 'white' },
      ];
      
      this.direction = 'right';
      
      // 保存最后一个数据,用于吃到食物的时候添加到蛇的末尾,达到变长的效果
      this.lastData = {};
    }

    moveFn() {
      // 最后一个元素的索引值
      let i = this.data.length - 1;

      // 保存最后一个数据,用于吃到食物的时候添加到蛇的末尾,达到变长的效果
      this.lastData = {
        x: this.data[i].x,
        y: this.data[i].y,
        color: this.data[i].color,
      };

      // 蛇的身体 从蛇尾开始,每走一下就是 后面的蛇的格子 移动到 前面的蛇的格子上
      for (i; i > 0; i--) {
        this.data[i].x = this.data[i - 1].x;
        this.data[i].y = this.data[i - 1].y;
      }

      // 蛇的头部 是根据方向来移动的
      switch (this.direction) {
        case 'left':
          this.data[0].x--;
          break;
        case 'right':
          this.data[0].x++;
          break;
        case 'top':
          this.data[0].y--;
          break;
        case 'bottom':
          this.data[0].y++;
          break;

        default:
          break;
      }
    }

    turnFn(direction) {
      let vertical = ['top', 'bottom'];
      let horizontal = ['left', 'right'];

      if (horizontal.includes(this.direction) && horizontal.includes(direction)) {
        return false;
      } else if (vertical.includes(this.direction) && vertical.includes(direction)) {
        return false;
      }

      this.direction = direction;
      return true;
    }

    eatFn() {
      this.data.push(this.lastData);
    }
}
  • 如上代码所示,我们需要先初始化一个蛇的位置数据,以及它移动的初始方向

  • 然后还初始化了一个 lastData,它用于存放蛇身体最后一个元素的坐标信息

    • 为什么要这样做,我们暂时先不管这个问题
  • moveFn 是负责控制蛇移动逻辑的函数

    • 每次移动我们都需要将蛇身体的最后一个元素,的坐标信息保存起来
    • 蛇移动的本质,可以分为蛇身体移动和蛇头移动
    • 蛇每走一步,对于蛇身体的每个元素来说,就是后面蛇身元素移动到了前面蛇身元素的位置上
    • 所以移动蛇身体,只需要遍历每个蛇身元素,将前面的蛇身元素的坐标,赋值给后面的蛇身元素即可
    • 那怎么移动蛇的头部呢?
    • 其实也很简单,蛇头部的移动,都是按照方向 direction,向前移动一格
    • 而向前移动一格,对应在是上下左右四个方向上,就是相应的横坐标或纵坐标,加 1 或者减 1,具体代码可以看上面 moveFn 的实现
  • turnFn 是控制蛇头部转向逻辑的函数

    • 这个很好实现,只需要更新方向值即可
    • 但是有个边界条件需要判断
    • 当前蛇移动的方向与改变的方向不能相同或者相反,也就是改变的方向只能与原方向垂直
    • 为了表示转向是否成功,我们可以在这个函数最后返回一个布尔值
  • eatFn 是实现蛇吃食物逻辑的函数

    • 这里可以解释上面说的那个 lastData 的问题了
    • 当蛇吃掉一个食物时,蛇的身体长度应该变长一个单元格
    • 因为每次移动,蛇身体都往前移动了一个单元格
    • 所以当,蛇吃掉食物时,相当于在蛇尾添加了一个元素
    • 只用在蛇移动前记录蛇身最后一个元素的信息,在蛇移动一个单元格后,将这个数据添加到蛇身数据末尾即可

小结

  • 到这里蛇类的实现就已经完成了
  • 为了不影响阅读体验,我会将这个 demo 的实现过程分为 4 篇文章来写,下一篇实现游戏主流程的控制类

最后

  • 今天的分享就到这里,欢迎大家在评论区留言讨论
  • 如果觉得文章写的还不错的话,希望大家不要吝惜点赞,大家的鼓励是我分享的最大动力 🥰