温馨提示×

温馨提示×

您好,登录后才能下订单哦!

密码登录×
登录注册×
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》

怎么使用Vue2+JS实现扫雷小游戏

发布时间:2022-06-18 13:45:37 来源:亿速云 阅读:129 作者:iii 栏目:开发技术

这篇文章主要讲解了“怎么使用Vue2+JS实现扫雷小游戏”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“怎么使用Vue2+JS实现扫雷小游戏”吧!

    实现步骤

    1、场景布局实现

    布局就是经典的方格布局,对于场景的美观度可以自行找几个配色网站作为参考。

    出现问题: 先初始化一个二维数组对应方块坐标,然后依次渲染 or 直接通过预期的行、列数渲染空白方块

    区别: 直接初始化二维数组,可以对坐标进行一些属性操作,例如标记、是否为地雷等等,之后操作的时候会方便很多,缺点在初始化的时候需要进行大量的计算工作(因为在点开一个安全坐标时需要显示周围的地雷个数,还要考虑边缘情况),而渲染空白方块就可以在点击坐标的时候再去做计算,并且在点击的时候只需要计算该方块的属性。

    这里我选择了渲染空白方块的形式。

    代码实现

    使用了 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)"
          >
            // 这里的逻辑现在可以暂时不用管,只需要先做好布局
            <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、初始化事件

    生成地雷随机二维数组

    因为布局已经通过空白方块生成了,所以我们只需要关心生成随机的地雷坐标就可以了

    代码实现

    /*
     *  type: 当前模式的地雷个数(自己定义数量)
     *  mineList: 地雷坐标数组
     *  layoutConfig: {
     *    row: 布局的行数
     *    col: 布局的列数
     *  }
     */   
    
    // 生成随机地雷坐标数组
    initMineListRange () {
      while (this.mineList.length < this.type) {
        this.initMineItem()
      }
    },
    // 生成单个地雷坐标并且放入地雷坐标数组(mineList)中
    initMineItem () {
      const position = this.initPositionRange([1, this.layoutConfig.row], [1, this.layoutConfig.cell])
      if (!this.hasPositionIn(position, this.mineList)) {
        this.mineList.push(position)
      }
    },
    // 生成一个在给定范围内的随机坐标
    initPositionRange ([xStart, xEnd], [yStart, yEnd]) {
      return [this.numRange(xStart, xEnd), this.numRange(yStart, yEnd)]
    },
    // 生成一个在给定范围内的随机整数
    numRange (start, end) {
      return Math.floor((Math.random() * (end - start + 1))) + start
    },
    // 判断参数中的 position 是否已经存在与 参数中的 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、游戏动作(action)

    指的是游戏中的一些操作以及某个操作导致的一系列变化

    点击方块

    分析:点击方块之后会出现三种情况

    • 该方块的九宫格范围内没有地雷

    • 该方块的九宫格方位内有地雷

    • 踩雷了(game over)

    对应这三种情况需要分别有不同的表现形式

    第一种情况:(方块的九宫格范围内没有地雷)

    这种情况只需要将该方块的样式改为点击过的样式即可(class="opened"

    第二种情况:(方块的九宫格方位内有地雷)

    修改样式为opened,并且需要计算周围的地雷数量(需要考虑边缘情况,即当前坐标是否在边缘)

    第三种情况:(踩雷)

    修改样式为opened, 并且展示地雷,提示用户游戏结束

    代码实现

    因为在点击之前该方块是空白对象,所以需要一个对象来存储该方块的属性或者状态(areaSign

    /*
    *  areaSign: Object  key: 坐标('1-2') value: 状态
    *  gameProcess:当前游戏是否处于进行状态
    *  statusEnum: 枚举 方块状态枚举值(fail,normal,tag)
    */
    
    // 方块点击事件 (传入坐标以及点击事件对象)
     open (rowIndex, colIndex, e) {
       // 判断当前游戏是否 
       if (!this.gameProcess) {
         this.gameEndConfirm()
         return
       }
       // 判断当前坐标是否被标记,被标记则不能被点开
       if (this.getAreaSignValueWithPosition(rowIndex, colIndex) === statusEnum.tag) {
         this.confirmMessageBox('该区域已经被标记,请选择其他区域点击')
         return
       }
       
       e.target.className = 'opened'
       if (this.hasTouchMine([rowIndex, colIndex])) {
         // 踩雷
         this.mineTouched([rowIndex, colIndex])
       } else {
         // 第一、二种情况
         this.safeTouched([rowIndex, colIndex])
       }
     },
     // 通过传入的坐标判断是否存在地雷坐标数组中
     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)
     },
     // 把传入坐标通过判断是否有雷设置对应提示
     setTips (position) {
       const total = this.positionAroundMineTotal(position)
       this.$set(this.areaSign, `${position[0]}-${position[1]}`, total || '')
     },
     // 把传入坐标设置为对应状态的svg图标
     setSvg (position, type) {
       this.$set(this.areaSign, `${position[0]}-${position[1]}`, type)
     },
     // 传入坐标与地雷坐标数组判断是否其周围存在雷
     positionAroundMineTotal (position) {
       const aroundPositionList = this.getAroundPosition(position[0], position[1])
       return aroundPositionList.filter(item => this.hasTouchMine(item)).length
     },
     // 获取传入坐标的周围九宫格坐标
     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]))
       // 判断传入数字是否在对应范围中
       function isInRange (num, range = [1, 10]) {
         return num >= range[0] && num <= range[1]
       }
     }

    标记坐标

    左键为点击方块,右键为标记坐标(第二次点击为取消标记),当该坐标为标记的时候,无法进行点击,并且当刚好标记的坐标数组和地雷数组一样时,则游戏结束,玩家胜利

    代码实现

    /*
    *  hasWin 见下文的 vue computed
    */
    sign (rowIndex, colIndex, e) {
    // 判断游戏当前状态
      if (!this.gameProcess) {
        this.gameEndConfirm()
        return
      }
      if (this.getAreaSignValueWithPosition(rowIndex, colIndex) === undefined ||
        this.getAreaSignValueWithPosition(rowIndex, colIndex) === statusEnum.normal) {
        // 当前坐标 为被标记过或者以及被取消标记 触发:添加标记
        this.setSvg([rowIndex, colIndex], statusEnum.tag)
      } else if (this.getAreaSignValueWithPosition(rowIndex, colIndex) === statusEnum.tag) {
        // 当前坐标 被标记 触发:取消标记
        this.setSvg([rowIndex, colIndex], statusEnum.normal)
      }
      console.log(this.tagList, this.mineList)
      // 检测游戏是否结束
      this.gameInspector()
    },
    // 游戏提示
    gameEndConfirm () {
      const message = this.hasWin ? '恭喜你通关,是否继续?' : '游戏失败,是否重新开始?'
      this.confirmMessageBox(message, {
        callback: () => {
          this.resetGame()
        },
        cancelCallback: () => {}
      }, 'confirm')
    },
    // 游戏状态检测员(判断当前游戏是否结束)
    gameInspector () {
      if (this.hasWin) {
        this.gameEndConfirm()
      }
    },
    // 通过传入坐标返回对应格式的字符串(areaSign的key值)
    getAreaSignAttrWithPosition (xPosition, yPosition) {
      return `${xPosition}-${yPosition}`
    },
    // 通过传入坐标返回areaSign的value值(获取该坐标的状态)
    getAreaSignValueWithPosition (xPosition, yPosition) {
      return this.areaSign[this.getAreaSignAttrWithPosition(xPosition, yPosition)]
    }
    // 被标记列表
    tagList () {
      return Object.keys(this.areaSign)
        .filter(item => this.areaSign[item] === 'tag')
        .map(attrStr => attrStr.split('-').map(str => parseInt(str)))
    },
    // 判断所有的地雷是否已经被标记
    hasSignAllMine () {
      return this.tagList.length === this.mineList.length &&
        this.tagList.every(tagPosition => this.hasPositionIn(tagPosition, this.mineList))
    },
    // 游戏是否胜利
    hasWin () {
      return this.hasSignAllMine
    }

    游戏收尾

    游戏失败或者胜利的时候需要重置游戏

    代码实现

    resetGame () {
      this.gameProcess = true
      this.areaSign = {}
      this.mineList = []
      this.resetOpenedClass()
      // 初始化游戏
      this.initMineListRange()
    },
    // 将class = "opened" 的元素改回 "block" 状态
    resetOpenedClass () {
      document.querySelectorAll('.opened').forEach(node => {
        node.className = 'block'
      })
    }

    感谢各位的阅读,以上就是“怎么使用Vue2+JS实现扫雷小游戏”的内容了,经过本文的学习后,相信大家对怎么使用Vue2+JS实现扫雷小游戏这一问题有了更深刻的体会,具体使用情况还需要大家实践验证。这里是亿速云,小编将为大家推送更多相关知识点的文章,欢迎关注!

    向AI问一下细节

    免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

    AI