加入收藏 | 设为首页 | 会员中心 | 我要投稿 银川站长网 (https://www.0951zz.com/)- 云通信、基础存储、云上网络、机器学习、视觉智能!
当前位置: 首页 > 综合聚焦 > 编程要点 > 语言 > 正文

用vue+JS制作扫雷游戏的过程和代码是什么

发布时间:2023-09-14 12:52:26 所属栏目:语言 来源:
导读:在日常操作或是项目的实际应用中,有不少朋友对于“用vue+JS制作扫雷游戏的步骤和代码是什么”的问题会存在疑惑,下面小编给大家整理和分享了相关知识和资料,易于大家学习和理解,有需要的朋友可以借鉴参

在日常操作或是项目的实际应用中,有不少朋友对于“用vue+JS制作扫雷游戏的步骤和代码是什么”的问题会存在疑惑,下面小编给大家整理和分享了相关知识和资料,易于大家学习和理解,有需要的朋友可以借鉴参考,下面我们一起来了解一下吧。

实现步骤

1、场景布局实现

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

出现问题: 先初始化一个二维数组对应方块坐标,然后依次渲染 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>

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'

})

}

扫雷的实现并不复杂,首先需要对扫雷这个游戏的机制有思路,并且可以将一些逻辑捋清楚就可以了,实现的时候再将一些边缘状态考虑一下。

(编辑:银川站长网)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章