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

news2024/11/25 4:53:11

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

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

文章目录

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

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

小程序

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

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

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

游戏测试

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

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

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

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

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

请添加图片描述

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

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

相关文章

多元逻辑回归模型的概念、模型检验以及应用

多元逻辑回归是逻辑回归的一种扩展&#xff0c;用于处理多类别分类问题。在二元逻辑回归中&#xff0c;我们通过一个逻辑函数&#xff08;也称为S形函数&#xff09;将输入特征映射到一个概率值&#xff0c;用于预测两个类别中一个的概率。而在多元逻辑回归中&#xff0c;我们面…

How to show square root of absolute of x isn‘t Lipschitz function

https://math.stackexchange.com/questions/667346/sqrtx-isnt-lipschitz-function https://math.stackexchange.com/questions/1375829/how-to-show-square-root-of-absolute-of-x-sqrtx-is-not-lipschitz-continu?noredirect1

新手如何购买保险,保险投资基础入门

一、教程描述 本套保险教程&#xff0c;大小2.63G&#xff0c;共有11个文件。 二、教程目录 第01课 保险到底有什么用.mp4 第02课 已有社保还需要商业保险吗.mp4 第03课 你必须要懂的保险基础知识.mp4 第04课 关于重疾你必须要知道的几件事情.mp4 第05课 家庭重疾险如何…

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

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

4.一维数组——用数组处理求Fibonacci数列前20项

文章目录 前言一、题目描述 二、题目分析 三、解题 程序运行代码 四、结果显示 前言 本系列为一维数组编程题&#xff0c;点滴成长&#xff0c;一起逆袭。 一、题目描述 用数组处理求Fibonacci数列前20项 二、题目分析 前两项&#xff1a;f[20]{1,1} 后18项&#xff1a;for(…

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

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

岁月随笔-穿拖鞋的汉子

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

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

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

SpringBoot事务处理

一、事务回顾 回顾地址&#xff1a; 深入理解数据库事务&#xff08;超详细&#xff09;_数据库事务操作_Maiko Star的博客-CSDN博客 事务&#xff1a; 是一组操作的集合&#xff0c;是一个不可分割的工作单位&#xff0c;这些操作要么同时成功&#xff0c;要么同时失败 事…

什么是 dropblock

大家好啊&#xff0c;我是董董灿。 之前介绍过 dropout 算法&#xff0c;它在训练神经网络中&#xff0c;可以随机丢弃神经元&#xff0c;是一种防止网络过拟合的方法。 但是在卷积神经网络中&#xff0c;dropout 的表现却不是很好&#xff0c;于是研究人员又搞了一个“结构化…

五大自动化测试的 Python 框架

1、Selenium: Selenium 是一个广泛使用的自动化测试框架&#xff0c;用于测试Web应用程序。它支持多种浏览器&#xff0c;并通过模拟用户在浏览器中的操作来进行测试。Selenium 的 Python 客户端库是 Selenium WebDriver&#xff0c;它提供了一组API来编写测试脚本&#xff0c…

系列十八、Spring bean线程安全问题

一、概述 我们知道Spring中的bean&#xff0c;默认情况下是单例的&#xff0c;那么Spring中的bean是线程安全的吗&#xff1f;这个需要分情况考虑&#xff0c;bean中是否存在成员变量&#xff1f;bean中的成员变量是怎么处理的&#xff1f;...&#xff0c;针对bean的状态会有不…

【挑战业余一周拿证】一、亚马逊云科技简介 - 第 1 节 - 模块 1 简介

CSDN 官方中文视频&#xff08;免费&#xff09;&#xff1a;点击进入 一、亚马逊云科技简介 第 1 节 - 模块 1 简介 1、讲师&#xff1a;李锦鸿 部门&#xff1a;亚马逊云科技培训与认证部门 方向&#xff1a;从事数据中心及云计算相关产品与解决方案工作 课程&#xff…

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

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

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

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

设计模式—依赖倒置原则(DIP)

1.概念 依赖倒置原则&#xff08;Dependence Inversion Principle&#xff09;是程序要依赖于抽象接口&#xff0c;不要依赖于具体实现。简单的说就是要求对抽象进行编程&#xff0c;不要对实现进行编程&#xff0c;这样就降低了客户与实现模块间的耦合。 通俗的讲&#xff1…

elastic -job和springboot集成实现分布式调度5

一 案例介绍说明 1.1 案例介绍 基于 Spring boot 集成方式的而产出的工程代码&#xff0c;完成对作业分片的实现&#xff0c;文件数据备份采取更接近真实项目的数 据库存取方式。 1.分片设置 2.每个线程获取给自的类型 二 操作说明 2.1 数据表的初始化 DROP TABLE IF EXIS…

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

力扣题目&#xff1a;回文链表 开篇 今天是备战蓝桥杯的第23天。我加入的编程导航算法通关村也在今天开营啦&#xff01;那从现在起&#xff0c;我的算法题更新会按照算法村的给的路线更新&#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_…

python桌面开发PyQt6库和工具库QTDesigner安装和配置

一、安装PyQt6 二、安装pyqt6-tools 三、安装外部工具 四、创建QTDesigner 1.首先查找designer.exe的路径&#xff08;可以自己在窗口中选择&#xff0c;也可以使用Everything搜索&#xff09; 2.使用Everything搜索后会出现多个designer.exe,选中&#xff0c;OpenPath 3.选择…