【方块消消乐】方块消除游戏-微信小程序开发流程详解

有做过俄罗斯方块游戏小程序的经验,这次有做了一个消灭方块的游戏,实现过程很顺利,游戏看着和之前做的俄罗斯方块游戏很像,这里调整了玩法,试玩感觉还可以,接下来给大家讲一讲消灭方块游戏开发过程。

俄罗斯方块游戏文章 【俄罗斯方块】单机游戏-微信小程序项目开发入门

文章目录

  • 小程序
  • 初始页面
  • 游戏页面
  • 游戏逻辑
    • 游戏背景
    • 游戏方块
    • 开始游戏
    • 选择方块
    • 拖动方块
    • 消灭方块
  • 游戏测试

这里的消灭方块游戏,也叫方块消消乐游戏,

小程序

用微信开发工具打开,新建一个小程序项目,例如项目名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(),是初始化顶部显示的游戏状态,看看游戏效果里有,

其它方法,以及模块的一些方法,这里不多讲了,不是重点,

建议看源代码吧,代码不多,值得研究学习

游戏测试

写到这里,方块消消乐游戏就算完成了,可以运行编译测试,

运行效果图如下,点鼠标拖动底部的方块,任选一个,拖放到上面的框中,
在这里插入图片描述

  • 当某行或某列没有留空的话,就会被消灭了,会奖励时间;
  • 底部出现的方块是随机的,玩家根据自己的想法去拖放方块,
  • 看谁消灭的多,得分就越高哦,同时会记录下来;

想看项目源码的前往下载请点这里查找,若不方便查找请在右侧输入关键词方块消消乐搜索即可,本博客站内请放心下载,感谢支持!

可能手机上看不到下载点,请改用电脑浏览器查看

请添加图片描述

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

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

相关文章

(离散数学)命题逻辑推理一:直接推理

P说明这一行是前提&#xff0c;T说明这一行是结论 &#xff0c;I说明该结论是由推导而来&#xff0c;E说明该结论是由化简而来&#xff0c;括号里的数字是推导这一结论需要的条件序号。 这种写法只是将重言蕴含的论证的思路进行了梳理 &#xff0c;前件为真则后件为真、后件为假…

【深度学习】DAMO-YOLO,阿里,701类通用检测模型,目标检测

https://github.com/tinyvision/DAMO-YOLO/blob/master/README_cn.md DAMO-YOLO是由阿里巴巴达摩院智能计算实验室TinyML团队开发的一个兼顾速度与精度的目标检测框架,其效果超越了目前的一众YOLO系列方法&#xff0c;在实现SOTA的同时&#xff0c;保持了很高的推理速度。DAMO…

Linux 面试题(一)

目录 1、绝对路径用什么符号表示&#xff1f;当前目录、上层目录用什么表示&#xff1f;主目录用什么表示? 切换目录用什么命令&#xff1f; 2、怎么查看当前进程&#xff1f;怎么执行退出&#xff1f;怎么查看当前路径&#xff1f; 3、怎么清屏&#xff1f;怎么退出当前命…

rdf-file:分布式环境下的文件处理

一&#xff1a;数据量大了以后&#xff0c;单机解析或者生成文件的效率就很低&#xff0c;需要通过集群处理 机构过来的文件&#xff1a;我们先对文件进行分片&#xff0c;在利用集群集群处理分片文件。给机构文件&#xff1a;分库分表数据&#xff0c;每个分表生成一个分片文…

VMware安装部署kail镜像服务器【详细包含百度云盘镜像】

VMware安装部署kail镜像服务器【详细包含百度云盘镜像】 kail是一个很好玩的操作系统&#xff0c;不多说了哈 下载kail镜像 kail官网:https://www.kali.org/get-kali/#kali-platforms 百度云盘下载&#xff1a; 链接&#xff1a;https://pan.baidu.com/s/1PRjoP_1v8DEZ7-dA_…

基于51单片机的智能垃圾桶硬件设计

**单片机设计介绍&#xff0c; 基于51单片机的智能垃圾桶硬件设计 文章目录 一 概要二、功能设计设计思路 三、 软件设计原理图 五、 程序六、 文章目录 一 概要 基于51单片机的智能垃圾桶设计旨在通过传感器和控制电路实现智能化的垃圾桶功能。下面是一个简要的硬件设计介绍&…

51代码审计-PHP框架MVC类上传断点调试

知识点1&#xff0c;文件上传漏洞挖掘 搜索关键字$_FILES phpmvc架构 MVC模式&#xff08;Model-View-Controller&#xff09;是软件工程中的一种软件架构模式。 MVC把软件系统分为三个基本部分&#xff1a;模型&#xff08;Model&#xff09;、视图&#xff08;View&#…

Redis面试题:Redis的数据淘汰策略有哪些?

目录 面试官&#xff1a;Redis的数据淘汰策略有哪些 ? 面试官&#xff1a;数据库有1000万数据 ,Redis只能缓存20w数据, 如何保证Redis中的数据都是热点数据 ? 面试官&#xff1a;Redis的内存用完了会发生什么&#xff1f; 面试官&#xff1a;Redis的数据淘汰策略有哪些 ? …

C++ day36 贪心算法 无重叠区间 划分字母区间 合并区间

题目1&#xff1a;435 无重叠区间 题目链接&#xff1a;无重叠区间 对题目的理解 移除数组中的元素&#xff0c;使得区间互不重叠&#xff0c;保证移除的元素数量最少&#xff0c;数组中至少包含一个元素 贪心算法 局部最优&#xff0c;使得重叠区间的个数最大&#xff0c…

GCPS—20型工程钻机的设计自动摊铺机的设计机械设计

wx供重浩&#xff1a;创享日记 对话框发送&#xff1a;摊铺机 获取完整论文报告工程源文件 摊铺机是一种复合式多功能摊铺机&#xff0c;为适应我国深基础和连续墙以及水利、纺织的发展与需要&#xff0c;结合大口径摊铺机灌注桩和地下连续墙施工的特点&#xff0c;为解决在复…

【数据结构】树的概念以及二叉树

目录 1 树概念及结构 1.1 树的概念 1.3 树的存储 2 二叉树的概念及结构 2.1 概念 2.2 特殊的二叉树 2.3 二叉树的性质 2.4 二叉树的存储结构 1 树概念及结构 1.1 树的概念 树是一种非线性的数据结构&#xff0c;它是由n&#xff08;n>0&#xff09;个有限结点组…

Java进阶(第二期):package 包 抽象类和抽象方法 接口的实现 多态的实现 综合继承、接口、多态的使用。

2023年11月26日20:11:11 文章目录 Java进阶&#xff08;第二期&#xff09;一、package包的概念二、抽象类和抽象方法(abstract)2.1 使用2.1 抽象类注意事项 三、接口3.1 接口的定义格式3.2 接口成员特点3.3 类和接口的关系3.4 接口和抽象类的对比 四、多态4.1 多态的前提条件4…

Sentry介绍与使用 - Issues模块

这篇文章是我在公司做 Sentry 相关分享的演讲稿。 大家好&#xff0c;现在由我来讲解 Sentry 的 Issues &#xff08;问题&#xff09;模块。我会分为三个部分来讲&#xff0c;首先我会介绍 Sentry 一些重要的概念&#xff0c;然后讲一下 Issues 的基本使用方式&#xff0c;最后…

steam/csgo搬砖项目真能月入过万吗?到底真的假的

steam/csgo搬砖第三课之如何出售 steam搬砖核心原理是什么&#xff1f;为什么会有差价产生&#xff1f;buff不是更低价吗&#xff1f;很多小白会有这些疑问&#xff01; steam搬砖指的是通过买卖csgo游戏装备赚钱的。 玩过游戏的应该就很清楚&#xff0c;像绝地求生&#xff…

Linux安装jdk8【十分丝滑】

1.上传安装包到Linux&#x1f495;&#x1f495;&#x1f495; 2.使用命令解压缩&#x1f495;&#x1f495;&#x1f495; tar -zxvf 压缩文件名 3.重命名&#x1f495;&#x1f495;&#x1f495; mv 原文件名 新的文件名 4.配置环境变量&#x1f929;&#x1f929;&…

Linux中vi常用命令-批量替换

在日常服务器日志查看中常用到的命令有grep、tail等&#xff0c;有时想查看详细日志&#xff0c;用到vi命令&#xff0c;记录下来&#xff0c;方便查看。 操作文件&#xff1a;test.properites 一、查看与编辑 查看命令&#xff1a;vi 文件名 编辑命令&#xff1a;按键 i&…

CountDownLatch实战应用——批量数据多线程协调异步处理(主线程执行事务回滚)

&#x1f60a; 作者&#xff1a; 一恍过去 &#x1f496; 主页&#xff1a; https://blog.csdn.net/zhuocailing3390 &#x1f38a; 社区&#xff1a; Java技术栈交流 &#x1f389; 主题&#xff1a; CountDownLatch实战应用——批量数据多线程协调异步处理(主线程执行事务…

图像分割模型及架构选型介绍(MMSegmentation|sssegmentation等)

参考&#xff1a; https://zhuanlan.zhihu.com/p/618226513 0. 图像分割概述 图像分割通过给出图像中每个像素点的标签&#xff0c;将图像分割成若干带类别标签的区块&#xff0c;可以看作对每个像素进行分类。图像分割是图像处理的重要组成部分&#xff0c;也是难点之一。随…

OpenGL 自学总结

前言&#xff1a; 本人是工作后才接触到的OpenGL&#xff0c;大学找工作的时候其实比较着急&#xff0c;就想着尽快有个着落。工作后才发现自己的兴趣点。同时也能感觉到自己当前的工作有一点温水煮青蛙的意思&#xff0c;很担心自己往后能力跟不上年龄的增长。因此想在工作之余…

3DCAT为华东师大设计学院打造元宇宙数字虚拟学院

6月11日&#xff0c;华东师范大学设计学院在chi K11美术馆举办了一场别开生面的 2023 年本科毕业设计暨项目实践教学现场演示展。其中&#xff0c;元宇宙数字虚拟学院&#xff08;一期&#xff09;的现场发布会引起了现场震撼&#xff0c;吸引了众多观众的目光和参与。 该元宇宙…