• 【碰碰球】弹珠游戏-微信小程序项目开发流程详解


    还记得小时候玩过的弹珠撞击游戏不,这里把它的实现原理通俗易懂地讲一下,看看怎样实现一个碰碰球(弹珠)小游戏,除了个人玩法,也可以双人玩哦,与打乒乓球一样的,可练习临场反应。

    创建项目

    打开微信开发者工具,

    小程序项目

    选择创建小程序,项目名称自己填写,例如miniprogram-pairs-play

    选择项目属性

    如下图,依次选择即可
    tu1

    • AppID 选自己的测试号
    • 不使用云开发
    • JavaScript - JS 基础模板

    小游戏项目

    如果要选创建小游戏项目来做,也是可以的,实现步骤大同小异,

    可以将小程序的游戏源码改写到小游戏项目中,有兴趣可以看看笔者写得这篇文章来做

    【贪吃蛇】微信小程序的游戏转到小游戏上实现方法详解

    修改主页

    创建一个项目,会看到里面自动创建好了一些文件,

    找到位置pages/index/index,打开其中index.wxml

    这是第一个页面布局文件,在里面加一个button按钮组件布局,

    在对应的index.js逻辑文件中,实现一个点击方法,点击可进入游戏,

    横屏显示

    在对应的index.json配置文件中添加一个设置,如下,可实现横屏操作

    "pageOrientation": "landscape"
    
    • 1

    游戏页面

    进入的游戏页面,位置在pages/game/game

    这里是没有的,需要自己创建一个游戏页面,

    打开其中的game.wxml文件,添加内容如下,

    
    <canvas class="canvas" id="zs1028_csdn" type="2d" bindtouchstart="onTouchStart" bindtouchmove="onTouchMove" bindtouchend="onTouchEnd">canvas>
    
    • 1
    • 2

    游戏页面只需要一个canvas画布组件就可以了,非常简单

    游戏逻辑

    理清了游戏逻辑思路,就可以实现了,

    看看游戏页面,如下图,思考一下它是怎样实现的,是否简单呢,
    在这里插入图片描述

    初始化

    首先,写好初始化代码,并绑定canvas组件的触摸点击方法,

    // pages/game/game.js
    import ZS1028_CSDN from '../../utils/zs1028_CSDN.js'
    
    const app = getApp()
    
    Page({
        /**
         * 生命周期函数--监听页面初次渲染完成
         */
        async onReady() {
            const { width, height, node:canvas } = await ZS1028_CSDN.query('#zs1028_csdn')
            Object.assign(canvas, { width, height })
    		// 创建一个小游戏的引擎框架对象
            let game = ZS1028_CSDN.createMiniGameEngine({
                canvas,
                bgColor: 'black', //背景色
                // isTest:true //性能测试用途,需要时取消这里注释
            })
            this.game = game
            // 初始化一些游戏数据对象
            const leftPlayerData = {}
            const rightPlayerData = {}
            const aBallData = {}
            // 定义需要存储的游戏数据
            const gameData = {
                leftPlayerData, //左侧的球拍
                rightPlayerData, //右侧的球拍
                aBallData, //弹珠的
                timeNum: 0, //计时
                scopeCount: 0, //记录分
                isGameEnd: false //游戏状态
            }
            // 定义游戏平面布局需要用到一些数据
            const r = canvas.height * 0.18  // 球拍半径
            const dR = r*0.25 // 弹珠半径
            const dH = canvas.height/2 //垂直居中位置
            const powR = Math.pow(r, 2) // 勾股定理中斜边长的平方
            this.gameData = gameData
    		// 这里处理将上面定义的一些游戏数据对象添加到游戏引擎中
    		game.addBgObject({
                data: leftPlayerData,
                reset(){...},
                redraw(data){...}
            })
            game.addBgObject({
                data: rightPlayerData,
                reset(){...},
                redraw(data){...}
            })
            game.addBgObject({
                data: aBallData,
                reset(){...},
                redraw(data){...}
            })
            game.addForeObject({
                data:{...},
                reset(){...},
                redraw(data){...}
            })
            // 最后,开始游戏,这个方法就是调用了game.start()
            this.restart()
        },
    
    	onTouchStart(e) {
    		//...触摸开始时处理
    	},
    
    	onTouchMove(e) {
    		//...触摸移动时处理
    	},
    
    	onTouchEnd(e) {
    		//...触摸结束时处理
    	}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74

    其中ZS1028_CSDN 是一个模块,是作者编写的游戏引擎(框架)的一些方法,总体代码不到200行,可以研究学习

    写好了初始化逻辑,就可以运行看了,界面显示效果如预期的一致

    游戏引擎

    这里说明一下模块ZS1028_CSDN 游戏引擎game对象的用法:

    • addBgObject() 是将参与的游戏对象添加背景中的方法;
    • addForeObject() 是将参与的游戏对象添加到前景中的方法,可以把绘制的背景对象覆盖;
    • add..() 还有其它方法都在模块中,这里没用到就不多讲了,请自己探索!
    • run() 是运行游戏的;
    • stop() 是停止游戏的,在游戏结束时就调用它

    球拍

    接下来,绘制左边和右边的球拍两个,然后让用户可以拖动它上下移动

    绘制球拍

    前面使用了游戏引擎,这样绘制变得简单一些,

    在添加游戏对象的方法addBgObject()中,

    在传入的redraw()方法这里实现绘制,代码如下

    game.addBgObject({
       data: leftPlayerData,
       reset(){
       		//初始化数据
    	   Object.assign(this.data,{
             r,
               x:0, 
               y:dH,
               relY:0,
               direY:0 
           })
       },
       redraw(data){
       const { context:ctx } = data
       		//获取球拍距离垂直中心点的长度
           let relY = that.calcRelY(this.data)
           //绘制球拍的背景色
           ctx.fillStyle = 'red'
           ctx.beginPath()
           //绘制一个半球形状
           ctx.arc(this.data.x,this.data.y+relY,this.data.r,Math.PI*1.5,Math.PI/2)
           ctx.fill()
       }
     })
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 方法reset()是重置数据的,里面写初始化它的数据即可,在开始或重置游戏时都会调用它;
    • 方法calcRelY()是计算球拍距离垂直中心点的长度的,实现很简单

    绘制右边的球拍同上面绘制球拍的方法一样,复制这一段代码,然后改一改,实现右边绘制

    拿起球拍

    球拍绘制出来后,下一步,需要实现玩家对它的控制,

    实现球拍的拾起操作,就从触摸开始的方法里开始写,

    如下代码,遍历判断触摸位置是否碰到球拍所在位置

    onTouchStart(e) {
        //此处省略了...
        
        for(let i=0; i<e.touches.length; i++){
        	//一个触摸点,有三个重要属性 x, y, identifier
            let touch = e.touches[i]
            //此处省略了...
        }
    },
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    看上面的代码,touches是一个数组,表示它是支持多点触摸操作的,
    利用这个特点,可以实现两个球拍同时移动操作,
    也就是说,可以用自己的双手操作,或者你一边我一边来控制球拍进行游戏

    移动球拍

    实现拖动球拍操作,就在触摸移动的方法里写,

    如下代码,判断触摸点的数据,修改球拍数据就行

    onTouchMove(e) {
        const { leftPlayerData, rightPlayerData } = this.gameData
    
        for(let i=0; i<e.touches.length; i++){
            let touch = e.touches[i]
            //此处省略了...
        }
    },
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    修改球拍的数据就是对象的relY属性,
    在游戏引擎绘制方法中,它绘制的球拍看起来是会移动的

    放下球拍

    在触摸结束的方法里,要处理放下球拍,

    如下代码,重置球拍数据,要考虑处理一个在触摸,另一个没在触摸时的场景

    onTouchEnd(e) {
      const { leftPlayerData, rightPlayerData } = this.gameData
    
        if (e.touches.length>0){
            for(let i=0; i<e.touches.length; i++){
                let touch = e.touches[i]
                //此处省略了...
            }
        }else{
        	//重置球拍的数据
            this.resetTouchRelY(leftPlayerData)
            this.resetTouchRelY(rightPlayerData)
        }
    },
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    弹珠(球)

    还有一个弹珠,绘制应是球的形状,

    一开始不要想那么复杂,先绘制看看

    绘制弹珠

    同样需要在添加的游戏对象中去实现绘制,

    如下代码,和之前的添加对象方式一样,

    game.addBgObject({
       data: aBallData,
       reset(){
           Object.assign(this.data,{
               x:canvas.width/2, //初始坐标位置
               y:dH,
               moveDirection: {
                   speed:8, //移动速度
                   angle:0.1 //方向角度
               },
           })
       },
       redraw(data){
           const { context:ctx } = data
           // 实现让弹珠动起来
           // 此处省略了...
           
    		//绘制弹珠
           ctx.fillStyle = 'green'
           ctx.beginPath()
           ctx.arc(this.data.x,this.data.y,dR,0,Math.PI*2)
           ctx.fill()
    
    		//保存改变,判断游戏是否结束
    		//此处省略了...
       }
    })
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27

    让弹珠动起来

    在上面绘制方法里,开始的地方,

    添加如下代码,即可让弹珠动起来,是不是很神奇

    let { speed, angle } = this.data.moveDirection
    
    // 球拍按照角度angle方向移动位置
    this.data.y += Math.sin(angle/180*Math.PI) * speed
    this.data.x += Math.cos(angle/180*Math.PI) * speed
    
    • 1
    • 2
    • 3
    • 4
    • 5

    这里用到了初中数学知识:勾股定理;
    上过初中的学生,可曾记得正弦sin(A),余弦cos(A)的公式呢;
    其中A是边长弧度,angle是相交于水平线夹角的角度;
    弧度和角度它们之间的关系是:A = angle / 180 * Math.PI

    碰撞检测

    现在弹珠可以动起来了,接下来,继续写代码,

    代码如下,实现碰撞检测的思路大致是这样,改变的速度和角度即可

    // 计算弹珠的四个顶点位置,用于实现碰撞检测
    let left = this.data.x-dR
    let right = this.data.x+dR
    let top = this.data.y-dR
    let bottom = this.data.y+dR
    // 开始判断,先看看看移动方向到了哪个位置
    // 判断是否碰撞上下边界
    // ...此处省略了
    // 再判是否断碰撞左边和右边的球拍
    // ...此处省略了
    // 再判是否断碰撞左边和右边界
    // ...此处省略了
    
    // 这里是绘制弹珠,上面已经讲了...此处省略
    
    //最后,保存改变的速度和角度
    Object.assign(this.data.moveDirection, { speed, angle })
    
    //到左边和右边边界就判断出界,到此游戏结束
    if (left<=0 || right>=canvas.width) {
        game.stop()
        gameData.isGameEnd = true
        // 记录最高分
        app.setHistoryScopeCount(gameData.scopeCount)
        // 弹窗提示游戏结束
        ZS1028_CSDN.showModal('游戏结束, 记录'+gameData.scopeCount,'重新开始','退出游戏').then(res=>{
            if (res.confirm) that.restart()
            else wx.navigateBack()
        })
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30

    角度方向改变

    实现碰撞检测的思路会复杂一些,需要研究搞清楚弹珠的运动轨迹,

    可要研究仔细了,不然这代码会看不懂的,

    弹珠的移动到碰撞后怎样改变方向,大致讲一下,代码如下

    // 判断上下边界的
    if (top<=0 || bottom>=canvas.height) {
        angle *= -1 //改变负号,弹珠在靠近水平线时会弹跳起来,是不是很神奇
    } else if (r >= left) {
    	//判断到左边,接近球拍半径就会执行到这里
    	//...此处省略了
    } else if (canvas.width > right && canvas.width-r <= right) {
    	//判断到右边,接近球拍半径就会执行到这里
    	//...此处省略了
    }
    //...此处省略了
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    显示记录

    显示记录的方法

    方法addForeObject()同上面的添加对象方式一样的,就绘制显示游戏的数据,

    最后添加的会在最上层显示的,很简单,不重要不讲吧,

    游戏运行

    写到这里,按照上面的实现思路去做,边写完一部边运行,在实践中学习,

    做好了,基本就可以运行游戏测试了,

    回到开始讲得游戏初始化地方,调用游戏开始方法是this.restart(),代码如下

    restart() {
     	const { game } = this
    	// 调用游戏引擎的run方法即可运行
        game.run()
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    要重新开始游戏,就调用restart()方法即可,

    用上游戏引擎框架,运行过程就不用自己考虑实现了,就是这么很简单!

    碰碰球的游戏运行效果的动图如下,
    请添加图片描述

    右边的球拍是可以拖动的,录制的电脑鼠标只能控制一个,
    在真机上可同时控制两个的,这样玩效果会效果更好

    完整代码可以看看碰碰球的小程序小游戏项目源码,已放在资源里了 请点前往查看(可能在手机上会找不到,改用电脑浏览器看看),感谢支持!❤
    请添加图片描述

  • 相关阅读:
    winform性能内存泄露检测工具
    Spring基础知识一
    WorkPlus | 好用、专业、安全的局域网即时通讯及协同办公平台
    基本放大电路的学习
    DSPE-PEG-TAT,磷脂-聚乙二醇-靶向穿膜肽,脂质体修饰细胞穿膜肽TAT
    Containerd高级命令行工具nerdctl安装及使用
    记一次php反序列化漏洞中的POPchain和POC构造实战
    计算机组成原理——计算机系统层次结构
    rancher或者其他容器平台使用非root用户启动jar
    【前端Vue3】——JQuery知识点总结(超详细)
  • 原文地址:https://blog.csdn.net/zs1028/article/details/134478924