vue實現一個簡單的自適應貪喫蛇

前一陣用vue寫了一個貪喫蛇,主要練習一下自己的邏輯拆分能力,在此與大家分享一下。

此貪喫蛇可以通過分辨率比例自動計算格子數量,用二維數組實現。

<<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Document</title>
  <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
  <style>
    .tetris-wrapper {
      position: absolute;
      top: 0;
      bottom: 0;
      left: 0;
      right: 0;
      background: #fff;
      overflow: auto;
    }
    .game-wrapper {
      position: absolute;
      top: 0.1rem;
      bottom: 0.1rem;
      left: 0.1rem;
      right: 0.1rem;
      display: flex;
      flex-direction: column;
      background-color: grey;
    }
    .block-group {
      flex: 1;
      position: relative;
      display: flex;
    }
    .block {
      flex: 1;
      border: 0.01rem solid #000;
    }
    .green {
      background-color: green;
    }
    .red {
      background-color: red;
    }
    .blue {
      background-color: blue;
    }
    .black {
      background-color: black;
    }
  </style>
</head>

<body>
  <div id="game"
  class="tetris-wrapper"
  @click="test"
  ref="test"
  @swipeup="changeMoveDirection(0)"
  @swiperight="changeMoveDirection(1)"
  @swipedown="changeMoveDirection(2)"
  @swipeleft="changeMoveDirection(3)">
    <div class="game-wrapper" ref="container">
      <div class="block-group" v-for="(item, index) in structureArray" :key="index">
        <div class="block"
        v-for="(block, blockIndex) in item"
        :key="blockIndex"
        :class="{green: block.state === 1, red: block.state === 2, blue: block.state === 3, black: block.state === 4}">
        </div>
      </div>
    </div>
  </div>
</body>

<script>
  let app = new Vue ({
    el: '#game',
    data () {
      return {
        timer: null, // 存儲定時器
        interval: 200, // 定時間隔時間
        transverse: 20, // 橫座標方格數量
        structureArray: [], // 遊戲畫布結構數組
        moveDirection: 1, // 蛇的移動方向,0、1、2、3,上右下左
        nextMoveDirection: 1, // 將要移動的方向,0、1、2、3,上右下左
        snakeHead: {}, // 蛇頭的座標
        snakeTail: {}, // 蛇尾座標
        foodCoordinate: {} // 食物座標
      }
    },
    methods: {
      test () {
        console.log(this.$refs.container.clientHeight);
        console.log(this.$refs.container.clientWidth);
        console.log(this.longitudinal);
      },
      changeMoveDirection (direction) { // 改變蛇的運動方向
        let judge = direction - this.moveDirection; // 判斷非法的蛇頭轉向
        if (judge === 2 || judge === -2) return;
        this.nextMoveDirection = direction;
      },
      mobileRule () { // 蛇的移動規則定義
        let {x, y} = this.snakeHead; // 當前蛇頭的座標
        let headCode = this.structureArray[y][x].code; // 獲取當前蛇頭的code,用來生成新蛇頭時++使用
        this.structureArray[y][x].state = 1; // 取消原來的蛇頭狀態
        switch(this.nextMoveDirection) { // 移動方向,計算移動後的橫縱座標
          case 0:
            y--;
            break;
          case 1:
            x++
            break;
          case 2:
            y++;
            break;
          case 3:
            x--;
            break;
          default:
            throw new Error('蛇的移動規則判斷出錯');
            break;
        }
        this.structureArray[y].splice(x, 1, {state: 2, code: ++headCode}); // 生成新的蛇頭
        this.snakeHead = {x, y};
        this.moveDirection = this.nextMoveDirection;
        this.whetherEatFood(); // 判斷是否喫到食物(蛇頭和食物座標是否重合)
      },
      whetherEatFood () { // 是否喫到食物判斷
        let state = JSON.stringify(this.snakeHead) === JSON.stringify(this.foodCoordinate); // 判斷食物和蛇頭是否在一個座標上
        if (state) { // 喫到了
          this.generateFood(); // 生成食物
          this.integralRule(); // 走積分
          this.increaseDifficulty(); // 增加難度
        } else { // 沒喫到食物
          this.generateSnakeTail(); // 重新生成蛇尾
        }
      },
      generateFood () { // 生成食物,生成隨機食物座標
        let x = Math.floor(Math.random() * this.transverse); // 生成隨機數
        let y = Math.floor(Math.random() * this.longitudinal); // 生成隨機數
        if (this.structureArray[y][x].state === 0) { // 是空位置
          this.foodCoordinate = {x, y}; // 新的食物座標
          this.structureArray[y].splice(x, 1, { state: 4, code: Infinity}); // 修改座標狀態值,code無限大用來計算蛇尾時不會出錯
        } else { // 位置不爲空,重新生成
          this.generateFood();
        }
      },
      integralRule () { // 積分規則

      },
      increaseDifficulty () { // 增加難度規則
        if (this.interval <= 100) return;
        this.interval -= 4;
        this.clearTimer();
        this.initializationTimer();
      },
      generateSnakeTail () { // 重新生成蛇尾(在沒有喫到食物的情況下調用)
        let {x, y} = this.snakeTail; // 獲取蛇尾座標
        // let tailCode = this.structureArray[y][x].code + 1; // 計算將要作爲蛇尾的code -------- 這裏code取錯了,這樣的話取得是蛇頭的,改成取上下左右最小的code
        // 銷燬之前的蛇尾(若當前蛇尾蛇尾位置是蛇頭,則不做處理,因爲頭先生成)
        if (this.structureArray[y][x].state !== 2) this.structureArray[y].splice(x, 1, {state: 0});
        // 接下來重新定位蛇尾座標
        /*if (this.structureArray[y + 1][x].state && tailCode === this.structureArray[y + 1][x].code) y++;
        else if (this.structureArray[y - 1][x].state && tailCode === this.structureArray[y - 1][x].code) y--;
        else if (this.structureArray[y][x + 1].state && tailCode === this.structureArray[y][x + 1].code) x++;
        else if (this.structureArray[y][x - 1].state && tailCode === this.structureArray[y][x - 1].code) x--;*/
        // else throw new Error('重新生成蛇尾錯誤')
        // 取上下左右code最小的是新蛇尾
        let top = (y - 1 >= 0) && this.structureArray[y - 1][x].state ? this.structureArray[y - 1][x].code : Infinity;
        let bottom = (y + 1 < this.longitudinal) && this.structureArray[y + 1][x].state ? this.structureArray[y + 1][x].code : Infinity;
        let left = (x - 1 >= 0) && this.structureArray[y][x - 1].state ? this.structureArray[y][x - 1].code : Infinity;
        let right = (x + 1 < this.transverse) && this.structureArray[y][x + 1].state ? this.structureArray[y][x + 1].code : Infinity;
        let min = Math.min(top, bottom, left, right);
        switch(min) {
          case top:
            y--;
            break;
          case bottom:
            y++;
            break;
          case left:
            x--;
            break;
          case right:
            x++;
            break;
          default:
            console.log('重新生成蛇尾出錯了');
            break;
        }
        this.snakeTail = {x, y}; // 保存蛇尾座標
        this.structureArray[y][x].state = 3; // 將此座標定義爲蛇尾
      },
      deathRule () { // 蛇的死亡規則判斷
        let {x, y} = this.snakeHead;
        switch (this.nextMoveDirection) { // 移動方向(碰壁判斷和碰蛇身判斷)
          case 0:
            if (y - 1 < 0) return false;
            else if (this.structureArray[y - 1][x].state === 1) return false;
            break;
          case 1:
            if (x + 1 > this.transverse - 1) return false;
            else if (this.structureArray[y][x + 1].state === 1) return false;
            break;
          case 2:
            if (y + 1 > this.longitudinal - 1) return false;
            else if (this.structureArray[y + 1][x].state === 1) return false;
            break;
          case 3:
            if (x - 1 < 0) return false;
            else if (this.structureArray[y][x - 1].state === 1) return false;
            break;
          default:
            throw new Error('蛇的死亡規則判斷錯誤');
            break;
        }
        return true;
      },
      deathTreatment () { // 蛇的死亡處理
        this.clearTimer();
        alert('你的小寶貝沒了!!!!');
      },
      initializationGrid () { // 初始化網格
        this.structureArray = [];
        for (let i = 0; i < this.longitudinal; i++) {
          this.structureArray.push([]);
          for (let j = 0; j < this.transverse; j++) {
            this.structureArray[i].push({
              state: 0 // 0爲空,1爲蛇身,2爲蛇頭,3爲蛇尾,4爲食物
            });
          }
        }
      },
      initializationSnake () { // 初始化蛇
        this.structureArray[2].splice(4, 1, {state: 2, code: 2});
        this.structureArray[2].splice(3, 1, {state: 1, code: 1});
        this.structureArray[2].splice(2, 1, {state: 3, code: 0});
        this.snakeHead = {x: 4, y: 2}; // 蛇頭座標
        this.snakeTail = {x: 2, y: 2}; // 蛇尾座標
      },
      initializationTimer () { // 初始化定時器
        this.timer = setInterval(() => {
          // 處理
          if (this.deathRule()) { // 死不了
            this.mobileRule(); // 走移動
          } else { // 死掉了
            this.deathTreatment();
          }
        }, this.interval);
      },
      clearTimer () { // 清理定時器
        clearInterval(this.timer);
      },
      addKeyboardEvents () { // 添加全局鍵盤按下事件
        document.onkeydown = (event) => {
          console.log(event);
          switch(event.keyCode) {
            case 38:
              this.changeMoveDirection(0);
              break;
            case 39:
              this.changeMoveDirection(1);
              break;
            case 40:
              this.changeMoveDirection(2);
              break;
            case 37:
              this.changeMoveDirection(3);
              break;
          }
        }
      }
    },
    async mounted () {
      await this.initializationGrid(); // 初始化網格
      this.initializationSnake(); // 初始化蛇
      this.initializationTimer(); // 初始化定時器
      this.generateFood(); // 生成食物
      this.addKeyboardEvents(); // 添加鍵盤按下事件
    },
    destroyed () {
      this.clearTimer();
    },
    computed: {
      longitudinal () { // 獲取容器寬高比,計算縱座標方格數
        let boxWidth = this.$refs.container.clientWidth;
        let boxHeight = this.$refs.container.clientHeight;
        let longitudinal = parseInt((boxHeight / boxWidth) * this.transverse);
        return longitudinal;
      }
    }
  });
</script>
</html>

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章