用vue+JS制作扫雷游戏的步骤和代码是什么
Admin 2022-11-01 群英技术资讯 260 次浏览
布局就是经典的方格布局,对于场景的美观度可以自行找几个配色网站作为参考。
出现问题: 先初始化一个二维数组对应方块坐标,然后依次渲染 or
直接通过预期的行、列数渲染空白方块
区别: 直接初始化二维数组,可以对坐标进行一些属性操作,例如标记、是否为地雷等等,之后操作的时候会方便很多,缺点在初始化的时候需要进行大量的计算工作(因为在点开一个安全坐标时需要显示周围的地雷个数,还要考虑边缘情况),而渲染空白方块就可以在点击坐标的时候再去做计算,并且在点击的时候只需要计算该方块的属性。
这里我选择了渲染空白方块的形式。
代码实现
使用了 element-ui组件
template
<div class="layout"> <div class="row" v-for="row in layoutConfig.row" :key="row"> <div class="cell" :style="{ width: edgeLength, height: edgeLength }" 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>
生成地雷随机二维数组
因为布局已经通过空白方块生成了,所以我们只需要关心生成随机的地雷坐标就可以了
代码实现:
/* * 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] }) }
指的是游戏中的一些操作以及某个操作导致的一系列变化
点击方块
分析:点击方块之后会出现三种情况
对应这三种情况需要分别有不同的表现形式
第一种情况:(方块的九宫格范围内没有地雷)
这种情况只需要将该方块的样式改为点击过的样式即可(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' }) }
扫雷的实现并不复杂,首先需要对扫雷这个游戏的机制有思路,并且可以将一些逻辑捋清楚就可以了,实现的时候再将一些边缘状态考虑一下。可以更多关注一下对于代码的封装,对于代码的提炼很重要,这样在之后继续开发或者需要修改的时候很容易上手。
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:mmqy2019@163.com进行举报,并提供相关证据,查实之后,将立刻删除涉嫌侵权内容。
猜你喜欢
path模块的方法有哪些,怎么使用?一些朋友可能会遇到这方面的问题,对此在下文小编向大家来讲解一下,内容详细,易于理解,希望大家阅读完这篇能有收获哦,有需要的朋友就往下看吧!
这篇文章主要为大家详细介绍了Vue组件库ElementUI实现表格加载树形数据教程,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
JS有哪些高效的魔法运算符?对于JS运算符大家应该都比较熟悉,但JavaScript在不断更新,也会增加不少高效的运算法,这篇文章给大家分享一些高效的魔法运算符,感兴趣的朋友就继续往下看吧。
使用组件库时,图标往往不能满足需求,所以我们常常需要用到第三方图标库,这篇文章主要给大家介绍了关于vue引入iconfont的相关资料,需要的朋友可以参考下
JWT简单的原理介绍JWT(JsonWebToken)是一种身份验证及授权方案,简单的说就是调用端调用api时,附带上一个由api端颁发的token,以此来验证调用者的授权信息。通过一种加密规则(如HS256)+secret生成一串字符串(token),token字符串中一般含有过期时间和签名信息,根据签名和过期时间,就可以判断当前token是否有效。//new
成为群英会员,开启智能安全云计算之旅
立即注册Copyright © QY Network Company Ltd. All Rights Reserved. 2003-2020 群英 版权所有
增值电信经营许可证 : B1.B2-20140078 粤ICP备09006778号 域名注册商资质 粤 D3.1-20240008