# 扫雷游戏(vue3)

扫雷游戏

简介:前段时间玩了一下扫雷,还挺感兴趣的,最近闲来无事,尝试自己写一下扫雷游戏。就当是消磨时间啦! 各位大人们,写的不好的话还请见谅。

扫雷界面截图

视频

扫雷游戏实现步骤

step1:搭建二维数组

step2:点击格子事件

step3:右击插旗事件

step4:重新开始游戏

step1:搭建二维数组

该部分代码包含生成的扫雷数据、页面布局、扫雷游戏显示的样式。

 <script setup>
 import { ref } from 'vue'
 // 列数
 const column = ref(10) 
 // 行数
 const row = ref(10) 
 // 炸弹数
 const bombNumber = ref(10)
 // 1、初始化网格数据
 const initGridData = () => {
   // 1.1 生成10*10的二维数组
   let arr = []
   for (let i = 0; i < row.value; i++) {
     arr[i] = []
     for (let j = 0; j < column.value; j++) {
       arr[i][j] = { count: 0, isBomb: false, isOpen: false, isFlag: false, position: { x: i, y: j } }
     }
   }
   // 1.2 随机生成炸弹
   let bomb = bombNumber.value
   while (bomb > 0) {
     const rowIndex = Math.floor(Math.random() * row.value)
     const columnIndex = Math.floor(Math.random() * column.value)
     if (arr[rowIndex][columnIndex].isBomb == false) {
       arr[rowIndex][columnIndex].isBomb = true
       bomb--
     }
   }
   // 1.3 生成炸弹周围数字
   for (let i = 0; i < arr.length; i++) {
     for (let j = 0; j < arr[i].length; j++) {
       if (arr[i][j].isBomb === false) {
         let count = 0
         for (let m = i - 1; m <= i + 1; m++) {
           for (let n = j - 1; n <= j + 1; n++) {
             if (
               m >= 0 &&
               m < arr.length &&
               n >= 0 &&
               n < arr[j].length &&
               arr[m][n].isBomb === true
             ) {
               count++
             }
           }
         }
         arr[i][j].count = count
       }
     }
   }
   return arr
 }
 // 生成网格
 let grid = initGridData()
 // 复制网格数据使其具有响应式 (实际真正使用的数据)
 const gridArr = ref(grid) 
 </script>
 ​
 <template>
   <div class="game-container">
     <div class="game-title">🎮扫雷游戏🎮</div>
     <div class="game-header">
       <el-link :underline="false" style="color:#409eff" @click="handleGameReStart">
         <svg-icon name="zhongxinkaishihover" width="20px" height="20px" style="margin-right:5px" />重新开始</el-link>
     </div>
     <div class="game-body">
       <div id="game-grid">
         <div v-for="(item, index) in gridArr" :key="index">
           <div :class="{ 'game-over': isGameOver, active: t.isOpen }" v-for="t in item" :key="t" class="grid-item"
             @click="handleChangeOpen(t)" @contextmenu.prevent="handleRightClick(t,$event)"
             :style="{ color: gameStatus ? 'green' : 'red' }">
             <div v-if="t.isOpen">
               <span v-if="t.isBomb">💣</span>
               <span v-else>{{ t.count === 0 ? '' : t.count }}</span>
             </div>
             <div v-if="t.isFlag">🚩</div>
           </div>
         </div>
       </div>
     </div>
     <div class="game-over game-over-text game-footer" :style="{ color: gameStatus ?'green':'red'}" v-if="isGameOver">
       {{
       gameOverText }}</div>
   </div>
 </template>
 <style scoped lang="scss">
 .game-container {
   height: 100%;
   width: 100%;
   .game-title{
     height: 30px;
     line-height: 30px;
     text-align: center;
     font-size: 25px;
     color: #fff;
     font-weight: bold;
   }
   .game-header {
     height: 50px;
     width: 100%;
     display: flex;
     justify-content: center;
     align-items: center;
     .el-link{
       margin: 0 10px;
     }
   }
   .game-body{
     width: 100%;
     display: flex;
     justify-content: center;
   }
   .game-footer{
     height: 100px;
     width: 100%;
     display: flex;
     justify-content: center;
     align-items: center;
   }
 }
 ​
 #game-grid {
   display: grid;
   grid-template-rows: repeat(10, 2rem);
   grid-template-columns: repeat(10, 2rem);
 }
 ​
 .grid-item {
   width: 2rem;
   height: 2rem;
   background-color: #ddd;
   border: solid 2px;
   border-top-color: #eee;
   border-left-color: #eee;
   border-right-color: #ccc;
   border-bottom-color: #ccc;
   display: flex;
   justify-content: center;
   align-items: center;
 }
 ​
 .active {
   background-color: #ccc;
   color: green;
 }
 ​
 .game-over {
   pointer-events: none;
 }
 ​
 .game-over-text {
   font-size: 1.5rem;
 }
 </style>

step2:点击格子事件

此代码主要包含点击格子之后可能发生的情况:当前的格子已经被打开或者被插旗、当前的格子是💣、当前的格子是空白格子、当前的格子是数字等。(将代码复制到script标签中)

// 点击某个格子(鼠标左击(单击))
 const handleChangeOpen = (row) => {
   // (1) 判断是否被打开过 或者被插上🚩(如果被打开或者被插旗则不进行打开)
   if (row.isOpen || row.isFlag) {
     return
   }
   // (2) 判断是否是雷 如果是雷 打开盒子并显示游戏结束
   if (row.isBomb) {
     row.isOpen = true
     gameOver('error')
     return
   }
   // (3) 判断是否是数字 如是数字直接进行打开 (并判断是否胜利)
   if (row.count) {
     row.isOpen = true
     if (allNonBombsAreRevealed()) {
       gameOver('success')
     }
     return
   }
   // (4) 如果是空白格 需要将空格旁边的空格进行递归打开
   row.isOpen = true
   openBlankGrid(row)
 }
 // 递归打开空白格
 const openBlankGrid = (p) => {
   const { x, y } = p.position
   for (let i = Math.max(x - 1, 0); i <= Math.min(x + 1, 9); i++) {
     for (let j = Math.max(y - 1, 0); j <= Math.min(y + 1, 9); j++) {
       if (i === x && j === y) continue
       const neighbor = gridArr.value[i][j]
       if (!neighbor.isBomb && !neighbor.isOpen && !neighbor.isFlag) {
         neighbor.isOpen = true
         if (neighbor.count === 0) {
           openBlankGrid(neighbor)
         }
       }
     }
   }
 }
 // 判断是否成功完成排雷任务 (如果没有打开的格子数 === 设置的炸弹数则挑战成功)
 function allNonBombsAreRevealed() {
   let revealedCells = 0
   for (let i = 0; i < grid.length; i++) {
     for (let j = 0; j < grid[i].length; j++) {
       if (!grid[i][j].isOpen) {
         revealedCells++;
       }
     }
   }
   return revealedCells === bombNumber.value;
 }

step3:右击插旗事件

鼠标右击首先阻止默认事件、其次将isFlag属性取反 (鼠标右击可切换插旗的状态)(同样将代码复制到script标签中)

 // 鼠标右击单元格(进行插旗工作)
 const handleRightClick = (t,$event) => {
   $event.preventDefault(); // 阻止默认的上下文菜单行为
   t.isFlag = !t.isFlag
 }

step4:重新开始游戏

重新生成grid二维数组、将gameOver有关的字段进行重置(同样将代码复制到script标签中)

// 重新开始游戏
 const handleGameReStart = () => {
   grid = initGridData()
   gridArr.value = grid
   isGameOver.value = false
   gameStatus.value = true
 }

总结:以上代码基本上能实现简单的扫雷功能,目前该代码还不支持游戏难度的切换和自定义游戏难度,有兴趣的大人们,也可以自己去试试,后面我也会抽时间进行完善。

  • 7
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值