微信小程序 | 做一个小程序端的扫雷游戏

简介: 微信小程序 | 做一个小程序端的扫雷游戏

需求背景

扫雷游戏作为小时候入手电脑的入门级别游戏,其中有很多的编程知识可以学习得到。本文整理了一个完整的基于vue的小程序端扫雷游戏。我们可以从实现扫雷规则的过程中锻炼到vue的各类语法操作以及前端的样式调整,以及最常用的各类排版布局!😄


一、效果预览

1edaa204624f4fcc920bba9ecdd51e92.gif


二、技术关键点

2.1 扫雷和排雷

在程序中我们通过随机生成的二维数组生成了动态雷区分布图。然后我们就需要用户进行扫雷和排雷操作:

  • 其中,扫雷操作是用户通过@tap绑定点击事件,将特定二维坐标的样式进行转化:如果判断是雷则直接显示地雷图标,如果是数字则正常显示。
  • 排雷操作我们让用户通过@longpress,该方法允许用户通过长按的动作进行触发。


三、完整源码

<template>
  <view class="content">
    <view style="
    background-image: url('https://img1.baidu.com/it/u=2339750922,2310796253&fm=253&fmt=auto&app=138&f=JPEG?w=500&h=500');
    background-repeat: no-repeat;
    background-position: 60% 200%;
    width: 100%;
    height: 100vh;
    position: absolute;
    z-index: -1;
    "></view>
    <view class="titleLine">
      <view @tap="initMap">
        <image src="./imgs/dead.png" v-if="isGameOver"></image>
        <image src="./imgs/smile.png" v-else-if="isGameSuccess"></image>
        <image src="./imgs/smile2.png" v-else></image>
      </view>
      <view style="font-weight: bold;color: #fff;">剩余:{{getRestBoomNum()}}</view>
    </view>
    <view class="contentMap">
      <view style="width: auto; height: auto; overflow: scroll;">
        <view class="placeInRow" v-for="(row,i) in mask" :key="'row-'+i">
          <view class="content" v-for="(block,j) in row" :key="'block-'+j">
            <view v-if="block === 1" class="block">
              <view v-if="maps[i][j] > 0" @tap="setMask(i,j,'open')">{{maps[i][j]}}</view>
              <view v-else-if="maps[i][j] === 0"></view>
              <view v-else>
                <image src="./imgs/boom.png"></image>
              </view>
            </view>
            <view v-else-if="block === 0" class="block mask"
             @tap="setMask(i,j,'open')" @longpress="setMask(i,j,'mask')"
            ></view>
            <view v-else-if="block === -1" class="block mask" @longpress="setMask(i,j,'mask')">
              <image src="./imgs/flag.png"></image>
            </view>
            <view v-else-if="block === 2" class="block mask">
              <image src="./imgs/error.png"></image>
            </view>
          </view>
        </view>
      </view>
    </view>
  </view>
</template>
<script>
  /**
   * @property {Number} width 扫雷地图宽
   * @property {Number} height 扫雷地图高
   * @property {Number} boomNum 雷个数     
   * @property {Function} @init 地图初始化监听,返回游戏地图:-1表示雷,0-9表示周围有几个雷
   * @property {Function} @result 游戏结束监听, code=0成功,其余失败
   * */
  export default {
    props:{
      width:{
        type:Number,
        default:8
      },
      height:{
        type:Number,
        default:8
      },
      boomNum:{
        type:Number,
        default:10,
      }
    },
    watch:{
      width(newVal){
        this.initMap()
      },
      height(newVal){
        this.initMap()
      },
      boomNum(newVal){
        this.initMap()
      }
    },
    data() {
      return {
        maps:[],
        mask:[],
        booms:[],
        isGameOver:false,
        isGameSuccess:false,
        lastAction:'',
      };
    },
    mounted() {
      this.initMap()
    },
    methods:{
      getRestBoomNum(){
        try{
          var maskNum = 0;
          var shownNum = 0;
          for (var i=0;i<this.width;i++){
            for (var j=0;j<this.height;j++){
              if(this.mask[i][j] == -1) maskNum ++;
              if(this.mask[i][j] == 1) shownNum ++;
            }
          }
          // console.log(shownNum, this.booms.length)
          this.$nextTick(function(){
            if(shownNum + this.booms.length == this.width*this.height && !this.isGameSuccess){
              this.isGameSuccess = true;
              for (var i=0;i<this.width;i++){
                for (var j=0;j<this.height;j++){
                  if(this.mask[i][j] == 0 && this.maps[i][j] == -1) this.mask[i][j] = -1
                }
              }
              this.$forceUpdate()
              this.$emit('result', {code:0, msg:'success'})
            }
          })
          return this.booms.length - maskNum;
        }
        catch(e){
          return this.boomNum;
        }
      },
      initMap(){
        this.maps = []
        this.mask = []
        this.isGameOver = false
        this.isGameSuccess = false
        this.booms = []
        for (var i=0;i<this.width;i++){
          this.maps.push([])
          this.mask.push([])
          for (var j=0;j<this.height;j++){
            this.maps[i].push(0),
            this.mask[i].push(0)
          }
        }
        var initBooms = []
        while (initBooms.length < this.boomNum){
          var xy = [
            parseInt(Math.random()*this.width), 
            parseInt(Math.random()*this.height)
          ]
          var hasSame = false;
          for (var 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 (var i=0;i<this.width;i++){
          for (var j=0;j<this.height;j++){
            if(this.maps[i][j] !== -1){
              var 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})
      },
      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(var b=0;b<this.booms.length;b++){
              var theBoomXY = this.booms[b];
              if(this.mask[theBoomXY[0]][theBoomXY[1]] != -1){
                this.mask[theBoomXY[0]][theBoomXY[1]] = 1
              }
            }
            this.isGameOver = true;
            for (var i=0;i<this.width;i++){
              for (var 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.canIOpen(i,j);
          }
        }
        else{
          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()
          })
        }
      },
      canIOpen(i,j,level=0){
        if (this.lastAction != 'open'){
          // 防止误触
          return;
        }
        if(this.maps[i][j] == -1){
          if (level <= 1){
            this.setMask(i,j,'open')
          }
          return;
        }
        this.mask[i][j] = 1;
        var boomSum = 0;
        if(i > 0) {
          if (j > 0 && this.mask[i-1][j-1] == -1) boomSum ++;
          if (this.mask[i-1][j] == -1) boomSum ++;
          if (j < this.height-1 && this.mask[i-1][j+1] == -1) boomSum ++;
        }
        if(i <this.width - 1) {
          if (j > 0 && this.mask[i+1][j-1] == -1) boomSum ++;
          if (this.mask[i+1][j] == -1) boomSum ++;
          if (j < this.height-1 && this.mask[i+1][j+1] == -1) boomSum ++;
        }
        if (j > 0 && this.mask[i][j-1] == -1) boomSum ++;
        if (j < this.height-1 && this.mask[i][j+1] == -1) boomSum ++;
        // console.log(boomSum)
        if (this.maps[i][j] <= boomSum && this.maps[i][j] != -1){
          if(i > 0) {
            if (j > 0 && this.mask[i-1][j-1] == 0) this.canIOpen(i-1,j-1,level+1);
            if (this.mask[i-1][j] == 0) this.canIOpen(i-1,j,level+1);
            if (j < this.height-1 && this.mask[i-1][j+1] == 0) this.canIOpen(i-1,j+1,level+1);
          }
          if(i <this.width - 1) {
            if (j > 0 && this.mask[i+1][j-1] == 0) this.canIOpen(i+1,j-1,level+1);
            if (this.mask[i+1][j] == 0) this.canIOpen(i+1,j,level+1);
            if (j < this.height-1 && this.mask[i+1][j+1] == 0) this.canIOpen(i+1,j+1,level+1);
          }
          if (j > 0 && this.mask[i][j-1] == 0) this.canIOpen(i,j-1,level+1);
          if (j < this.height-1 && this.mask[i][j+1] == 0) this.canIOpen(i,j+1,level+1);
        }
        this.$set(this,'mask',this.mask)
        // console.log(this.mask)
        this.$forceUpdate();
      },
    }
  }
</script>
<style>
  .content {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: space-between;
        background-color: #1b5470;
  }
  .contentMap {
    width: 100%;
    overflow: hidden;
  }
  .titleLine{
    width: 90%;
    display: flex;
    flex-direction: row;
    align-items: center;
    justify-content: space-between;
    padding-top: 10px;
    padding-bottom: 10px;
  }
  .titleLine image{
    width: 60upx;
    height: 60upx;
  }
  .placeInRow{
    display: flex;
    flex-direction: row;
    justify-content: center;
  }
  .block{
    width: 60upx;
    height: 60upx;
    background-color: #ffffffc4;
    border: #9a9a9a solid 1px;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
  }
  .mask{
    background-color: #e6e6e6;
    box-shadow: 2px 2px 5px 5px #bcbcbc inset;
    border: #8d8d8d solid 1px;
  }
  image{
    width: 45upx;
    height: 45upx;
  }
</style>


相关文章
|
2天前
|
小程序 Java API
微信小程序和springboot实现微信支付
微信小程序和springboot实现微信支付
6 0
|
2天前
|
小程序 API
微信小程序-微信支付
微信小程序-微信支付
6 0
|
13天前
|
小程序 开发工具 Android开发
微信小程序开发工具的使用,各个配置文件详解,小程序开发快速入门(二)
微信小程序开发工具的使用,各个配置文件详解,小程序开发快速入门(二)
|
13天前
|
小程序 JavaScript 开发工具
微信小程序开发工具的使用,各个配置文件详解,小程序开发快速入门(一)
微信小程序开发工具的使用,各个配置文件详解,小程序开发快速入门(一)
|
14天前
|
移动开发 小程序 安全
使用阿里云短信+微信短链接跳转微信小程序
此内容是关于使用阿里云短信带传递参数的微信短链接跳转到微信小程序。首先,需要准备微信开发者工具和一个已认证的小程序。接着,开通云开发并配置云开发权限。然后,配置H5静态网页,包括设置云开发权限和处理不同设备的跳转方式。最后,上传云函数并修改其权限,获取微信短信链接,配置短链接参数,并开通阿里云短信服务以进行测试验证。整个过程涉及到了微信开发者工具、云开发、H5页面配置、云函数的创建和部署以及阿里云短信服务的开通和使用等步骤。
40 0
|
23天前
|
小程序 JavaScript Java
基于SpringBoot+Vue+uniapp微信小程序的在线课堂微信小程序的详细设计和实现
基于SpringBoot+Vue+uniapp微信小程序的在线课堂微信小程序的详细设计和实现
32 3
|
23天前
|
小程序 JavaScript Java
基于SpringBoot+Vue+uniapp微信小程序的微信课堂助手小程序的详细设计和实现
基于SpringBoot+Vue+uniapp微信小程序的微信课堂助手小程序的详细设计和实现
56 3
|
23天前
|
小程序 JavaScript Java
基于SpringBoot+Vue+uniapp微信小程序的微信点餐小程序的详细设计和实现
基于SpringBoot+Vue+uniapp微信小程序的微信点餐小程序的详细设计和实现
31 1
|
23天前
|
小程序 JavaScript Java
基于SpringBoot+Vue+uniapp微信小程序的助农扶贫微信小程序的详细设计和实现
基于SpringBoot+Vue+uniapp微信小程序的助农扶贫微信小程序的详细设计和实现
33 2
|
23天前
|
小程序 JavaScript Java
基于SpringBoot+Vue+uniapp微信小程序的微信阅读网站小程序的详细设计和实现
基于SpringBoot+Vue+uniapp微信小程序的微信阅读网站小程序的详细设计和实现
43 2

热门文章

最新文章