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

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

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

文章目录

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

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

小程序

用微信开发工具打开,新建一个小程序项目,例如项目名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 })
        //创建小游戏引擎(框架)对象,传入canvas
        const 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 = img
            img.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.data
        blocks.length = 0
        //调用下面的方法,分别设置三个随机方块在下面的网格中,传入的是索引
        that.setRandomBlock()
        that.setRandomBlock(1)
        that.setRandomBlock(2)
    },
    redraw(data){
        const {canvas,context:ctx} = data
        const {blocks,select}=this.data
        // 遍历所有方块
        blocks.forEach((block,index)=>{
            let offsetX=0
            let 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.offsetY
                BLOCK.updateSelectBlockLocation(this.data.select)
                //拖动改变了位置
                offsetX = this.data.select.offsetX
                offsetY = 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}=this
    const {stateData}=this.gameData
	//关闭定时器
    this.closeTimer()
    //重置游戏
    game.reset()
    //运行游戏
    game.run()
    //定时更新
    this.timer = setInterval(()=>{
        stateData.timerNum--
        if (stateData.timerNum==0){
            stateData.isGameEnd = true
            this.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 = -1
         return
     }
     //执行到这里,就是触摸到了,设置以下选择数据
     blocksData.select.index = blockIndex
     Object.assign(blocksData.select.startPoint,{
         x: touch.x,
         y: touch.y
     })
 },

看上面的代码,游戏交互的实现,只需要自己实现修改游戏数据就可以了,
具体的绘制逻辑,之前就有讲过,当数据变化,绘制的状态也会跟着变化

拖动方块

触摸移动时,也就是拖动操作,会调用方法onTouchMove(e)

代码如下,这里实现拖动方块

onTouchMove(e) {
   const touch = e.touches[0]
    const { bgGridsData, blocksData } = this.gameData
    if (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.gameData
    if (blocksData.select.index<0) return
    const { select, mapBlock } = blocksData
    //如果有投影出来的方块
    if (mapBlock) {
    	//这里就处理,把投影的方块设置到上面的网格中,就真的放置上去了
        mapBlock.list.forEach((value,index)=>{
            if (value<1) return
            let 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 += count
            stateData.timerNum += 60
            //弹出提示
            wx.showToast({
              title: `消灭方块,加60秒`,
              icon: 'none'
            })
        }
    }
	//以下是重置选择数据
    select.index = -1
    Object.assign(select.startPoint, {
        x: -1,
        y: -1
    })
    Object.assign(select.movePoint, {
        x: -1,
        y: -1
    })
},

还有对象game有个方法game.initTopBar(),是初始化顶部显示的游戏状态,看看游戏效果里有,

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

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

游戏测试

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

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

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

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

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

请添加图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Unity2D-URP基于ShaderGraph创建带粒子特效的激光光束

文章目录 创建Shader新建Node: UV新建Node: Split......参数说明 基于Shader创建Material创建Line创建粒子系统StartVFX创建粒子材质更改粒子系统的材质设置透明模式设置粒子效果创建一个Beam设置EndVFX效果预览激光光束管理脚本最终预览 创建Shader Create --> Shader Gra…

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

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

掌握文件夹重命名技巧:字母大小写批量转换的实用操作

在这个数字化时代&#xff0c;经常要与各种文件和文件夹打交道。有时候&#xff0c;为了提高工作效率或整理文件&#xff0c;要对文件夹名称进行修改。其中&#xff0c;字母大小写的转换是一个常见的需求。例如&#xff0c;将所有文件夹名称中的大写字母转换为小写字母&#xf…

【Linux系统编程】冯 • 诺依曼体系结构(什么是冯 • 诺依曼体系结构?冯 • 诺依曼体系结构如何应用?)

目录 一、前言 二、什么是冯 • 诺依曼体系结构&#xff1f; &#x1f4a6; 冯 • 诺依曼体系结构的发展推导 &#x1f4a6;冯 • 诺依曼体系结构的5大部件 ⭐输入和输出设备 ⭐存储器 ⭐中央处理器&#xff08;CPU&#xff09; &#x1f4a6;冯 • 诺依曼体系结构的细节…

《数据结构、算法与应用C++语言描述》-二叉树与其他树-二叉树的C++实现-设置信号放大器与并查集问题

二叉树和其他树 可编译运行程序见&#xff1a;Github::Jasmine-up/Data-Structures-Algorithms-and-Applications/_23BinaryTree 定义 树 定义 11-1 一棵树 t是一个非空的有限元素的集合&#xff0c;其中一个元素为根&#xff08;root&#xff09;&#xff0c;其余的元素&a…

Python 测试框架 Pytest 的入门

简介 pytest 是一个功能强大而易于使用的 Python 测试框架。它提供了简单的语法和灵活的功能&#xff0c;用于编写和组织测试代码。 1、简单易用&#xff1a;pytest 的语法简洁明了&#xff0c;使得编写测试用例更加直观和易于理解。它使用 assert 语句来验证预期结果&#x…

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…

4G模块(EC600N)通过MQTT连接华为云

目录 一、前言 二、EC600N模块使用 1&#xff0e;透传模式 2&#xff0e;非透传模式 3、华为云的MQTT使用教程&#xff1a; 三、具体连接步骤 1、初始化检测 2、打开MQTT客户端网络 3、创建产品 4、创建模型 5、注册设备 6、连接客户端到MQTT服务器 7、发布主题消…

2023-11-26 事业-代号s-跨境物流-记录

摘要: 2023-11-26 事业-代号s-跨境物流-记录 跨境物流: 【结论】 中小卖家&#xff08;最低适合1个人经营的卖家&#xff09;首选以下两种物流&#xff0c;目前已知的是以下两种&#xff0c;后续有新的发现再更新。 1、云途物流&#xff08;YunExpress&#xff09;&#xff…

2016年五一杯数学建模A题购房中的数学问题解题全过程文档及程序(采光与房款)

2016年五一杯数学建模 A题 购房中的数学问题 原题再现 随着现代社会经济的快速发展&#xff0c;房地产成为国家经济发展中重要的经济增长点之一。为了充分利用楼房建设的土地面积&#xff0c;开发商经常会选择建筑高层住宅。在购买住房时&#xff0c;影响消费者选择购房的因素…

企业文档文件管理软件推荐:提升管理效率与数据安全性

Zoho WorkDrive企业网盘是一种高效的文件管理工具&#xff0c;它不仅可以为组织搭建统一、高效、安全、智能的内容管理体系&#xff0c;还能够提供大规模支撑、海量数据处理、非结构化数据治理、智能挖掘与洞察等服务能力。通过这些服务&#xff0c;企业可以更好地管理和利用其…

Linux 面试题(一)

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

更改MacBook壁纸,有时可以带来不一样的感觉,特别是动态壁纸

在我看来&#xff0c;买一台新的MacBook最棒的部分就是挑选一张完美的桌面壁纸&#xff0c;为我的新工作伙伴定下基调。有时&#xff0c;在真正更换壁纸之前&#xff0c;我会花一整天的时间&#xff0c;仔细决定我的新笔记本电脑到底是什么样子&#xff0c;而且由于Macbook如此…

使用项目管理工具进行新媒体运营管理的策略与方法

使用Zoho Projects项目管理工具&#xff0c;新媒体运营可轻松驾驭从策划选题、撰写到排期发布的全流程。运用项目管理工具对新媒体运营进行精细化管理&#xff0c;助力团队更高效地规划、执行和追踪各项任务与活动。 以下是运用项目管理工具管理新媒体运营的妙招&#xff1a; 1…

软件测试面试题之如何进行项目介绍

邯郸网上银行系统旨在为企业搭建安全便捷的账户管理&#xff0c;资金汇化及投资服务通道&#xff0c;提升企业财富与价值增值它主要包含首页、我的账户、信用卡、邮储业务、投资理财、转账汇款、个人贷款等模块。 个人贷款一般有抵押贷款&#xff0c;和信用贷等&#xff0c;房…

【算法萌新闯力扣】:回文链表

力扣题目&#xff1a;回文链表 开篇 今天是备战蓝桥杯的第23天。我加入的编程导航算法通关村也在今天开营啦&#xff01;那从现在起&#xff0c;我的算法题更新会按照算法村的给的路线更新&#xff0c;更加系统。大家也可以关注我新开的专栏“算法通关村”。里面会有更全面的知…

IDEA2023版本创建Sping项目只能勾选17和21,却无法使用Java8?(已解决)

文章目录 前言分析解决方案一&#xff1a;替换创建项目的源方案二&#xff1a;升级JDK版本 参考文献 前言 起因 想创建一个springboot的项目&#xff0c;本地安装的是1.8&#xff0c;但是在使用Spring Initializr创建项目时&#xff0c;发现版本只有17和21。 在JDK为1.8的情况下…

岁月随笔-穿拖鞋的汉子

时间如白驹过隙般&#xff0c;转眼间2023年也只剩下最后的40天。汉子我拿出年初自己定的目标&#xff0c;立下的Flag&#xff0c;恍恍惚若昨天发生&#xff0c;不禁让人感慨万千。 其实最近自己遇到了很大的困惑&#xff0c;也导致了断更了一个月。自己逐渐摸不清自己的定位啦…

由走“贸工技”的联想联想到传统OEM,带给了自己那些思考?

2022年1月16日&#xff0c;自己来到魔都的第1597天&#xff0c;这城市还是保持着相似的容颜&#xff0c;而自己却悄悄的起了变化。 以前对时间概念其实不是特别敏感&#xff0c;感觉自己有大把的时光可以浪费&#xff08;虽然知道死亡是个永远无法逃避的话题&#xff09;&#…