溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點(diǎn)擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

怎么使用Vue2+JS實(shí)現(xiàn)掃雷小游戲

發(fā)布時(shí)間:2022-06-18 13:45:37 來源:億速云 閱讀:147 作者:iii 欄目:開發(fā)技術(shù)

這篇文章主要講解了“怎么使用Vue2+JS實(shí)現(xiàn)掃雷小游戲”,文中的講解內(nèi)容簡單清晰,易于學(xué)習(xí)與理解,下面請(qǐng)大家跟著小編的思路慢慢深入,一起來研究和學(xué)習(xí)“怎么使用Vue2+JS實(shí)現(xiàn)掃雷小游戲”吧!

    實(shí)現(xiàn)步驟

    1、場景布局實(shí)現(xiàn)

    布局就是經(jīng)典的方格布局,對(duì)于場景的美觀度可以自行找?guī)讉€(gè)配色網(wǎng)站作為參考。

    出現(xiàn)問題: 先初始化一個(gè)二維數(shù)組對(duì)應(yīng)方塊坐標(biāo),然后依次渲染 or 直接通過預(yù)期的行、列數(shù)渲染空白方塊

    區(qū)別: 直接初始化二維數(shù)組,可以對(duì)坐標(biāo)進(jìn)行一些屬性操作,例如標(biāo)記、是否為地雷等等,之后操作的時(shí)候會(huì)方便很多,缺點(diǎn)在初始化的時(shí)候需要進(jìn)行大量的計(jì)算工作(因?yàn)樵邳c(diǎn)開一個(gè)安全坐標(biāo)時(shí)需要顯示周圍的地雷個(gè)數(shù),還要考慮邊緣情況),而渲染空白方塊就可以在點(diǎn)擊坐標(biāo)的時(shí)候再去做計(jì)算,并且在點(diǎn)擊的時(shí)候只需要計(jì)算該方塊的屬性。

    這里我選擇了渲染空白方塊的形式。

    代碼實(shí)現(xiàn)

    使用了 element-ui組件

    template

    <div class="layout">
      <div class="row" v-for="row in layoutConfig.row" :key="row">
        <div
          class="cell"
          :
          v-for="col in layoutConfig.cell"
          :key="col">
          <div
            class="block"
            @click="open(row, col, $event)"
            @contextmenu.prevent="sign(row, col, $event)"
          >
            // 這里的邏輯現(xiàn)在可以暫時(shí)不用管,只需要先做好布局
            <template v-if="areaSign[`${row}-${col}`] === 'fail'">
              <img src="../../assets/svg/fail.svg" alt="">
            </template>
            <template v-else-if="areaSign[`${row}-${col}`] === 'tag'">
              <img src="../../assets/svg/Flag.svg" alt="">
            </template>
            <template v-else-if="areaSign[`${row}-${col}`] === 'normal'">
            </template>
            <template v-else>
              {{areaSign[`${row}-${col}`]}}
            </template>
          </div>
        </div>
      </div>
    </div>

    style:

    <style scoped lang="less">
    .container {
      display: flex;
      justify-content: center;
      align-items: center;
      flex-direction: column;
      margin-top: 100px;
      .typeChoose {
        display: flex;
        justify-content: center;
        align-items: center;
        width: 100%;
        margin-bottom: 20px;
        .item {
          margin: 0 10px;
        }
      }
      .layout {
        width: 500px;
        height: 500px;
        .row {
          display: flex;
          justify-content: center;
          align-items: center;
          .cell {
            border: 1px solid #735E30;
            caret-color: transparent;
            cursor: pointer;
            line-height: 50px;
            .block {
              height: 100%;
              background: #292E17;
            }
            .block:hover {
              background: #96875F;
            }
            .opened {
              height: 100%;
              background: #E8E0D8;
            }
          }
        }
      }
    }
    </style>

    2、初始化事件

    生成地雷隨機(jī)二維數(shù)組

    因?yàn)椴季忠呀?jīng)通過空白方塊生成了,所以我們只需要關(guān)心生成隨機(jī)的地雷坐標(biāo)就可以了

    代碼實(shí)現(xiàn)

    /*
     *  type: 當(dāng)前模式的地雷個(gè)數(shù)(自己定義數(shù)量)
     *  mineList: 地雷坐標(biāo)數(shù)組
     *  layoutConfig: {
     *    row: 布局的行數(shù)
     *    col: 布局的列數(shù)
     *  }
     */   
    
    // 生成隨機(jī)地雷坐標(biāo)數(shù)組
    initMineListRange () {
      while (this.mineList.length < this.type) {
        this.initMineItem()
      }
    },
    // 生成單個(gè)地雷坐標(biāo)并且放入地雷坐標(biāo)數(shù)組(mineList)中
    initMineItem () {
      const position = this.initPositionRange([1, this.layoutConfig.row], [1, this.layoutConfig.cell])
      if (!this.hasPositionIn(position, this.mineList)) {
        this.mineList.push(position)
      }
    },
    // 生成一個(gè)在給定范圍內(nèi)的隨機(jī)坐標(biāo)
    initPositionRange ([xStart, xEnd], [yStart, yEnd]) {
      return [this.numRange(xStart, xEnd), this.numRange(yStart, yEnd)]
    },
    // 生成一個(gè)在給定范圍內(nèi)的隨機(jī)整數(shù)
    numRange (start, end) {
      return Math.floor((Math.random() * (end - start + 1))) + start
    },
    // 判斷參數(shù)中的 position 是否已經(jīng)存在與 參數(shù)中的 positionList 中
    hasPositionIn (position, positionList) {
      console.assert(position.length === 2, 'position length < 2, not a position item')
      return positionList.some(p => {
        return p[0] === position[0] && p[1] === position[1]
      })
    }

    3、游戲動(dòng)作(action)

    指的是游戲中的一些操作以及某個(gè)操作導(dǎo)致的一系列變化

    點(diǎn)擊方塊

    分析:點(diǎn)擊方塊之后會(huì)出現(xiàn)三種情況

    • 該方塊的九宮格范圍內(nèi)沒有地雷

    • 該方塊的九宮格方位內(nèi)有地雷

    • 踩雷了(game over)

    對(duì)應(yīng)這三種情況需要分別有不同的表現(xiàn)形式

    第一種情況:(方塊的九宮格范圍內(nèi)沒有地雷)

    這種情況只需要將該方塊的樣式改為點(diǎn)擊過的樣式即可(class="opened"

    第二種情況:(方塊的九宮格方位內(nèi)有地雷)

    修改樣式為opened,并且需要計(jì)算周圍的地雷數(shù)量(需要考慮邊緣情況,即當(dāng)前坐標(biāo)是否在邊緣)

    第三種情況:(踩雷)

    修改樣式為opened, 并且展示地雷,提示用戶游戲結(jié)束

    代碼實(shí)現(xiàn)

    因?yàn)樵邳c(diǎn)擊之前該方塊是空白對(duì)象,所以需要一個(gè)對(duì)象來存儲(chǔ)該方塊的屬性或者狀態(tài)(areaSign

    /*
    *  areaSign: Object  key: 坐標(biāo)('1-2') value: 狀態(tài)
    *  gameProcess:當(dāng)前游戲是否處于進(jìn)行狀態(tài)
    *  statusEnum: 枚舉 方塊狀態(tài)枚舉值(fail,normal,tag)
    */
    
    // 方塊點(diǎn)擊事件 (傳入坐標(biāo)以及點(diǎn)擊事件對(duì)象)
     open (rowIndex, colIndex, e) {
       // 判斷當(dāng)前游戲是否 
       if (!this.gameProcess) {
         this.gameEndConfirm()
         return
       }
       // 判斷當(dāng)前坐標(biāo)是否被標(biāo)記,被標(biāo)記則不能被點(diǎn)開
       if (this.getAreaSignValueWithPosition(rowIndex, colIndex) === statusEnum.tag) {
         this.confirmMessageBox('該區(qū)域已經(jīng)被標(biāo)記,請(qǐng)選擇其他區(qū)域點(diǎn)擊')
         return
       }
       
       e.target.className = 'opened'
       if (this.hasTouchMine([rowIndex, colIndex])) {
         // 踩雷
         this.mineTouched([rowIndex, colIndex])
       } else {
         // 第一、二種情況
         this.safeTouched([rowIndex, colIndex])
       }
     },
     // 通過傳入的坐標(biāo)判斷是否存在地雷坐標(biāo)數(shù)組中
     hasTouchMine ([xPosition, yPosition]) {
       return this.hasPositionIn([xPosition, yPosition], this.mineList)
     },
     mineTouched (position) {
       this.setSvg(position, statusEnum.fail)
       // 游戲失敗提示
       this.gameProcess = false
       this.gameEndConfirm()
     },
     safeTouched (position) {
       this.setTips(position)
     },
     // 把傳入坐標(biāo)通過判斷是否有雷設(shè)置對(duì)應(yīng)提示
     setTips (position) {
       const total = this.positionAroundMineTotal(position)
       this.$set(this.areaSign, `${position[0]}-${position[1]}`, total || '')
     },
     // 把傳入坐標(biāo)設(shè)置為對(duì)應(yīng)狀態(tài)的svg圖標(biāo)
     setSvg (position, type) {
       this.$set(this.areaSign, `${position[0]}-${position[1]}`, type)
     },
     // 傳入坐標(biāo)與地雷坐標(biāo)數(shù)組判斷是否其周圍存在雷
     positionAroundMineTotal (position) {
       const aroundPositionList = this.getAroundPosition(position[0], position[1])
       return aroundPositionList.filter(item => this.hasTouchMine(item)).length
     },
     // 獲取傳入坐標(biāo)的周圍九宮格坐標(biāo)
     getAroundPosition (xPosition, yPosition) {
       const aroundPositionList = [
         [xPosition - 1, yPosition - 1],
         [xPosition - 1, yPosition],
         [xPosition - 1, yPosition + 1],
         [xPosition, yPosition - 1],
         [xPosition, yPosition + 1],
         [xPosition + 1, yPosition - 1],
         [xPosition + 1, yPosition],
         [xPosition + 1, yPosition + 1]
       ]
       return aroundPositionList.filter(position => isInRange(position[0]) && isInRange(position[1]))
       // 判斷傳入數(shù)字是否在對(duì)應(yīng)范圍中
       function isInRange (num, range = [1, 10]) {
         return num >= range[0] && num <= range[1]
       }
     }

    標(biāo)記坐標(biāo)

    左鍵為點(diǎn)擊方塊,右鍵為標(biāo)記坐標(biāo)(第二次點(diǎn)擊為取消標(biāo)記),當(dāng)該坐標(biāo)為標(biāo)記的時(shí)候,無法進(jìn)行點(diǎn)擊,并且當(dāng)剛好標(biāo)記的坐標(biāo)數(shù)組和地雷數(shù)組一樣時(shí),則游戲結(jié)束,玩家勝利

    代碼實(shí)現(xiàn)

    /*
    *  hasWin 見下文的 vue computed
    */
    sign (rowIndex, colIndex, e) {
    // 判斷游戲當(dāng)前狀態(tài)
      if (!this.gameProcess) {
        this.gameEndConfirm()
        return
      }
      if (this.getAreaSignValueWithPosition(rowIndex, colIndex) === undefined ||
        this.getAreaSignValueWithPosition(rowIndex, colIndex) === statusEnum.normal) {
        // 當(dāng)前坐標(biāo) 為被標(biāo)記過或者以及被取消標(biāo)記 觸發(fā):添加標(biāo)記
        this.setSvg([rowIndex, colIndex], statusEnum.tag)
      } else if (this.getAreaSignValueWithPosition(rowIndex, colIndex) === statusEnum.tag) {
        // 當(dāng)前坐標(biāo) 被標(biāo)記 觸發(fā):取消標(biāo)記
        this.setSvg([rowIndex, colIndex], statusEnum.normal)
      }
      console.log(this.tagList, this.mineList)
      // 檢測游戲是否結(jié)束
      this.gameInspector()
    },
    // 游戲提示
    gameEndConfirm () {
      const message = this.hasWin ? '恭喜你通關(guān),是否繼續(xù)?' : '游戲失敗,是否重新開始?'
      this.confirmMessageBox(message, {
        callback: () => {
          this.resetGame()
        },
        cancelCallback: () => {}
      }, 'confirm')
    },
    // 游戲狀態(tài)檢測員(判斷當(dāng)前游戲是否結(jié)束)
    gameInspector () {
      if (this.hasWin) {
        this.gameEndConfirm()
      }
    },
    // 通過傳入坐標(biāo)返回對(duì)應(yīng)格式的字符串(areaSign的key值)
    getAreaSignAttrWithPosition (xPosition, yPosition) {
      return `${xPosition}-${yPosition}`
    },
    // 通過傳入坐標(biāo)返回areaSign的value值(獲取該坐標(biāo)的狀態(tài))
    getAreaSignValueWithPosition (xPosition, yPosition) {
      return this.areaSign[this.getAreaSignAttrWithPosition(xPosition, yPosition)]
    }
    // 被標(biāo)記列表
    tagList () {
      return Object.keys(this.areaSign)
        .filter(item => this.areaSign[item] === 'tag')
        .map(attrStr => attrStr.split('-').map(str => parseInt(str)))
    },
    // 判斷所有的地雷是否已經(jīng)被標(biāo)記
    hasSignAllMine () {
      return this.tagList.length === this.mineList.length &&
        this.tagList.every(tagPosition => this.hasPositionIn(tagPosition, this.mineList))
    },
    // 游戲是否勝利
    hasWin () {
      return this.hasSignAllMine
    }

    游戲收尾

    游戲失敗或者勝利的時(shí)候需要重置游戲

    代碼實(shí)現(xiàn)

    resetGame () {
      this.gameProcess = true
      this.areaSign = {}
      this.mineList = []
      this.resetOpenedClass()
      // 初始化游戲
      this.initMineListRange()
    },
    // 將class = "opened" 的元素改回 "block" 狀態(tài)
    resetOpenedClass () {
      document.querySelectorAll('.opened').forEach(node => {
        node.className = 'block'
      })
    }

    感謝各位的閱讀,以上就是“怎么使用Vue2+JS實(shí)現(xiàn)掃雷小游戲”的內(nèi)容了,經(jīng)過本文的學(xué)習(xí)后,相信大家對(duì)怎么使用Vue2+JS實(shí)現(xiàn)掃雷小游戲這一問題有了更深刻的體會(huì),具體使用情況還需要大家實(shí)踐驗(yàn)證。這里是億速云,小編將為大家推送更多相關(guān)知識(shí)點(diǎn)的文章,歡迎關(guān)注!

    向AI問一下細(xì)節(jié)

    免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請(qǐng)聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

    AI