文章目录

  • 干货内容推荐
  • 需求背景
  • 一、效果预览
  • 二、技术关键点
  • 2.1 扫雷和排雷
  • 三、完整源码



需求背景

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

一、效果预览

红包扫雷算法java 红包扫雷app开发_i++

二、技术关键点

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>