【方块消消乐】方块消除游戏-微信小程序开发流程详解
有做过俄罗斯方块游戏小程序的经验,这次有做了一个消灭方块的游戏,实现过程很顺利,游戏看着和之前做的俄罗斯方块游戏很像,这里调整了玩法,试玩感觉还可以,接下来给大家讲一讲消灭方块游戏开发过程。
俄罗斯方块游戏文章 【俄罗斯方块】单机游戏-微信小程序项目开发入门
文章目录
- 小程序
- 初始页面
- 游戏页面
- 游戏逻辑
- 游戏背景
- 游戏方块
- 开始游戏
- 选择方块
- 拖动方块
- 消灭方块
- 游戏测试
这里的消灭方块游戏,也叫方块消消乐游戏,
小程序
用微信开发工具打开,新建一个小程序项目,例如项目名miniprogram-BoxDesalination,
如下图,依次选择即可

- AppID 选自己申请的测试号
- 不使用云服务(不免费)
- JavaScript - 基础模板
这里选创建小程序项目,因为这开发比小游戏项目开发难度低了不少,很适合新手学习
初始页面
初始页面文件位置在/pages/index/index,
在页面index.wxml布局文件中添加按钮,添加内容如下,点击按钮进入游戏,
<view><button type="default" bindtap="onClickKey" data-key="enter">进入游戏</button>
</view>
然后在页面index.js逻辑文件中添加进入游戏的点击事件
Component({methods: {// 事件处理函数onClickKey(e) {wx.navigateTo({url: '/pages/game/game',})}}
}
现在这个时间点,微信开发工具自动创建的小程序项目的初始页面跟旧版的不同了,
初始页面以前是用页面Pages对象,
现在发现是用的自定义组件Component展示,添加的方法是在methods里面的
游戏页面
创建一个游戏页面,文件位置在/pages/game/game,
打开页面game.wxml布局文件,布局内容如下
<!--pages/game/game.wxml-->
<view class="page"><canvas class="canvas" type="2d" id="zs1028_csdn" bindtouchstart="onTouchStart" bindtouchmove="onTouchMove" bindtouchend="onTouchEnd" disable-scroll="{{true}}"></canvas>
</view>
只需要放一个组件canvas就好
游戏逻辑
接下来,准备写游戏逻辑,首先思考如何初始化页面,
打开页面game.js逻辑文件,在里面的onReady()方法里写初始化代码,得到一个canvas
// pages/game/game.js
import ZS1028_CSDN from '../../utils/zs1028_CSDN.js'
import BLOCK from '../../utils/block.js'const app = getApp()Page({/*** 生命周期函数--监听页面初次渲染完成*/async onReady() {const { node:canvas, width, height } = await ZS1028_CSDN.query('#zs1028_csdn')Object.assign(canvas, { width, height })//创建小游戏引擎(框架)对象,传入canvasconst game = ZS1028_CSDN.createMiniGameEngine({canvas,// isTest: true //性能测试用途})this.game = game// 初始化游戏数据...// 背景和网格数据const bgGridsData = {...}// 所有的方块数据const blocksData = {...}// 游戏状态数据const stateData = {...}// 初始化游戏数据都放到gameData中,方便下次获取this.gameData = {bgGridsData,blocksData,stateData}// 调用这个会在画布中划分一块区域在顶部显示游戏状态game.initTopBar({data:stateData,reset(){...},redraw(data){...}})// 将上面的方法都加入到游戏引擎对象中...game.addBgObject({data:bgGridsData,reset(){...},redraw(data){...},methods:{...}})game.addBgObject({data:blocksData,reset(){...},redraw(data){...},methods:{...}})// 调用开始游戏方法this.restart()},/*** 以下都是触摸产生事件调用的方法*/onTouchStart(e) {...},onTouchMove(e) {...},onTouchEnd(e) {...},
)}
看上面,用了两个模块,省了很多代码,这样实现和读代码很容易;
- 模块
ZS1028_CSDN,是小游戏引擎(框架)对象,154行代码;- 模块
BLOCK,是封装了方块的一些处理方法,186行代码;
游戏背景
初始化的游戏背景对象是bgGridsData,如下代码,看看有哪些数据
const bgGridsData = {grids1:[],//上面的网格grids2:[],//下面的网格cols: 20,//网格列数isShowGrids: false //是否展示网格,调试用途,默认不展示
}
将背景对象添加到game对象的属性data中,
如下代码,在方法redraw()实现绘制背景
game.addBgObject({data:bgGridsData,reset(){...}, //这里是可实现重置背景数据的方法redraw(data){const { topBar, canvas, context:ctx } = data// 如果没有背景图片,就需要绘制出来,也是背景初始化的逻辑if (!this.cacheImg) {//省略更多...在这里实现绘制背景和网格//绘制好了,就导出为图像对象let img = canvas.createImage()img.onload = () => this.cacheImg = imgimg.src = canvas.toDataURL()//最后要处理保存背景数据...return}// 有背景图像了,这里就把图像绘制出来ctx.drawImage(this.cacheImg,0,0,canvas.width,canvas.height)// 这里判断初始化的网格大小if (bgGridsData.gridSize>0) {// 设定方块的颜色ctx.strokeStyle = '#000000'ctx.fillStyle = '#000000'// 上面的网格有数据以后this.data.grids1.forEach((grid)=>{if (!grid.isBlock) return//有方块的话,就调用方块模块的绘制方法,绘制出来方块BLOCK.drawBlockAtGrid(ctx,bgGridsData.gridSize,grid.x,grid.y)})}},methods:{//类似组件的方法...都在这里添加实现,在内部调用}
})
网格背景图像是静态的,可以当作缓存来绘制,
未变化的图形是不建议重新初始化处理的,这会消耗CPU计算资源
游戏方块
初始化游戏方块的是在blocksData对象里,
如下代码,看看有哪些数据
const blocksData = {blocks:[],// 所有方块列表数据// 选择方块的select:{index:-1, // 执行方块列表数据的索引// 点击开始点startPoint:{x:-1,y:-1},// 拖动位置点movePoint:{x:-1,y:-1},// 位置变化差offsetX:0,offsetY:0}
}
能看懂上面的一些定义吗,看到后面就会知道有什么用了吧
将方块对象添加到game对象的属性data中,
如下代码,在方法redraw()实现绘制所有方块
const that = this
game.addBgObject({data:blocksData,//重置方法reset(){const { blocks } = this.datablocks.length = 0//调用下面的方法,分别设置三个随机方块在下面的网格中,传入的是索引that.setRandomBlock()that.setRandomBlock(1)that.setRandomBlock(2)},redraw(data){const {canvas,context:ctx} = dataconst {blocks,select}=this.data// 遍历所有方块blocks.forEach((block,index)=>{let offsetX=0let offsetY=0//如果选择了下面网格的一个方块if (index==select.index){//调用模块的方法,查找此方块的索引let gIndex = BLOCK.findIndexFormGrid(bgGridsData.cols,block,0)//计算方块的开始点和结束点let startPoint = ...let endX = ...let endY = ...//此处省略了...//再绘制选中的方块背景...ctx.strokeStyle = '#000000'ctx.fillStyle = '#777777'ctx.beginPath()ctx.rect(startPoint.x,startPoint.y,endX,endY)ctx.fill()ctx.stroke()//调用模块的更新选择方法,就是改变select.offsetX和select.offsetYBLOCK.updateSelectBlockLocation(this.data.select)//拖动改变了位置offsetX = this.data.select.offsetXoffsetY = this.data.select.offsetY//获取下面方块在网格的数据let grid = bgGridsData.grids2[block.index]if (grid) {//调用模块的方法,获取拖动的方块在上面投影出来网格的方块let mapBlock = BLOCK.findMappingBlock(bgGridsData.cols,bgGridsData.gridSize,bgGridsData.grids1,block,grid.x+offsetX,grid.y+offsetY)//如果有的话,就绘制出来if (mapBlock) {ctx.strokeStyle = '#000000'ctx.fillStyle = '#999999'//调用此方法绘制方块mapBlock,方法是在下面的methods定义的this.drawBlock(ctx,bgGridsData.grids1,mapBlock)}//最后将投影出来的方块存到起this.data.mapBlock = mapBlock}}ctx.strokeStyle = '#000000'ctx.fillStyle = '#000000'//调用此方法绘制方块block,方法是在下面的methods定义的this.drawBlock(ctx,bgGridsData.grids2,block,offsetX,offsetY)})},methods:{drawBlock(ctx,grids,block,offsetX=0,offsetY=0){//方块有个属性list,这里记录方块的每一块相对坐标位置block.list.forEach((g,i)=>{//此处省略了...绘制方块的每一块})}}
})
开始游戏
在开始的游戏方法里,调用game的方法run()即可,
在调用游戏开始前,需要调用reset()就可以重置游戏数据,代码如下
restart() {const {game}=thisconst {stateData}=this.gameData//关闭定时器this.closeTimer()//重置游戏game.reset()//运行游戏game.run()//定时更新this.timer = setInterval(()=>{stateData.timerNum--if (stateData.timerNum==0){stateData.isGameEnd = truethis.closeTimer()app.setMaxScope(stateData.scope)wx.showModal({title: '系统提示',content: '游戏结束!当前得分'+stateData.scope,showCancel: true,cancelText: '返回游戏',confirmText: '重新开始',complete: (res) => {if (res.confirm) {this.restart()}}})return}},1100)
},
写到这里,游戏基本就可以运行了,界面如下图

还没完呢,需要实现游戏交互,在触摸事件里实现
选择方块
开始触摸时,会调用方法onTouchStart(e),
代码如下,这里实现选择底部的方块
onTouchStart(e) {const touch = e.touches[0]const { bgGridsData, blocksData, stateData } = this.gameData//先判断游戏是否结束if (stateData.isGameEnd) return//获取触摸到方块的索引let blockIndex = blocksData.blocks.findIndex(block=>{return block.list.find((g,i)=>{//...调用模块的方法,查找触摸到的方块索引let index = BLOCK.findIndexFormGrid(bgGridsData.cols,block,i)let grid = bgGridsData.grids2[index + block.index]//判断指定网格位置return grid.x<touch.x && grid.x+bgGridsData.gridSize>=touch.x && grid.y<touch.y && grid.y+bgGridsData.gridSize >= touch.y})})//如果没有触摸到,重置选择返回if (blockIndex<0) {blocksData.select.index = -1return}//执行到这里,就是触摸到了,设置以下选择数据blocksData.select.index = blockIndexObject.assign(blocksData.select.startPoint,{x: touch.x,y: touch.y})},
看上面的代码,游戏交互的实现,只需要自己实现修改游戏数据就可以了,
具体的绘制逻辑,之前就有讲过,当数据变化,绘制的状态也会跟着变化
拖动方块
触摸移动时,也就是拖动操作,会调用方法onTouchMove(e),
代码如下,这里实现拖动方块
onTouchMove(e) {const touch = e.touches[0]const { bgGridsData, blocksData } = this.gameDataif (blocksData.select.index<0) return//选择到方块,然后移动,就是拖动方块的操作了,边移动边更新它的位置Object.assign(blocksData.select.movePoint,{x: touch.x,y: touch.y})
},
消灭方块
不再触摸时,也就是拖动取消了,会调用方法onTouchEnd(e),
代码如下,这里实现放置方块
onTouchEnd(e) {const { bgGridsData, blocksData, stateData } = this.gameDataif (blocksData.select.index<0) returnconst { select, mapBlock } = blocksData//如果有投影出来的方块if (mapBlock) {//这里就处理,把投影的方块设置到上面的网格中,就真的放置上去了mapBlock.list.forEach((value,index)=>{if (value<1) returnlet gIndex = BLOCK.findIndexFormGrid(bgGridsData.cols,mapBlock,index)let grid = bgGridsData.grids1[gIndex + mapBlock.index]grid.isBlock = true})//放置好了,接下来,再出一个随机方块this.setRandomBlock(select.index)//调用模块的方法,算出消灭方块的数量,同时会修改网格的方块数据(清空操作)let count = BLOCK.decreaseBlocksFormGrids(bgGridsData.grids1,bgGridsData.cols)if (count>0) {//更新游戏状态,加分,加时间stateData.scope += countstateData.timerNum += 60//弹出提示wx.showToast({title: `消灭方块,加60秒`,icon: 'none'})}}//以下是重置选择数据select.index = -1Object.assign(select.startPoint, {x: -1,y: -1})Object.assign(select.movePoint, {x: -1,y: -1})
},
还有对象game有个方法game.initTopBar(),是初始化顶部显示的游戏状态,看看游戏效果里有,
其它方法,以及模块的一些方法,这里不多讲了,不是重点,
建议看源代码吧,代码不多,值得研究学习
游戏测试
写到这里,方块消消乐游戏就算完成了,可以运行编译测试,
运行效果图如下,点鼠标拖动底部的方块,任选一个,拖放到上面的框中,

- 当某行或某列没有留空的话,就会被消灭了,会奖励时间;
- 底部出现的方块是随机的,玩家根据自己的想法去拖放方块,
- 看谁消灭的多,得分就越高哦,同时会记录下来;
想看项目源码的前往下载请点这里查找,若不方便查找请在右侧输入关键词方块消消乐搜索即可,本博客站内请放心下载,感谢支持!
可能手机上看不到下载点,请改用电脑浏览器查看

相关文章:
【方块消消乐】方块消除游戏-微信小程序开发流程详解
有做过俄罗斯方块游戏小程序的经验,这次有做了一个消灭方块的游戏,实现过程很顺利,游戏看着和之前做的俄罗斯方块游戏很像,这里调整了玩法,试玩感觉还可以,接下来给大家讲一讲消灭方块游戏开发过程。 俄罗斯…...
mybatis配置文件中配置类型别名的方式
在MyBatis配置文件(通常是mybatis-config.xml)中,可以通过以下方式配置类型别名: 1. 使用typeAliases元素配置全局类型别名 <configuration> <typeAliases> <typeAlias alias"YourAlias" type"…...
leetcode目标和
给你一个非负整数数组 nums 和一个整数 target 。 向数组中的每个整数前添加 ‘’ 或 ‘-’ ,然后串联起所有整数,可以构造一个 表达式 : 例如,nums [2, 1] ,可以在 2 之前添加 ‘’ ,在 1 之前添加 ‘-…...
2023人形机器人行业海外科技研究:从谷歌看机器人大模型进展
今天分享的是人形机器人系列深度研究报告:《2023人形机器人行业海外科技研究:从谷歌看机器人大模型进展》。 (报告出品方:华鑫证券) 报告共计:26页 大模型是人形机器人的必备要素 长期来看,人…...
【深度学习笔记】01 数据操作与预处理
01 数据操作与预处理 一、数据操作1.1 基本数据操作1.2 广播机制1.3 索引和切片1.4 节省内存1.5 转换为其他Python对象 二、数据预处理读取数据集处理缺失值转换为张量格式练习 一、数据操作 1.1 基本数据操作 导入torch import torch张量表示一个由数值组成的数组ÿ…...
Python与设计模式--门面模式
8-Python与设计模式–门面模式 一、火警报警器(1) 假设有一组火警报警系统,由三个子元件构成:一个警报器,一个喷水器, 一个自动拨打电话的装置。其抽象如下: class AlarmSensor:def run(self):…...
改进YOLOv8 | YOLOv5系列:RFAConv续作,即插即用具有任意采样形状和任意数目参数的卷积核AKCOnv
RFAConv续作,构建具有任意采样形状的卷积AKConv 一、论文yolov5加入的方式论文 源代码 一、论文 基于卷积运算的神经网络在深度学习领域取得了显著的成果,但标准卷积运算存在两个固有缺陷:一方面,卷积运算被限制在一个局部窗口,不能从其他位置捕获信息,并且其采样形状是…...
机器学习-激活函数的直观理解
机器学习-激活函数的直观理解 在机器学习中,激活函数(Activation Function)是用于引入非线性特性的一种函数,它在神经网络的每个神经元上被应用。 如果不使用任何的激活函数,那么神经元的响应就是wxb,相当…...
Fedora 36 ARM 镜像源更换与软件安装
1、什么是Fedora Fedora Linux是较具知名度的Linux发行套件之一,由Fedora专案社群开发、红帽公司赞助,目标是建立一套新颖、多功能并且自由的作业系统。 Fedora是商业化的Red Hat Enterprise Linux发行版的上游原始码。 2、Fedora软件安装 64 位 .deb&a…...
多级缓存快速上手
哈喽~大家好,这篇来看看多级缓存。 🥇个人主页:个人主页 🥈 系列专栏:【微服务】 🥉与这篇相关的文章: JAVA进程和线程JAVA进程和线程-CSDN博客Http…...
初始React
<!DOCTYPE html> <html> <head> <meta charset"UTF-8" /> <title>React</title> </head> <body> 了解React <!-- React是一个用于构建web和原生态交互界面的库 相对于传统DOM开发优势:组件化开发…...
2.5 逆矩阵
一、逆矩阵的注释 假设 A A A 是一个方阵,其逆矩阵 A − 1 A^{-1} A−1 与它的大小相同, A − 1 A I A^{-1}AI A−1AI。 A A A 与 A − 1 A^{-1} A−1 会做相反的事情。它们的乘积是单位矩阵 —— 对向量无影响,所以 A − 1 A x x A^{…...
物流实时数仓:数仓搭建(ODS)
系列文章目录 物流实时数仓:采集通道搭建 物流实时数仓:数仓搭建 文章目录 系列文章目录前言一、IDEA环境准备1.pom.xml2.目录创建 二、代码编写1.log4j.properties2.CreateEnvUtil.java3.KafkaUtil.java4.OdsApp.java 三、代码测试总结 前言 现在我们…...
【ARM 嵌入式 编译 Makefile 系列 18 -- Makefile 中的 export 命令详细介绍】
文章目录 Makefile 中的 export 命令详细介绍Makefile 使用 export导出与未导出变量的区别示例:导出变量以供子 Makefile 使用 Makefile 中的 export 命令详细介绍 在 Makefile 中,export 命令用于将变量从 Makefile 导出到由 Makefile 启动的子进程的环…...
【opencv】计算机视觉:停车场车位实时识别
目录 目标 整体流程 背景 详细讲解 目标 我们想要在一个实时的停车场监控视频中,看看要有多少个车以及有多少个空缺车位。然后我们可以标记空的,然后来车之后,实时告诉应该停在那里最方便、最近!!!实现…...
播放器开发(三):FFmpeg与SDL环境配置
学习课题:逐步构建开发播放器【QT5 FFmpeg6 SDL2】 环境配置 我这边的是使用macOS;IDE用的是CLion;CMake构建,除了创建项目步骤、CMakeLists文件有区别之外的代码层面不会有太大区别。 配置上只添加一下CMakeLists中FFmpeg和SD…...
KubeVela核心控制器原理浅析
前言 在学习 KubeVela 的核心控制器之前,我们先简单了解一下 KubeVela 的相关知识。 KubeVela 本身是一个应用交付与管理控制平面,它架在 Kubernetes 集群、云平台等基础设施之上,通过开放应用模型来对组件、云服务、运维能力、交付工作流进…...
迎接“全全闪”时代 XSKY星辰天合发布星海架构和星飞产品
11 月 17 日消息,北京市星辰天合科技股份有限公司(简称:XSKY星辰天合)在北京首钢园举办了主题为“星星之火”的 XSKY 星海全闪架构暨星飞存储发布会。 (图注:XSKY星辰天合 CEO 胥昕) XSKY星辰天…...
[架构相关]基础架构设计原则
基础架构设计原则 文章目录 基础架构设计原则一、可用性(Availability)1.1、引入冗余1.2、负载均衡1.3、故障转移1.4、备份和恢复策略 二、可扩展性(Scalability)2.1 水平扩展2.2 垂直扩展2.3 弹性扩展 三、可靠性(Rel…...
测试在 Oracle 下直接 rm dbf 数据文件并重启数据库
创建一个新的表空间并创建新的用户,指定新表空间为新用户的默认表空间 create tablespace zzw datafile /oradata/cesdb/zzw01.dbf size 10m;zzw用户已经创建过,这里修改其默认表空间 alter user zzw quota unlimited on zzw; alter user zzw default …...
深度学习在微纳光子学中的应用
深度学习在微纳光子学中的主要应用方向 深度学习与微纳光子学的结合主要集中在以下几个方向: 逆向设计 通过神经网络快速预测微纳结构的光学响应,替代传统耗时的数值模拟方法。例如设计超表面、光子晶体等结构。 特征提取与优化 从复杂的光学数据中自…...
AI-调查研究-01-正念冥想有用吗?对健康的影响及科学指南
点一下关注吧!!!非常感谢!!持续更新!!! 🚀 AI篇持续更新中!(长期更新) 目前2025年06月05日更新到: AI炼丹日志-28 - Aud…...
51c自动驾驶~合集58
我自己的原文哦~ https://blog.51cto.com/whaosoft/13967107 #CCA-Attention 全局池化局部保留,CCA-Attention为LLM长文本建模带来突破性进展 琶洲实验室、华南理工大学联合推出关键上下文感知注意力机制(CCA-Attention),…...
2024年赣州旅游投资集团社会招聘笔试真
2024年赣州旅游投资集团社会招聘笔试真 题 ( 满 分 1 0 0 分 时 间 1 2 0 分 钟 ) 一、单选题(每题只有一个正确答案,答错、不答或多答均不得分) 1.纪要的特点不包括()。 A.概括重点 B.指导传达 C. 客观纪实 D.有言必录 【答案】: D 2.1864年,()预言了电磁波的存在,并指出…...
深入解析C++中的extern关键字:跨文件共享变量与函数的终极指南
🚀 C extern 关键字深度解析:跨文件编程的终极指南 📅 更新时间:2025年6月5日 🏷️ 标签:C | extern关键字 | 多文件编程 | 链接与声明 | 现代C 文章目录 前言🔥一、extern 是什么?&…...
Map相关知识
数据结构 二叉树 二叉树,顾名思义,每个节点最多有两个“叉”,也就是两个子节点,分别是左子 节点和右子节点。不过,二叉树并不要求每个节点都有两个子节点,有的节点只 有左子节点,有的节点只有…...
【生成模型】视频生成论文调研
工作清单 上游应用方向:控制、速度、时长、高动态、多主体驱动 类型工作基础模型WAN / WAN-VACE / HunyuanVideo控制条件轨迹控制ATI~镜头控制ReCamMaster~多主体驱动Phantom~音频驱动Let Them Talk: Audio-Driven Multi-Person Conversational Video Generation速…...
CVE-2020-17519源码分析与漏洞复现(Flink 任意文件读取)
漏洞概览 漏洞名称:Apache Flink REST API 任意文件读取漏洞CVE编号:CVE-2020-17519CVSS评分:7.5影响版本:Apache Flink 1.11.0、1.11.1、1.11.2修复版本:≥ 1.11.3 或 ≥ 1.12.0漏洞类型:路径遍历&#x…...
破解路内监管盲区:免布线低位视频桩重塑停车管理新标准
城市路内停车管理常因行道树遮挡、高位设备盲区等问题,导致车牌识别率低、逃费率高,传统模式在复杂路段束手无策。免布线低位视频桩凭借超低视角部署与智能算法,正成为破局关键。该设备安装于车位侧方0.5-0.7米高度,直接规避树枝遮…...
Spring Boot + MyBatis 集成支付宝支付流程
Spring Boot MyBatis 集成支付宝支付流程 核心流程 商户系统生成订单调用支付宝创建预支付订单用户跳转支付宝完成支付支付宝异步通知支付结果商户处理支付结果更新订单状态支付宝同步跳转回商户页面 代码实现示例(电脑网站支付) 1. 添加依赖 <!…...
