vue 实现简单贪吃蛇

163 阅读2分钟

趁着闲下来 做个小游戏玩玩

先看看效果 GIF.gif

绘制网格

trunk:蛇身体 food:食物 isHead:蛇头

<template>
  <div class="snake">
    <div class="grid-container">
      <div class="grid-row" v-for="(row,index) in gridList" :key="index">
        <div class="grid-item" v-for="(item,index2) in row" :key="index2"
          :class="{ 'trunk': item===1, 'food': item===2,'isHead': index==bodyList[0].x&&bodyList[0].y===index2}">
        </div>
      </div>
    </div>
    <p v-if="state===2" @click="init">你输了 点击重来</p>
  </div>
</template>
  data() {
    return {
      n: 16, //行列数
      gridList: [],//宫格
      bodyList: [],//蛇身位置存储
      direction: 39,//方向
      timer: "",//定时器
      loading: false,
      state: 1,//游戏状态
    };
  },
.snake {
  padding: 20px;
  .grid-container {
    background-color: #2196f3;
    padding: 10px;
    box-sizing: content-box;
  }
  .grid-row {
    display: flex;
  }
  .grid-item {
    background-color: rgba(255, 255, 255, 0.8);
    border: 1px solid rgba(0, 0, 0, 0.8);
    font-size: 30px;
    text-align: center;
    width: 40px;
    height: 40px;
    display: inline-block;
  }
  .trunk {
    background-color: black;
  }
  .food {
    background-color: yellow;
  }
  .isHead {
    background-color: red;
  }
}
  methods: {
    init() {
      let arr = Array.from(Array(this.n), () => Array(this.n).fill(0));
      //生成蛇的初始位置
      let x = Math.round(this.n / 2);
      let y = Math.round(this.n / 2);
      arr[x][y] = 1;
      arr[x][y - 1] = 1;
      this.bodyList = [
        { x, y },
        { x: x, y: y - 1 },
      ];
      this.gridList = arr;
    },
    }

效果如图

image.png

生成食物

    randomFood() {
      let x = Math.round(Math.random() * this.n);
      let y = Math.round(Math.random() * this.n);
      let arr = [...this.gridList];
      if (arr[x][y] !== 0) {
        // 如果出生点位在不是空白位 则重新调用
        this.randomFood();
        return;
      } else {
        arr[x][y] = 2;
        this.gridList = arr;
      }
    },

image.png

键盘监听

 monitorKeydown() {
      let _this = this;
      //监听键盘事件
      document.onkeydown = function (e) {
        let key = window.event.keyCode;
        if (key === 32) {
           //空格暂停
          _this.endGame();
          return false;
        }
        // 防止连点
        if (_this.loading) {
          return false;
        }
        // 37左 38上 39右 40下
        // 转向方向如跟当前方向对立 则不进行转向
        if (key === 37 || key === 38 || key === 39 || key === 40) {
          if (key === 37 && _this.direction === 39) {
            return false;
          } else if (key === 39 && _this.direction === 37) {
            return false;
          } else if (key === 38 && _this.direction === 40) {
            return false;
          } else if (key === 40 && _this.direction === 38) {
            return false;
          }
          _this.direction = key;
          _this.loading = true;
          _this.action();
        }
       
      };
    },
    //结束游戏
    endGame() {
      clearInterval(this.timer);
      this.state = 2;
    },

方向移动

采用定时器 触发移动事件

这里采用一个技巧 由于蛇移动是连着身子的 所以 蛇身移动后位置[i] = 蛇身移动移动前位置[i+1] 然后处理蛇头和蛇尾就可以

action() {
  if (this.timer) {
    clearInterval(this.timer);
  }
  this.timer = setInterval(this.crawl, 500);
},
crawl() {
  const { direction, n } = this;
  let gridList = [...this.gridList];
  let bodyList = [...this.bodyList];
  let movex = 0,
    movey = 0;
    //边界处理
  if (direction === 39) {
    if (bodyList[0].y >= n - 1) {
      this.endGame();
      return false;
    }
    movey = 1;
  } else if (direction === 40) {
    if (bodyList[0].x >= n - 1) {
      this.endGame();
      return false;
    }
    movex = 1;
  } else if (direction === 37) {
    if (bodyList[0].y <= 0) {
      this.endGame();
      return false;
    }
    movey = -1;
  } else {
    if (bodyList[0].x <= 0) {
      this.endGame();
      return false;
    }
    movex = -1;
  }
  
  let start = bodyList[0],
    end = bodyList[bodyList.length - 1];
   // 蛇头前进
  gridList[start.x + movex][start.y + movey] = 1;
  // 蛇身依次前进
  bodyList.forEach((item, index) => {
      if (index < bodyList.length - 1) {
         let next = bodyList[index + 1];
         gridList[item.x][item.y] = gridList[next.x][next.y];
       }
  });
  //更新蛇的记录位置
  bodyList = bodyList.map((item, index) => {
      if (index === 0) {
        return {
          x: start.x + movex,
          y: start.y + movey,
        };
      } else {
        let next = bodyList[index - 1];
        return next;
      }
  });
  //蛇尾处理
  gridList[end.x][end.y] = 0;
  this.gridList = gridList;
  this.bodyList = bodyList;
  this.loading = false;
},

GIF.gif

剩下就是碰到食物 或者蛇头碰到自己时的处理

 if (gridList[start.x + movex][start.y + movey] === 1) { //碰到自身游戏结束
        this.endGame();
        return false;
  } else if (gridList[start.x + movex][start.y + movey] === 2) { //碰到食物 加进蛇身数组
    gridList[start.x + movex][start.y + movey] = 1;
    bodyList.unshift({
      x: start.x + movex,
      y: start.y + movey,
    });
    this.$nextTick(() => {
      this.randomFood();
    });
  } 

大功告成 又是摸鱼的一天