我正在参加掘金社区游戏创意投稿大赛团队赛,详情请看:游戏创意投稿大赛
体验方式 小程序搜索 九霄雷霆之扫雷, 仓库地址
我这边主要是更改了游戏界面,先来几张图
一、游戏介绍及思路分析
首先,我先简单的介绍一下扫雷游戏的基本规则(了解规则的跳过即可)
- 1、创建一个X行X列的二维数组。
- 2、创建雷:随机行数 i 和列数 j,根据随机到 i、j 从二维数组中取出对应的元素,将取到的元素设置一个属性type等于1,表示当前元素已经是雷,并且递增雷计数器,然后递归调用;如果取到的元素已经是雷了,则跳过继续执行,雷计数器达到设定的最大值就跳出递归
- 3、计算每个元素周围的雷数量,当前位置显示对应的数字
- 4、同样根据这个二维数组来创建遮罩的小方块,正好盖住之前创建的图形。
- 5、点击这个遮罩的小方块则触发揭开,揭开后根据对应的数字或者雷做不同的操作。
二、静态页面制作(核心代码)
<view class="placeInRow" :class="classMax" v-for="(row,i) in mask" :key="'row-'+i">
<view class="content" v-for="(block,j) in row" :key="'block-'+j">
<!-- 0 表示没有探过 1 表示探过 -1表示立flag 2表示flag错了 -->
<view v-if="block === 1" class="block">
<view class="open" v-if="maps[i][j] > 0" >{{maps[i][j]}}</view>
<view v-else-if="maps[i][j] === 0" class="none" ></view>
<view class="boom" v-else></view>
</view>
<view v-else-if="block === 0" class="block "
@tap="setMask(i,j,'open')" @longpress="setMask(i,j,'mask')"
></view>
<view v-else-if="block === -1" class="block " @longpress="setMask(i,j,'mask')">
<image src="../../static/imgs/flag.png"></image>
</view>
<view v-else-if="block === 2" class="block ">
<image src="../../static/imgs/error.png"></image>
</view>
</view>
</view>
三、创建雷及显示对应的图片(雷、数字)
1.随机row 和 col,并从二维数组中获取到这个对象;
2.判断他是否是雷,如果是则跳过当前;
3.如果当前不是雷,则标记当前对象为雷对象,并且更改图片;
4.递归,当达到设定的数量时跳出。
this.maps = []
this.mask = []
this.isGameOver = false
this.isGameSuccess = false
this.booms = []
/*
1.随机row 和 col,并从二维数组中获取到这个对象
2.判断他是否是雷,如果是则跳过当前
3.如果当前不是雷,则标记当前对象为雷对象,并且更改图片
4.递归,当达到设定的数量时跳出
*/
for (let i=0;i<this.width;i++){
this.maps.push([])
this.mask.push([])
for (let j=0;j<this.height;j++){
this.maps[i].push(0)
this.mask[i].push(0)
}
}
let initBooms = []
while (initBooms.length < this.boomNum){
let xy = [
parseInt(Math.random()*this.width),
parseInt(Math.random()*this.height)
]
let hasSame = false;
for (let b =0; b<initBooms.length;b++){
if(initBooms[b][0] == xy[0] && initBooms[b][1] == xy[1]){
hasSame = true;
break;
}
}
if (!hasSame){
initBooms.push(xy)
this.maps[xy[0]][xy[1]] = -1;
}
}
this.booms = initBooms;
// 计算周围雷的数量
for (let i=0;i<this.width;i++){
for (let j=0;j<this.height;j++){
if(this.maps[i][j] !== -1){
let boomSum = 0;
if(i > 0) {
if (j > 0 && this.maps[i-1][j-1] == -1) boomSum ++;
if (this.maps[i-1][j] == -1) boomSum ++;
if (j < this.height - 1 && this.maps[i-1][j+1] == -1) boomSum ++;
}
if(i < this.width - 1) {
if (j > 0 && this.maps[i+1][j-1] == -1) boomSum ++;
if (this.maps[i+1][j] == -1) boomSum ++;
if (j < this.height - 1 && this.maps[i+1][j+1] == -1) boomSum ++;
}
if (j > 0 && this.maps[i][j-1] == -1) boomSum ++;
if (j < this.height - 1 && this.maps[i][j+1] == -1) boomSum ++;
this.maps[i][j] = boomSum
}
}
}
this.$emit('init',{maps:this.maps})
console.log(this.booms,this.maps);
四、增加音乐 及对应音乐的操作(开始,停止,暂停,切换音乐等)
initMusic(){
!this.startAudio&& (this.startAudio = uni.createInnerAudioContext())
this.startAudio.src ='https://vkceyugu.cdn.bspapp.com/VKCEYUGU-3c5dd365-9db5-4687-8b00-e7c4a23710e1/d53274e0-321b-4e55-9ebc-e4dd86ef3e9b.mp3'; ; // 开场音效
!this.didaAudio &&(this.didaAudio = uni.createInnerAudioContext())
this.didaAudio.loop=true;
this.didaAudio.src= 'https://vkceyugu.cdn.bspapp.com/VKCEYUGU-3c5dd365-9db5-4687-8b00-e7c4a23710e1/edbc4906-940d-4add-b99b-94f014525cdc.mp3'// 氛围音效x
// 启动之后开始氛围渲染
this.startAudio.onEnded( ()=>{
this.playDida()
})
!this.flagAudio && (this.flagAudio = uni.createInnerAudioContext())
this.flagAudio.src='https://vkceyugu.cdn.bspapp.com/VKCEYUGU-3c5dd365-9db5-4687-8b00-e7c4a23710e1/a0a4d7c4-1b53-4554-9e71-7c79844458b8.wav'; // 插旗音效
!this.boomAudio &&(this.boomAudio = uni.createInnerAudioContext())
this.boomAudio.src='https://vkceyugu.cdn.bspapp.com/VKCEYUGU-3c5dd365-9db5-4687-8b00-e7c4a23710e1/d9ce405b-9410-4595-939b-7207cb8e2f72.mp3'; // 炸雷音效
// 没踩到雷的
!this.normalAudio && (this.normalAudio = uni.createInnerAudioContext() );
this.normalAudio.src = 'https://vkceyugu.cdn.bspapp.com/VKCEYUGU-3c5dd365-9db5-4687-8b00-e7c4a23710e1/4b6ffade-9811-43e6-b378-fefc879e49b9.mp3'
}
switchMusic(){
this.quiet = !this.quiet ;
let volume = Number(!this.quiet)
this.startAudio.volume = volume ;
this.didaAudio.volume = volume ;
this.flagAudio.volume = volume ;
this.boomAudio.volume = volume ;
},
playStart(){ //播放音乐
this.startAudio.play()
} ,
playDida(){
this.didaAudio.play()
} ,
pauseDida(){ //暂停音乐
this.didaAudio.pause()
} ,
playFlag(){
this.flagAudio.play()
} ,
playBoom(){
this.boomAudio.play()
} ,
playNormal(){
this.normalAudio.play()
} ,
五、块的点击事件
setMask(i,j,action){
// action 可以是 open 或 mask
this.lastAction = action;
if (this.isGameOver || this.isGameSuccess){
return;
}
else if (action === 'open'){
if (this.maps[i][j] === -1){
// 踩到雷了
for(let b=0;b<this.booms.length;b++){
let theBoomXY = this.booms[b];
if(this.mask[theBoomXY[0]][theBoomXY[1]] != -1){
this.mask[theBoomXY[0]][theBoomXY[1]] = 1
}
}
console.log(this.mask)
this.isGameOver = true;
this.startAudio.pause() // 有可能片头曲还没完就炸了
this.pauseDida()
this.playBoom()
for (let i=0;i<this.width;i++){
for (let j=0;j<this.height;j++){
if(this.mask[i][j] == -1 && this.maps[i][j] != -1) this.mask[i][j] = 2;
}
}
this.$forceUpdate()
this.$emit('result', {code:-1, msg:'failed'})
}
else{
this.playNormal() // 先声夺人
this.canIOpen(i,j);
console.log(this.mask)
}
}
else{
this.playFlag()
if(this.mask[i][j] == 0)
this.mask[i][j] = -1;
else if(this.mask[i][j] == -1)
this.mask[i][j] = 0;
// console.log(i,j,this.mask[i][j])
this.$nextTick(function(){
this.$forceUpdate()
})
}
}
主要涉及的知识点:
1、雷区和数字的制作与分布 2、侦听用户的点击及对相应事件进行处理