前言
本次做的小游戏是经典的扫雷游戏,以前的老旧电脑上面会自带扫雷游戏,应该都玩过。以前玩这个的时候,基本上都是乱蒙,也不知道格子里出现的数字是啥意思,也没想着去查。自从后来入了这一行,才慢慢去了解一下,原来每个数字的意思,是数字周围一圈其他8个格子中所包含的雷数,知道这个才慢慢喜欢并上手这个小游戏。
这次我们就来手把手做一个H5版本的《扫雷》。因为用的是uniapp,所以可在小程序或者H5移动版使用,用uniapp主要也是布局样式啥的比较简单快速。后面再做小游戏,会用vue去写。
一、基本布局
这里我使用的布局样式,是参考微信小游戏里面的扫雷,我感觉画面很舒服,就直接拿来参考了。后续我会把做的所有游戏都按照这个样式风格重新修改调整统一。
- 首先是头部的时间与雷数展示
<!-- 时间、雷数 -->
<view class="bomb-head flexItems boxtb">
<view class="bomb-head-time flexCenter flex1">
<view class="bomb-head-icon flexCenter">
<u-icon name="clock" size="80" color="#999"></u-icon>
</view>
<view class="bomb-head-box">{{time}}</view>
</view>
<view class="bomb-head-number flexCenter flex1">
<view class="bomb-head-icon flexCenter">
<u-icon name="/static/image/home/index-bomb.png" size="70"></u-icon>
</view>
<view class="bomb-head-box">{{showBomb}}</view>
</view>
</view>
- 其次是中间战场展示
<!-- 游戏区 -->
<view class="bomb-main flex1 flexCenter">
<scroll-view scroll-y="true" style="width: 100%;height: 100%;">
<view class="bomb-main-box">
<view class="bomb-tr flex" v-for="(item1, index1) in bombList" :key="index1">
<view class="bomb-td" v-for="(item2, index2) in item1" :key="index2" @click="bindBlock(index1,index2)">
<view class="bomb-td-box bomb-td-close" v-if="!item2.is_open">
<u-icon name="/static/image/bomb/flag.png" size="44" v-show="item2.is_flag" />
</view>
<view class="bomb-td-box bomb-td-open" :class="{'bomb-td-boom' : item2.is_bomb}">
<u-icon name="/static/image/home/index-bomb.png" size="50" v-if="item2.is_bomb" />
<text v-else>{{item2.num > 0 ? item2.num : ''}}</text>
</view>
</view>
</view>
</view>
</scroll-view>
</view>
- 最后是底部操作功能展示
<!-- 插旗、翻开、等级 -->
<view class="bomb-foot" v-if="!is_end">
<view class="bomb-foot-btn" :class="{'bomb-foot-btn-hover': bind == 0}" @click="bindBottom(0)">翻 开</view>
<view class="bomb-foot-btn" :class="{'bomb-foot-btn-hover': bind == 1}" @click="bindBottom(1)">插 旗</view>
<view class="bomb-foot-btn" :class="{'bomb-foot-btn-hover': bind == 2}" @click="bindBottom(2)">等 级</view>
</view>
- 当我们点击等级的时候,会有一个等级弹框
<!-- 等级选择 -->
<u-popup v-model="showLevel" mode="center" borderRadius="10">
<view class="bomb-level">
<view class="bomb-level-item fontBold flexColumn flexCenter" @click="bindLevel(0)">
<view class="bomb-level-item-title u-font-40">入 门</view>
<view class="flexItems u-m-t-10">(<u-icon name="/static/image/home/index-bomb.png" size="40"></u-icon>10个<text class="u-m-l-20">10*10</text>)</view>
</view>
<view class="bomb-level-item fontBold flexColumn flexCenter" @click="bindLevel(1)">
<view class="bomb-level-item-title u-font-40">普 通</view>
<view class="flexItems u-m-t-10">(<u-icon name="/static/image/home/index-bomb.png" size="40"></u-icon>30个<text class="u-m-l-20">15*10</text>)</view>
</view>
<view class="bomb-level-item fontBold flexColumn flexCenter" @click="bindLevel(2)">
<view class="bomb-level-item-title u-font-40">地 狱</view>
<view class="flexItems u-m-t-10">(<u-icon name="/static/image/home/index-bomb.png" size="40"></u-icon>50个<text class="u-m-l-20">20*10</text>)</view>
</view>
</view>
</u-popup>
二、实现
1. 定义我们需要的数据
is_pause: false, // 是否暂停
is_end: false, // 是否结束
row: 10, // 行
col: 10, // 列
bombList: [], // 战场二维数组
bomb: 10, // 地雷数量
showBomb: 10, // 地雷数字展示
bind: 0, // 0=翻开 1=插旗 2=暂停
time: 0, // 需要展示的时间
timer: null, // 定时器
level: 0, // 等级
showLevel: false, // 等级选择弹框
2. 初始化战场
首先我们要知道每一块地,展示的样式,默认是砖块,要么是空地、要么数字、要么插旗子、要么有雷。 基本数据初始化好了,我们需要拿出响应的雷数,也就是提前随机埋地雷。 埋完地雷我们还要去计算雷周围每块地的数字。
(1)布置战场
for(let i = 0; i < this.row; i++){
this.bombList[i] = [];
for(let j = 0; j < this.col; j++){
this.bombList[i][j] = {
is_open: false, // 是否翻开
is_bomb: false, // 是否有雷
is_flag: false, // 是否插旗
num: 0, // 提示数字
};
}
}
(2)埋地雷
let num = 0;
for(let a = 1; a > 0; a++){
// 随机选取纵横坐标随机值
let bomb_x = Math.floor(Math.random() * this.row);
let bomb_y = Math.floor(Math.random() * this.col);
// 如果随机的地方没有雷,则可以埋雷
if(!this.bombList[bomb_x][bomb_y].is_bomb){
this.bombList[bomb_x][bomb_y].is_bomb = true;
num++;
}
if(num == this.bomb) break;
}
(3)计算每个雷周围的数字
这里也可以去循环地雷,然后给每个雷周围的格子中的数字+1,也能达到效果
this.bombList.forEach((item1, index1) => {
item1.forEach((item2, index2) => {
// 计算每个格子周围8个方向上格子,是否有雷
if((index1 - 1 >= 0 && index2 - 1 >= 0) && this.bombList[index1 - 1][index2 - 1].is_bomb){
item2.num++;
}
if((index1 - 1 >= 0) && this.bombList[index1 - 1][index2].is_bomb){
item2.num++;
}
if((index1 - 1 >= 0 && index2 + 1 < this.col) && this.bombList[index1 - 1][index2 + 1].is_bomb){
item2.num++;
}
if((index2 - 1 >= 0) && this.bombList[index1][index2 - 1].is_bomb){
item2.num++;
}
if((index2 + 1 < this.col) && this.bombList[index1][index2 + 1].is_bomb){
item2.num++;
}
if((index1 + 1 < this.row && index2-1 >= 0) && this.bombList[index1 + 1][index2 - 1].is_bomb){
item2.num++;
}
if((index1 + 1 < this.row) && this.bombList[index1 + 1][index2].is_bomb){
item2.num++;
}
if((index1 + 1 < this.row && index2 + 1 < this.col) && this.bombList[index1 + 1][index2 + 1].is_bomb){
item2.num++;
}
});
});
(4)游戏开始时,计时器也会开始
// 计时器
clearInterval(this.timer)
this.timer = setInterval(() => {
this.time++;
}, 1000)
3. 点击方块
要注意,我们点击扫雷和插旗,需要跟下面的按钮切换配合 这里有一点就是,当我们点击到空地,这里的空地是没有数字的空地,需要注意,空地会向四面八方延伸,会自动打开周围所有的空地。
bindBlock(x, y){
// 是否结束
if(this.is_end){
return;
}
// 翻开 bind == 0
// 当前是翻开操作,并且没有插旗子、没有打开
if(this.bind == 0 && !this.bombList[x][y].is_flag && !this.bombList[x][y].is_open){
this.bombList[x][y].is_open = true;
this.$forceUpdate();
// 判断是否有雷
if(this.bombList[x][y].is_bomb){
// 如果点到雷了,就把所有雷都展示出来
this.bombList.forEach(item1 => {
item1.forEach(item2 => {
if(item2.is_bomb) item2.is_open = true;
})
});
uni.showModal({showCancel: false, content: "你输了"});
this.is_end = true;
clearInterval(this.timer);
return;
}
// 判断是否是空白,如果是空白,则需要延伸打开空地周围的方块
if(this.bombList[x][y].num == 0){
this.blockExtend(x, y);
}
// 判断是否赢了
if(this.getWin()){
uni.showModal({showCancel: false, content: "你!终!于!赢!了!"});
this.is_end = true;
clearInterval(this.timer);
return;
}
}
// 插旗子 bind == 1
// 只有没翻开的地,才能插旗子,每插一个棋子,或者去掉一个棋子,都会进行响应雷数+1或-1
if(this.bind == 1 && !this.bombList[x][y].is_open){
this.bombList[x][y].is_flag = !this.bombList[x][y].is_flag;
this.bombList[x][y].is_flag ? this.showBomb-- : this.showBomb++;
this.$forceUpdate();
}
}
4. 空地延伸
这块也跟简单,就是判断空地方块周围8个方位是否有空地,利用递归可完成。 [x-1, y], [x+1, y], [x, y-1], [x, y+1], [x-1, y-1], [x+1, y+1], [x-1, y+1], [x+1, y-1]
blockExtend(x, y){
if(x - 1 >= 0 && !this.bombList[x - 1][y].is_bomb && !this.bombList[x - 1][y].is_open && !this.bombList[x - 1][y].is_flag){
this.bombList[x - 1][y].is_open = true;
if(this.bombList[x - 1][y].num == 0){
this.blockExtend(x - 1, y);
}
}
if(x + 1 < this.row && !this.bombList[x + 1][y].is_bomb && !this.bombList[x + 1][y].is_open && !this.bombList[x + 1][y].is_flag){
this.bombList[x + 1][y].is_open = true;
if(this.bombList[x + 1][y].num == 0){
this.blockExtend(x + 1, y);
}
}
if(y - 1 >= 0 && !this.bombList[x][y - 1].is_bomb && !this.bombList[x][y - 1].is_open && !this.bombList[x][y - 1].is_flag){
this.bombList[x][y - 1].is_open = true;
if(this.bombList[x][y - 1].num == 0){
this.blockExtend(x, y - 1);
}
}
if((y + 1 < this.col) && !this.bombList[x][y + 1].is_bomb && !this.bombList[x][y + 1].is_open && !this.bombList[x][y + 1].is_flag){
this.bombList[x][y + 1].is_open = true;
if(this.bombList[x][y + 1].num == 0){
this.blockExtend(x, y + 1);
}
}
if((x - 1 >=0 && y - 1 >= 0) && !this.bombList[x - 1][y - 1].is_bomb && !this.bombList[x - 1][y - 1].is_open && !this.bombList[x - 1][y - 1].is_flag){
this.bombList[x - 1][y - 1].is_open = true;
if(this.bombList[x - 1][y - 1].num == 0){
this.blockExtend(x - 1, y - 1);
}
}
if((x + 1 < this.row && y + 1 < this.col) && !this.bombList[x + 1][y + 1].is_bomb && !this.bombList[x + 1][y + 1].is_open && !this.bombList[x + 1][y + 1].is_flag){
this.bombList[x + 1][y + 1].is_open = true;
if(this.bombList[x + 1][y + 1].num == 0){
this.blockExtend(x + 1, y + 1);
}
}
if((x - 1 >= 0 && y + 1 < this.col) && !this.bombList[x - 1][y + 1].is_bomb && !this.bombList[x - 1][y + 1].is_open && !this.bombList[x - 1][y + 1].is_flag){
this.bombList[x - 1][y + 1].is_open = true;
if(this.bombList[x - 1][y + 1].num == 0){
this.blockExtend(x - 1, y + 1);
}
}
if((x + 1 < this.row && y - 1 >= 0) && !this.bombList[x + 1][y - 1].is_bomb && !this.bombList[x + 1][y - 1].is_open && !this.bombList[x + 1][y - 1].is_flag){
this.bombList[x + 1][y - 1].is_open = true;
if(this.bombList[x + 1][y - 1].num == 0){
this.blockExtend(x + 1, y - 1);
}
}
}
5. 判断输赢
每点开一个方块,就判断剩余方块数是否等于雷数(前提是点开的那个方块不是雷)
getWin(){
let arr = [];
this.bombList.forEach(item1 => {
item1.forEach(item2 => {
if(!item2.is_open){
arr.push(item2);
}
});
});
return arr.length == this.bomb ? true : false;
}
三、总结
这个游戏总体下来比较简单的,不算难,比较适合新手来锻炼代码和逻辑思维能力。
以上就是全部内容,下面是代码地址。 游戏完整代码地址 gitee