热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

Vue2+JS实现扫雷小游戏

Vue2+JS实现扫雷小游戏-目录实现步骤1、场景布局实现2、初始化事件3、游戏动作(action)游戏收尾总结实现步骤1、场景布局实现布局就是经典的方格布局,对于场景的美观度可以

实现步骤

1、场景布局实现

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

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

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

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

代码实现

使用了 element-ui组件

template



class="cell"
:
v-for="col in layoutConfig.cell"
:key="col">
class="block"
@click="open(row, col, $event)"
@contextmenu.prevent="sign(row, col, $event)"
>
// 这里的逻辑现在可以暂时不用管,只需要先做好布局







style:

2、初始化事件

生成地雷随机二维数组

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

代码实现

/*
* type: 当前模式的地雷个数(自己定义数量)
* mineList: 地雷坐标数组
* layoutConfig: {
* row: 布局的行数
* col: 布局的列数
* }
*/
// 生成随机地雷坐标数组
initMineListRange () {
while (this.mineList.length 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)

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

点击方块

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


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

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

这种情况只需要将该方块的样式改为点击过的样式即可(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'
})
}

总结

扫雷的实现并不复杂,首先需要对扫雷这个游戏的机制有思路,并且可以将一些逻辑捋清楚就可以了,实现的时候再将一些边缘状态考虑一下。可以更多关注一下对于代码的封装,对于代码的提炼很重要,这样在之后继续开发或者需要修改的时候很容易上手。




推荐阅读
author-avatar
PHP_sunshine
这个家伙很懒,什么也没留下!
Tags | 热门标签
RankList | 热门文章
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有