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

news2024/9/21 3:25:23

📌个人主页:个人主页
​🧀 推荐专栏:小程序开发成神之路 --【这是一个为想要入门和进阶小程序开发专门开启的精品专栏!从个人到商业的全套开发教程,实打实的干货分享,确定不来看看? 😻😻】
📝作者简介从web开发,再到大数据算法,踩过了无数的坑,用心总结经验教训,助你在技术生涯一臂之力!若想获取更多精彩内容,敬请订阅专栏或者关注😁😂🤣😃😆😉😊😋😍😘🥰
⭐️您的小小关注是我持续输出的动力!⭐️


干货内容推荐

🥇入门和进阶小程序开发,不可错误的精彩内容🥇 :

  • 《小程序开发必备功能的吐血整理【个人中心界面样式大全】》
  • 《微信小程序 | 动手实现双十一红包雨》
  • 《微信小程序 | 人脸识别的最终解决方案》
  • 《来接私活吧?小程序接私活必备功能-婚恋交友【附完整代码】》
  • 《吐血整理的几十款小程序登陆界面【附完整代码】》

文章目录

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


需求背景

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

一、效果预览

在这里插入图片描述

二、技术关键点

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>

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/21949.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【第三部分 | 移动端开发】3:Flex布局

目录 | Flex布局简介 | Flex父元素属性 设置主轴的方向 flex-direction 设置主轴上的子元素排列方式 justify-content 设置子元素是否换行 flex-wrap 设置侧轴上的子元素排列方式&#xff08;单行&#xff09; align-items 设置侧轴上的子元素的排列方式&#xff08;多行…

HIve数仓新零售项目DWD层的构建

HIve数仓新零售项目 注&#xff1a;大家觉得博客好的话&#xff0c;别忘了点赞收藏呀&#xff0c;本人每周都会更新关于人工智能和大数据相关的内容&#xff0c;内容多为原创&#xff0c;Python Java Scala SQL 代码&#xff0c;CV NLP 推荐系统等&#xff0c;Spark Flink Kaf…

网络是怎样链接的--向DNS服务器查询Web服务器的IP地址

文章目录2.1 IP地址的基本知识2.2 域名和IP地址共用理由2.3 DNS本质是什么2.4 浏览器如何获取IP2.5 DNS解析器内部工作原理2.1 IP地址的基本知识 浏览器能够解析网址并生成HTTP消息&#xff0c;但并不具备将消息发送到网络中的功能&#xff0c;因此这一功能需要委托操作系统来…

DataObjectImpl

DataObjectImpl目录概述需求&#xff1a;设计思路实现思路分析1.DataObjectImpl2.DeadLetterJobQueryImpl3.DeploymentQueryImpl4.Direction5.DynamicBpmnServiceImpl参考资料和推荐阅读Survive by day and develop by night. talk for import biz , show your perfect code,fu…

IDEA2022版本创建maven web项目(两种方式)

目录 一、使用骨架的方式 二、maven中添加 web方式 总结&#xff1a; 前言&#xff1a; 创建maven web项目有两种方式&#xff0c;一种是使用骨架方式&#xff0c;一种是不使用骨架的方式 一、使用骨架的方式 1.打开idea&#xff0c;按照步骤创建一个新的项目 2.点击Mave…

【高级篇】Java JVM实战 之 内存调优

文章目录一、通过Jprofiler调式Dump文件错误⛅ 什么是Jprofiler&#xff1f;⚡使用Jprofiler调试Dump文件二、堆内存调优三、 GC垃圾回收器四、GC常用算法❄️引用计数法⛄复制算法♨️标记清除算法⛽标记压缩⚠️标记清除压缩五、JMM⛵小结一、通过Jprofiler调式Dump文件错误 …

Spark 3.0 - 4.Pipeline 管道的工作流程

目录 一.引言 二.基本组件 三.Pipeline 基本流程 1.训练 Pipeline - Estimator 2.预测 Pipeline - Transformer 四.Pipeline 分解与构造 1.DataFrame 2.Transformer1 - Tokenizer 3.Transformer2 - HashingTF 4.Estimator - LR 5.Pipeline With ParamMap - Estimat…

SpringCloud微服务(一)——Consul服务注册中心

Consul服务注册中心 SpringCloud 中文官网&#xff1a;https://www.springcloud.cc/spring-cloud-consul.html Consul是一套开源的分布式服务发现和配置管理系统&#xff0c;Go语言开发。 Consul是一个服务网格&#xff08;微服务间的 TCP/IP&#xff0c;负责服务之间的网络…

SharedPreferences存储

文章目录 前言 听说SharedPreferences存储技术快过时了&#xff0c;不过如果是单纯的使用的话&#xff0c;不费什么时间成本。 本文的Demo摘录自《第一行代码》。 一.什么是SharedPreferences SharedPreferences&#xff0c;一种通过使用键值对的方式来存储数据的技术。 二…

【深入浅出Spring6】第八期——面向切面编程 AOP

AOP&#xff08;Aspect Oriented Programming&#xff09;面向切面编程&#xff0c;属于面向对象编程的一种衍射&#xff0c;是一种编程思想或技术AOP的底层是由动态代理机制实现的 JDK动态代理CGLIB动态代理&#xff0c;自动识别并切换我们也可以通过配置属性指定就是用CGLIB …

【MySQL】六,sql_model的合理设置

宽松模式和严格模式 宽松模式 如果设置的是宽松模式&#xff0c;那么我们在插入数据的时候&#xff0c;即使是给了一个错误的数据&#xff0c;那么可能也不会报错。 举例&#xff1a;某张表的name字段为 char(10) &#xff0c;插入数据的时候&#xff0c;如果name字段的数据长…

免费搜题系统

免费搜题系统 本平台优点&#xff1a; 多题库查题、独立后台、响应速度快、全网平台可查、功能最全&#xff01; 1.想要给自己的公众号获得查题接口&#xff0c;只需要两步&#xff01; 2.题库&#xff1a; 查题校园题库&#xff1a;查题校园题库后台&#xff08;点击跳转&a…

跨模态神经搜索实践VCED 基于Streamlit实现前端页面设计和逻辑

1. Streamlit入门 1.1 Streamlit介绍 Streamlit是基于Python的Web应用程序框架&#xff0c;它可以使用Python代码轻松构建机器学习/数据科学相关的仪表板&#xff0c;其特点包括&#xff1a; 跨平台&#xff1a;支持Windows、macOS、Linux只需要掌握Python&#xff1a;不需要…

【时序】时间序列数据预处理

目录 1. 时间戳转换 2. 缺失值处理 3. 去噪 1&#xff09;滚动平均值 2&#xff09;傅里叶变换 4. 异常点检测 1&#xff09;基于滚动统计的方法 2&#xff09;孤立森林 3&#xff09;K-means 聚类 为了分析预处理结果&#xff0c;我们后续使用 Kaggle 的 Air Passenge…

【Python】发布一个简单好用的日志记录器bestlog

需求 日志是非常重要的一个东西&#xff0c;我们往往习惯于在开发一个新项目的第一行代码时&#xff0c;就用 logging.info 代替 print&#xff0c;随时保持记录的好习惯&#xff0c;等代码上线以后也无需修改替换那些 print&#xff0c;直接开跑&#xff0c;有了完善的日志&a…

牛客刷题——Python入门总结

&#x1f935;‍♂️ 个人主页: 北极的三哈 个人主页 &#x1f468;‍&#x1f4bb; 作者简介&#xff1a;Python领域优质创作者。 &#x1f4d2; 系列专栏&#xff1a;《Python入门学习》《牛客题库-Python篇》 &#x1f310;推荐《牛客网》——找工作神器|笔试题库|面试经…

【软考软件评测师】第三十章 操作系统(PV操作与死锁)

【软考软件评测师】第三十章 操作系统&#xff08;PV操作与死锁&#xff09; 第三十章 操作系统&#xff08;PV操作与死锁&#xff09;【软考软件评测师】第三十章 操作系统&#xff08;PV操作与死锁&#xff09;第一部分 知识点集锦1.PV操作1&#xff09;P操作的定义2&#xf…

win11的文件属性默认显示全部,Windows11右键菜单修改为Win10模式的方法(手把手详细操作)

win11的文件属性默认显示全部&#xff0c;Windows11右键菜单修改为Win10模式的方法&#xff08;手把手详细操作&#xff09; 文章目录win11的文件属性默认显示全部&#xff0c;Windows11右键菜单修改为Win10模式的方法&#xff08;手把手详细操作&#xff09;Tips 1 先以管理员…

Source Map知多少?Golang手写SourceMap转换过程

文章目录一、问题背景二、Source Map 简介基本格式应用场景三、Source Map 的工作原理四、Source Map 的转换过程代码示例总结本文从原理的角度入手对 Source Map 进行了较为深入的分析&#xff0c;并从业务需要的角度出发&#xff0c;手动编写根据 Source Map 映射编码前后代码…

SpringBoot集成Mybatis项目实操

本文为《从零打造项目》系列第三篇文章&#xff0c;首发于个人网站。 《从零打造项目》系列文章 比MyBatis Generator更强大的代码生成器 SpringBoot项目基础设施搭建 前言 基于 orm-generate 项目可以实现项目模板代码&#xff0c;集成了三种 ORM 方式&#xff1a;Mybatis、M…