阿望教你用vue写扫雷小游戏
前言
话说阿望还在大学时,某一天寝室突然停网了,于是和室友两人不约而同地打开了扫雷,比相同难度下谁更快找出全部的雷,玩得不亦乐乎,就这样,扫雷伴我们度过了断网的一周,是整整一周啊,不用上课的那种,可想而知我们是有多无聊了。
这两天临近过年了,该放假的已经放假了,不该放假的已经请假了,公交不打挤了,地铁口不堵了,公司也去了大半部分的人了,就留阿望这种不得不留下来值班的人守着空荡荡的办公室了,于是,多年前那种无所事事的断网心态再次袭来,于是,想玩扫雷的心再次蹦跶出来,于是,点开了电脑的附件,于是,发现我的电脑上并没有扫雷【手动微笑.jpg】。
怎么想起要写扫雷的阿望就不多废话了,直接开干。
扫雷游戏规则
想当年阿望还是在大学才参透扫雷的游戏规则的,初高中的时候都是靠运气瞎点,从没赢过,当然了,境界提升以后,追求的自然就不是赢了,而是速度。
来,看规则:
大白话游戏规则,计算逻辑如下:
游戏开始,选择难度鼠标左键随机点开一个格子A(你想用右键标雷也可以)左键点击后一共有三种情况1)是数字,假设是2,表示以该格子A为中心的九宫格除了该格子以外的8个格子内有2个雷2)是空白,表示以该格子A为中心的九宫格除了该格子以外的8个格子内没有雷,并且系统计算分别以这8个格子为中心九宫格除了该格子以外的8个格子是否是空白a.是空白,继续计算以这8个格子为中心九宫格除了该格子以外的8个格子是否是空白,循环扩散b.是格子边界,停止边界扩散c.是数字,数字格子翻出来显示,停止扩散3)是雷,gameover了。。。如果点第一次只翻了一个数字是比较难开始游戏的,如果第一次翻了一大片出来,恭喜你,可以开始秀你的智商了,在确认是雷点的格子点右键,即可标记地雷格,表示你认定这个格子是地雷如果数字周围的雷被全部标记出来了,但数字周围还有没被翻开的格子的话,双击该格子,自动将其他格子翻开
好,这下规则我们了解了,接下来,摩拳擦掌,开始写代码咯,本次代码用vue来写,没有原因,习惯了而已。
扫雷代码逻辑
看着游戏规则,我们先来理一理,要如何完成这个功能,我们以最简单的,最能使人理解的步骤,来完成这次功能。
- 难度选择
- 根据选择的游戏难度绘制游戏主界面,就是格盘
- 根据选择的游戏难度随机生成地雷放到盘中
- 计算每个格子周围的雷数
- 完成用户交互(左键点击翻格子,右键点击放雷)
- 空白格扩散翻格子
- 计算游戏结束
生成项目
避免vue新手不知如何下手,那就从建项目开始吧,环境安装我就不讲了,脚手架也是要有的
第一步,初始化lovesweeping工程(阿望不喜欢地雷,喜欢小桃心)
项目很小,不需要路由,不需要vuex,即可完成,只带了sass,连eslint都可以不要,看官们可以根据自己的喜好建项目
项目生成之后,helloworld就可以删掉了,它的存在并没有什么意义
文件切分
这一步主要是构建工程结构,简单画一下主要的几个文件
-src -components -SelectLevel.vue[新增,难度选择组件] -LoveSweeping.vue[新增,游戏界面组件] -App.vue[父组件,负责组件间的切换和某些数据传递] -main.js -package.json
好,初步认识了项目结构以后,咱把该新建的建好,可以不加东西,不报错就行
然后就是把组件间的切换代码写好,再来一步步的填充代码
难度选择
这一步很简单,首先画好难度选择的页面,难度可以自己设置,你觉得合理就行,我这里的数据格式是这样的,用一个对象表示一个难度等级,对象中包含了难度描述,以及难度设置,设置中包含了格子横排数,格子纵排数,雷数
//难度 level:[ { text:'青铜',//难度描述 value:[9,9,10]//格子横排数,格子纵排数,雷数 },{ text:'黄金', value:[12,9,20] ]
然后模板中直接渲染列表,这样做的好处是,想要增加难度直接在数组中添加数据即可
该组件中只有一个方法:选择难度之后,跳转到游戏主界面上去,因为项目没有用路由,直接使用组件间的切换,所以,这个方法只负责告诉父组件,我已经选择好难度了,可以开始游戏了
//选择难度 handleChoseLevel(level){ this.$emit("chose-level",level); }
代码如下:
界面长这样,当然,你要觉得难看自己换个样式也行
游戏界面
画格盘
通过游戏难度选择来决定游戏格盘的大小,组件间已通过App将游戏难度传至界面组件中,我们用props把数据接收到,消化成自己的数据,画格盘需要的数据有:横排格子数,纵排格子数
画格子:我们将格子的索引暴露出来,后续可以帮助我们试错。整个格局有两种方式来表示格盘,坐标式和索引式,比如横9纵9的格子,[0,0]代表第1个格子,[2,3]代表第三行第四列也就是第20个格子。此次使用索引式来标志格盘
随机生成地雷
首先data中添加一个minePosition属性,用来记录雷点位置
随机生成地雷比较简单,主要注意,生成的地雷点数在格盘个数范围内,那么就可以写出随机生成的地雷了。界面组件已收集到横排格子数、纵排格子数、雷数,那么就能得到格子总数,假设横9纵9,10个雷,那么就是生成10个81以内的随机数(如果索引从0开始,即80以内)。
//随机获取雷点位置 getMinePosition(){ //定义一个数组装不重复的格点 letmineArr=[]; //循环雷数生成不重复的雷点 for(letn=0;n把地雷位置暴露出来
格子周围的雷数
确认了雷点位置,接下来要做的就是确认每一个非雷点位置周围的雷的数量,我们用对象来描述一个格子,这个对象主要包含以下几个属性
//格子属性 lattice:[{ index:0,//格子索引 mineNum:0,//周围雷数 isMine:false,//是否是雷 isOpen:false,//是否已经被点开 isMark:false,//是否被标记 }],这里我们主要用到index,isMine,mineNum属性,这一步,主要是计算每个格子元素的mineNum值,依赖于以下两个方法,个人觉得扫雷游戏最难理解的,最难捋清的逻辑,其中一个就是获取非雷点位置周围8个位置索引的方法getLatticeIndex(另一个是点击空白格扩散)
//获取格子周围的雷数, getMineNumAroundLattice(lattice,index){ //先获取格子周围的有效索引 constlatticeIndexArr=this.getLatticeIndex(index); //循环索引,索引值在雷点数组中的,即为雷,当前格子的雷点数加1 latticeIndexArr.forEach(i=>{ if(this.minePosition.indexOf(i)>-1){ lattice.mineNum++; } }); }, //获取格子周围的有效索引 getLatticeIndex(index){ //存索引值的变量 letlatticeIndexArr=[]; //当前格子位于第几行 constlatticeRow=Math.ceil(index/this.rows); //当前格子位于第几列(求余为0说明是最右边一列) constlatticeCol=Math.ceil(index%this.rows)||this.rows; //第一行没有上一行,不需要计算减1的行值,最后一行没有下一行,不需要计算加1的行值 for(leti=(latticeRow===1?0:-1);i<(latticeRow===this.cols?1:2);i++){ //第一列没有左列,不需要计算减1的列值,最后一列没有右列,不需要计算加1的列值 for(letj=(latticeCol===1?0:-1);j<(latticeCol===this.rows?1:2);j++){ //索引值=(当前行值+(上一行【-1】/当前行【0】/下一行【+1】)-1【1是索引从0开始,所以需要减去】)*每行格子数+当前列值+(上一列【-1】/当前列【0】/下一列【+1】) constlatticeIndex=(latticeRow+i-1)*this.rows+(latticeCol+j); latticeIndexArr.push(latticeIndex); } } returnlatticeIndexArr; },有了这两个方法,咱成功地获取到了每个非雷点格子周围的雷的数量,来,展示出来,这样展示的好处是,我们一眼就可以看出算法是否正确
没问题了,来,接着往下走,格盘数据基本都设置好了,那我们接下来要做的就是,点开格子操作
点击交互
这一步先做简单点,有个明显的区别就可以了,点雷我们先不管,先看点数字和空白的情况,首先得明确,到时候格子的可见属性是全部要被隐藏的,点击了才会显示出来,这就用到了我们上一步提到的isOpen属性,默认肯定全是不可见的,点击之后,非雷翻开
点数字
点数字很简单,直接翻开,将isOpen属性设置为true
来点不一样的,isOpen===true的时候字体变红色,走你┏(゜ω゜)=☞
@click.left="handleClickLattice(lattice[(col-1)*rows+row-1])" //点了格子 handleClickLattice(lattice){ //是数字 if(lattice.mineNum){ if(!lattice.isOpen&&!lattice.isMark){ lattice.isOpen=true; } } },点空白
第二个难点来咯,点空白格需要注意以下几点:1、空白格表示周围8格都没有雷2、扩散周围8格,判断雷数,循环往复3、遇边界停止扩散,遇数字停止扩散
假设横9纵9的格盘,第二排第三列格为空白格即第12格,那么点了该空白格之后,首先将其与周围8格(2,3,4,11,12,13,20,21,22)一起,isOpen置为true,然后分别以周围8格为中心,判断该格子是数字,停止扩散,是空白格,继续扩散
这一步写完,
//代码把下半部分补齐 handleClickLattice(lattice){ ...else{ //是空白 constlatticeIndexArr=this.getLatticeIndex(lattice.index); this.showWhiteAround(lattice,latticeIndexArr); } }, //展示周围的空白标记,直至边缘(格子边缘或者数字) showWhiteAround(lattice,latticeIndexArr){ //避免有重复的数据停不下来,去个重 latticeIndexArr=[...newSet(latticeIndexArr)]; for(leti=0;i基本明面上的扫雷步骤就已经完成了,handleClickLattice方法再加一步判断,如果是雷,游戏结束
右键标记雷点
这个就很简单了,写个右击事件,修改一下格子的isMark和isOpen属性,这一步的基本逻辑就是
右击格子格子本身已经被打开1)是:格子已经被标记为地雷?a.是:取消标记(isMark和isOpen取反),剩余地雷数+1b.否:说明是右击了已经被点开的数字格,不做任何操作2)否:isMark和isOpen取反,记录该格子已经被标记为地雷,格子处于打开状态,剩余地雷数-1,判断是否结束
//右键确认是雷点 handleSureMinePoint(lattice){ if(!lattice.isOpen){ lattice.isMark=true; lattice.isOpen=true; this.minePosition.splice(this.minePosition.indexOf(lattice.index),1); this.judgeIsOver(); }else{ if(lattice.isMark){ lattice.isMark=false; lattice.isOpen=false; this.minePosition.push(lattice.index); } } },游戏是否结束
游戏结束一共有三种情况:1、点到雷了,直接结束2、雷被标记完了(有可能失败了,标错了)3、翻开的格子数+雷数=总格子数
完成功能
接下来要做的就是把格子属性隐藏起来,假装不知道,再假装鼠标一点,格子就翻过来了。这就用到之前提到的格子属性isMark和isOpen,本身元素处于隐藏状态,当被标记或者被打开的时候设置相应的属性使其可见就行了,如此,便完成了扫雷的基本功能,有兴趣的小朋友可以自己融合多种功能试一试
当然,这只是其中一种实现方式,把所有的计算全部放在玩游戏之前了,爱动脑筋的朋友们也可以想想,如果放在每一次点击时做计算该如何组织代码
阿望的源代码中还集合了【重开一局】、【改变难度】、【游戏计时】等功能,样式兼容手机和PC在线玩,在手机上玩的时候我在纠结手机如何模仿PC端的右键点击标雷操作,没有好的想法,不想用双击,于是多加了一个状态,点击页面【标记】按钮,即表示标记雷点,再点击一次表示还原,正常点开数字格,坐火车无聊的小朋友可以玩一玩哟
查看阿望的源码:mineSweeping
在线试玩:mine-sweeping-online
希望各位看官不吝右上角赐个小星星哦,阿望这厢有礼啦,新年快乐啦★,°:.☆( ̄▽ ̄)/$:.°★。
总结
以上所述是小编给大家介绍的阿望教你用vue写扫雷小游戏,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对毛票票网站的支持!
如果你觉得本文对你有帮助,欢迎转载,烦请注明出处,谢谢!声明:本文内容来源于网络,版权归原作者所有,内容由互联网用户自发贡献自行上传,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任。如果您发现有涉嫌版权的内容,欢迎发送邮件至:czq8825#qq.com(发邮件时,请将#更换为@)进行举报,并提供相关证据,一经查实,本站将立刻删除涉嫌侵权内容。