【方块消消乐】方块消除游戏-微信小程序开发流程详解
有做过俄罗斯方块游戏小程序的经验,这次有做了一个消灭方块的游戏,实现过程很顺利,游戏看着和之前做的俄罗斯方块游戏很像,这里调整了玩法,试玩感觉还可以,接下来给大家讲一讲消灭方块游戏开发过程。
俄罗斯方块游戏文章 【俄罗斯方块】单机游戏-微信小程序项目开发入门
文章目录
- 小程序
- 初始页面
- 游戏页面
- 游戏逻辑
- 游戏背景
- 游戏方块
- 开始游戏
- 选择方块
- 拖动方块
- 消灭方块
- 游戏测试
这里的消灭方块游戏,也叫方块消消乐游戏,
小程序
用微信开发工具打开,新建一个小程序项目,例如项目名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 …...
应用升级/灾备测试时使用guarantee 闪回点迅速回退
1.场景 应用要升级,当升级失败时,数据库回退到升级前. 要测试系统,测试完成后,数据库要回退到测试前。 相对于RMAN恢复需要很长时间, 数据库闪回只需要几分钟。 2.技术实现 数据库设置 2个db_recovery参数 创建guarantee闪回点,不需要开启数据库闪回。…...
逻辑回归:给不确定性划界的分类大师
想象你是一名医生。面对患者的检查报告(肿瘤大小、血液指标),你需要做出一个**决定性判断**:恶性还是良性?这种“非黑即白”的抉择,正是**逻辑回归(Logistic Regression)** 的战场&a…...
iPhone密码忘记了办?iPhoneUnlocker,iPhone解锁工具Aiseesoft iPhone Unlocker 高级注册版分享
平时用 iPhone 的时候,难免会碰到解锁的麻烦事。比如密码忘了、人脸识别 / 指纹识别突然不灵,或者买了二手 iPhone 却被原来的 iCloud 账号锁住,这时候就需要靠谱的解锁工具来帮忙了。Aiseesoft iPhone Unlocker 就是专门解决这些问题的软件&…...
智能在线客服平台:数字化时代企业连接用户的 AI 中枢
随着互联网技术的飞速发展,消费者期望能够随时随地与企业进行交流。在线客服平台作为连接企业与客户的重要桥梁,不仅优化了客户体验,还提升了企业的服务效率和市场竞争力。本文将探讨在线客服平台的重要性、技术进展、实际应用,并…...
【快手拥抱开源】通过快手团队开源的 KwaiCoder-AutoThink-preview 解锁大语言模型的潜力
引言: 在人工智能快速发展的浪潮中,快手Kwaipilot团队推出的 KwaiCoder-AutoThink-preview 具有里程碑意义——这是首个公开的AutoThink大语言模型(LLM)。该模型代表着该领域的重大突破,通过独特方式融合思考与非思考…...
【项目实战】通过多模态+LangGraph实现PPT生成助手
PPT自动生成系统 基于LangGraph的PPT自动生成系统,可以将Markdown文档自动转换为PPT演示文稿。 功能特点 Markdown解析:自动解析Markdown文档结构PPT模板分析:分析PPT模板的布局和风格智能布局决策:匹配内容与合适的PPT布局自动…...
微服务商城-商品微服务
数据表 CREATE TABLE product (id bigint(20) UNSIGNED NOT NULL AUTO_INCREMENT COMMENT 商品id,cateid smallint(6) UNSIGNED NOT NULL DEFAULT 0 COMMENT 类别Id,name varchar(100) NOT NULL DEFAULT COMMENT 商品名称,subtitle varchar(200) NOT NULL DEFAULT COMMENT 商…...
Ascend NPU上适配Step-Audio模型
1 概述 1.1 简述 Step-Audio 是业界首个集语音理解与生成控制一体化的产品级开源实时语音对话系统,支持多语言对话(如 中文,英文,日语),语音情感(如 开心,悲伤)&#x…...
【开发技术】.Net使用FFmpeg视频特定帧上绘制内容
目录 一、目的 二、解决方案 2.1 什么是FFmpeg 2.2 FFmpeg主要功能 2.3 使用Xabe.FFmpeg调用FFmpeg功能 2.4 使用 FFmpeg 的 drawbox 滤镜来绘制 ROI 三、总结 一、目的 当前市场上有很多目标检测智能识别的相关算法,当前调用一个医疗行业的AI识别算法后返回…...
Maven 概述、安装、配置、仓库、私服详解
目录 1、Maven 概述 1.1 Maven 的定义 1.2 Maven 解决的问题 1.3 Maven 的核心特性与优势 2、Maven 安装 2.1 下载 Maven 2.2 安装配置 Maven 2.3 测试安装 2.4 修改 Maven 本地仓库的默认路径 3、Maven 配置 3.1 配置本地仓库 3.2 配置 JDK 3.3 IDEA 配置本地 Ma…...
