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

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

创建项目

打开微信开发者工具,

小程序项目

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

选择项目属性

如下图,依次选择即可
tu1

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

小游戏项目

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

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

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

修改主页

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

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

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

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

横屏显示

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

"pageOrientation": "landscape"

游戏页面

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

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

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

<!--pages/game/game.wxml-->
<canvas class="canvas" id="zs1028_csdn" type="2d" bindtouchstart="onTouchStart" bindtouchmove="onTouchMove" bindtouchend="onTouchEnd"></canvas>

游戏页面只需要一个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) {
		//...触摸结束时处理
	}

其中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()
   }
 })
  • 方法reset()是重置数据的,里面写初始化它的数据即可,在开始或重置游戏时都会调用它;
  • 方法calcRelY()是计算球拍距离垂直中心点的长度的,实现很简单

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

拿起球拍

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

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

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

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

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

移动球拍

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

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

onTouchMove(e) {
    const { leftPlayerData, rightPlayerData } = this.gameData

    for(let i=0; i<e.touches.length; i++){
        let touch = e.touches[i]
        //此处省略了...
    }
},

修改球拍的数据就是对象的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)
    }
},

弹珠(球)

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

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

绘制弹珠

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

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

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()

		//保存改变,判断游戏是否结束
		//此处省略了...
   }
})

让弹珠动起来

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

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

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

这里用到了初中数学知识:勾股定理;
上过初中的学生,可曾记得正弦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()
    })
}

角度方向改变

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

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

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

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

显示记录

显示记录的方法

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

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

游戏运行

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

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

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

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

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

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

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

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

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

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

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

相关文章

自动驾驶学习笔记(九)——车辆控制

#Apollo开发者# 学习课程的传送门如下&#xff0c;当您也准备学习自动驾驶时&#xff0c;可以和我一同前往&#xff1a; 《自动驾驶新人之旅》免费课程—> 传送门 《Apollo Beta宣讲和线下沙龙》免费报名—>传送门 文章目录 前言 控制器设计 比例积分微分控制 线性…

[C/C++] 数据结构 LeetCode:用队列实现栈

题目描述: 请你仅使用两个队列实现一个后入先出&#xff08;LIFO&#xff09;的栈&#xff0c;并支持普通栈的全部四种操作&#xff08;push、top、pop 和 empty&#xff09;。 实现 MyStack 类&#xff1a; void push(int x) 将元素 x 压入栈顶。int pop() 移除并返回栈顶元…

Vulhub靶场-KIOPTRIX: LEVEL 1.1

目录 环境配置 端口扫描 漏送发现 漏送利用 提权&#xff08;内核漏洞提权&#xff09; 环境配置 环境配置的过程同主页该专栏第一个靶场&#xff0c;不在赘述。 端口扫描 首先通过arp-scan并根据靶机的mac地址确定靶机的IP地址 然后对靶机进行一个扫描 首先扫描到OpenS…

Python大数据之linux学习总结——day10_hive调优

hive调优 hive调优hive命令和参数配置1.hive数据压缩压缩对比开启压缩 2.hive数据存储[练习]行列存储原理存储压缩比拓展dfs -du -h 3. fetch抓取4. 本地模式5. join的优化操作6. 列裁剪7. 分区裁剪8. group by 操作9. count(distinct)10. 笛卡尔积11. 动态分区[练习]12. 如何调…

【C++】入门三

接下来我们说一下引用这个概念&#xff0c;那么什么是引用呢&#xff1f;简单来说引用就是取别名&#xff0c;比如有一个变量叫a&#xff0c;现在我给它取了一个别名叫b&#xff0c;那么此时a和b管理的都是一块空间 这个例子就可以很好的体现a和b管理的是同一块空间&#xff0…

2023.11.18 每日一题(AI自生成应用)【C++】【Python】【Java】【Go】 动态时间序列分析

目录 一、编程挑战&#xff1a;动态时间序列分析 实际应用&#xff1a; 实现提示&#xff1a; 二、实现 1. C 2. Python 3. JAVA 4. Go 一、编程挑战&#xff1a;动态时间序列分析 问题描述&#xff1a; 假设你是一名软件工程师&#xff0c;需要开发一个应用来分析和预…

Spark 平障录

Profile Profile 是最重要的第一环。 利用好 spark UI 和 yarn container log分析业务代码&#xff0c;对其计算代价进行预判建设基准&#xff0c;进行对比&#xff0c;比如application id 进行对比&#xff0c;精确到 job DAG 环节 充分利用 UI Stage 页面 页头 summary&…

java拼图游戏(待优化)

启动类 package com.yx.ui;public class App { //启动入口public static void main(String[] args) {//如果想要开启一个界面&#xff0c;就创建谁的对象 // new DengJFrame(); // new ZCJFrame();new GameJFrame();}}游戏类 package com.yx.ui;import java.awt.event.KeyEv…

【数据结构】前言

数据结构是在计算机中维护数据的方式。 数据结构是OI重要的一部分。 同的数据结构各有优劣&#xff0c;能够处理的问题各不相同&#xff0c;而根据具体问题选取合适的数据结构&#xff0c;可以大大提升程序的效率。 所以&#xff0c;学习各种各样的数据结构是很有必要的。 数据…

Infineon+EB构建MCAL驱动包Demo实现片内外设使用

本篇文章以实际MCAL示例程序的实现与使用&#xff0c;帮助读者理解MCAL层在BSW中具体担任的功能与角色。文章首先介绍了为了构建MCAL示例程序所需要的相关应用程序的安装&#xff1b;然后介绍了个软件相互集成配置的过程&#xff0c;达到可以编译生成可执行文件&#xff1b;最后…

055-第三代软件开发-控制台输出彩虹日志

第三代软件开发-控制台输出彩虹日志 文章目录 第三代软件开发-控制台输出彩虹日志项目介绍控制台输出彩虹日志实现原理真实代码 总结 关键字&#xff1a; Qt、 Qml、 关键字3、 关键字4、 关键字5 项目介绍 欢迎来到我们的 QML & C 项目&#xff01;这个项目结合了 QM…

054-第三代软件开发-信号槽

第三代软件开发-信号槽 文章目录 第三代软件开发-信号槽项目介绍信号槽实现原理与MFC消息映射机制区别Qt信号槽机制的优缺点 关键字&#xff1a; Qt、 Qml、 关键字3、 关键字4、 关键字5 项目介绍 欢迎来到我们的 QML & C 项目&#xff01;这个项目结合了 QML&#x…

网络层——IP协议

文章目录 一.IP协议二.基本概念三.IP协议格式四.分片与组装五.网段划分六.特殊的IP地址七.IP地址的数量限制八.私网IP地址和公网IP地址九.路由十.路由表生成算法 一.IP协议 IP协议全称为“网际互连协议&#xff08;Internet Protocol&#xff09;”&#xff0c;IP协议是TCP/IP…

视频合并:掌握视频嵌套合并技巧,剪辑高手的必备秘籍

在视频剪辑的过程中&#xff0c;掌握视频合并的技巧是每个剪辑高手必备的技能之一。通过合理的合并视频&#xff0c;可以增强视频的视觉效果&#xff0c;提高观看体验。 一、视频合并的准备工作 收集素材&#xff1a;在进行视频合并之前&#xff0c;首先需要收集足够的素材&a…

C语言 深入理解指针

目录 前言 指针的重要概念 剖析 题目一 题目二 题目三 题目四 题目五 题目六 题目七 题目八 **cpp *--*cpp 3 *cpp[-2] 3 cpp[-1][-1] 1 前言 简单来说&#xff0c;指针是一个变量&#xff0c;其值为另一个变量的地址。通过指针&#xff0c;我们可以直…

交易机器人-规则部分

微信公众号&#xff1a;大数据高性能计算 背景 背景是基于人工去做交易本身无法做到24小时无时无刻的交易&#xff0c;主要是虚拟币本身它是24小时交易&#xff0c;人无法做到24小时盯盘&#xff0c;其次就是如果你希望通过配置更加复杂的规则甚至需要爬取最新的信息走模型进行…

二阶低通滤波器(二阶巴特沃斯滤波器)

连续传递函数G(s) 离散传递函数G(z) 差分方程形式 二阶巴特沃斯滤波器参数设计 设计采样频率100Hz&#xff0c;截止频率33Hz。 注意&#xff1a;设计参数使用在离散系统中&#xff01; 同理&#xff0c;其他不同阶数不同类型的滤波器设计&#xff0c;如二阶高通滤波器、二阶…

OFDM通信系统仿真之交织技术

文章目录 前言一、交织1、概念2、图形举例3、交织的位置 二、MATLAB仿真1、MATLAB 程序2、仿真结果 前言 之前的博客&#xff1a;OFDM深入学习及MATLAB仿真 中有对交织的概念进行讲解&#xff0c;但讲解还是比较浅显&#xff0c;且仿真实现时并没有加入交织及解交织流程&#…

系列十二、强引用、软引用、弱引用、虚引用分别是什么?

一、整体架构 二、强引用&#xff08;默认支持模式&#xff09; 2.1、概述 当内存不足时&#xff0c;JVM开始垃圾回收&#xff0c;对于强引用的对象&#xff0c;就算是出现了OOM也不会对该对象进行回收&#xff0c;死都不收。 强引用是我们最常见的普通对象引用&#xff0c;只…

特效!视频里的特效在哪制作——Adobe After Effects

今天&#xff0c;我们来谈谈一款在Adobe系列中推出的一款图形视频处理软件&#xff0c;适用于从事设计和视频特技的机构&#xff0c;包括电视台、动画制作公司、个人后期制作工作室以及多媒体工作室的属于层类型后期软件——Adobe After Effects。 Adobe After Effects&#xf…
最新文章