当前位置: 首页 > news >正文

【见缝插针】射击类游戏-微信小程序项目开发流程详解

还记得小时候玩过的见缝插针游戏吗,比一比看谁插得针比较多,可有趣了,当然了,通过它可以训练自己的手速反应,以及射击水平,把握时机,得分越高就越有成就感,相信小朋友们会喜欢它的,游戏实现原理看似简单,实则包含了数学中几何知识,接下来讲一讲实现过程吧。

这篇文章主要是用微信小程序项目做的,需要用微信开发者工具打开,

打开微信开发者工具,选择创建小程序,项目名称自己填写,例如miniprogram-shoot-scope

如下图,依次选择即可
tu1

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

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

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

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

游戏页面

接下来,创建一个游戏页面,文件路径是/pages/game/game,

打开布局文件/pages/game/game.wxml

在里面放一个画布组件(Canvas元素)就可以了,内容如下

<!--pages/game/game.wxml-->
<view class="page"><canvas class="canvas" type="2d" id="zs1028_csdn" bindtouchend="onTouchEnd"/>
</view>

游戏逻辑

打开逻辑文件/pages/game/game.js

在里面写游戏逻辑代码,先把画布组件的触摸事件和初始化方法都写好,

游戏引擎

写好的代码如下,从onReady()开始执行,绑定onTouchEnd()触摸事件

//导入一个模块,这是个小游戏引擎,或者框架
import ZS1028_CSDN from '../../utils/zs1028_CSDN.js'const app = getApp()Pages({/*** 生命周期函数--监听页面初次渲染完成*/onReady() {//选择查询画布组件实例wx.createSelectorQuery().select('#zs1028_csdn').fields({ size: true, node: true }, res => {//查询结果从这里返回const { width, height, node: canvas } = res//将画布大小调整一下,保证宽高一致Object.assign(canvas, { width, height })// 创建一个游戏引擎,用这实现游戏会方便一些const engine = ZS1028_CSDN.createMiniGameEngine({canvas,// isTest: true, //测试的,去掉注释可以显示游戏动画帧率,数字越大越流畅})//这里处理初始化游戏数据...稍后讲const newShotData = {...}const shotsData = {...}const centerScopeData = {...}//将上面的定义游戏都缓存到this.gameData = {centerScopeData,shotsData,newShotData}//把游戏引擎缓存到this.engine = engine//再把初始化游戏数据添加到游戏引擎对象中...稍后讲engine.addBgObject({data: newShotData,...})engine.addBgObject({data: shotsData,...})engine.addForeObject({data: centerScopeData,...})//最后,调用此方法开始游戏this.restart()}).exec()},/*** 绑定画布的触摸结束事件*/onTouchEnd() {if (this.isGameEnd) return//将底部的针箭的速度属性从0改为10,就可以发射const { newShotData } = this.gameDataif (newShotData.speed > 0) returnnewShotData.speed = 10},
})

模块文件zs1028_CSDN.js由开发者实现,代码看起来可能复杂些,这里讲简单的实现思路,

接下来讲,怎么用这个模块来轻松实现游戏,

有JavaScript编程基础功底的学者可以研究这模块来学习,
文件代码不多,有150行

初始化数据

开始游戏前,需要向游戏引擎传入游戏的一些初始化数据,

在上面省略的初始化游戏数据代码位置开始着写,写好代码如下

//记录一个水平的中心点位置
const centerX = width / 2
//定义一个靶的中心数据,就是绘制大红点的
const centerScopeData = {x: centerX,y: centerX,r: centerX * 0.5,initTime: 0,
}
//定义一个针箭的数据
const shotsData = {x: centerX,y: centerX,r: centerX * 0.1,//针的尾部圆半径shots: [],//放置已钉上的列表speed: 1,//旋转速度angle: 0,//旋转角度offset: 0
}
//定义一个在底部的针箭数据
const newShotData = {y: -1,//上下的位置,在底部speed: 0,//射出的速度,为0就是待发的状态
}

将初始化好的一些游戏数据对象都放到this.gameData,后面有需要就取,
对照游戏界面看看,就知道只用这三个数据就可以了,

加入游戏对象

继续写下去,将初始化数据添加到游戏引擎对象中,

钉上靶中的针箭

这里添加所有钉上针箭的对象,代码如下

//创建一个新的针箭对象
let shot = this.addNewShot()
//算出它的开始位置
const startY = canvas.height - shotsData.r - shot.data.linerLength - centerScopeData.r - centerScopeData.y
//使用游戏引擎的添加背景对象方法,绘制中心的所有针箭
engine.addBgObject({data: shotsData, //传入对象数据//实现重置数据方法reset() {const { shots } = this.datashots.length = 0 //重置时候,将所有钉上的针箭都清除},//实现绘制方法redraw(data) {const { canvas, context: ctx, topBar } = datalet { x, y, r, shots, speed, angle } = this.data// 内边距上边距离let paddingTop = topBar?.bottom || 0
// 绘制所有钉上的针箭shots.forEach((item, index) => {//夹角角度let deg = item.data.angle + angle//圆边弧度let radian = deg / 2 / Math.PI// 线段(针)长let c = item.data.linerLength + centerScopeData.r//使用数学中的勾股定理公式,求得线段的两点坐标let y2 = Math.sin(radian) * c + y + paddingToplet x2 = Math.cos(radian) * c + x//调用对象的绘制方法,传入的是针箭的两端坐标点item.redraw(x, y + paddingTop, x2, y2)})//旋转位置更新let offset = speed / 10this.data.offset = offsetthis.data.angle = (angle + offset) % 360}
})

在初中数学课上才有讲勾股定理哦,可见学好数学对做游戏来说是多么重要

底下待发的针箭

这里添加在底部针箭的对象,代码如下

const that = this
//调用引擎的添加背景对象方法,绘制底部的针箭
engine.addBgObject({data: newShotData,reset() {this.data.y = startYthis.data.speed = 0 //重置时候,速度为0表示不动shot?.reset()},redraw(data) {const { canvas, topBar } = data//其中y就是移动位置,通过改变这个值可实现上下移动射击let { y, speed } = this.datalet x1 = canvas.width / 2let y1 = y + centerScopeData.y + centerScopeData.rlet y2 = y1 + shot.data.linerLength//调用对象的绘制方法shot.redraw(x1, y1, x1, y2)if (speed > 0) {//当y的值还是大于0时,说明它还没有钉到靶中心,继续移动if (y >= (topBar?.bottom || 0)) {this.data.y -= speedreturn}//执行到这里,说明它刚刚钉上靶中心,就设置它在靶中心的角度以及偏移位置shot.data.angle = 90 - shotsData.angle - shotsData.offset * 10//这里用遍历,逐个判断这个针箭尾部的圆是否与已钉上的针箭相碰for (let i = 0; i < shotsData.shots.length; i++) {let item = shotsData.shots[i]if (item.x <= 0 || item.y <= 0) continue//计算两一个圆心之间的距离,这里用到数学的勾股定理公式:直角三角形三边关系let c = Math.sqrt(Math.pow(x1 - item.data.x, 2) + Math.pow(y2 - item.data.y, 2))//如果两个圆相交(互相碰到),那么就游戏结束if (c <= item.data.r + shot.data.r) {//调用游戏引擎的停止方法engine.stop()//记录最高分app.setMaxScope(shotsData.shots.length)//弹出对话框提示游戏结束that.showModalForGameEnd()return}}//将新的针箭添加到shotsData.shots.push(shot)//重置一下this.data.y = startYthis.data.speed = 0//重新添加shot = that.addNewShot()}}
})

💡直角三角形三边关系:假设直角的两边长是a和b,那么斜边长 c²=a²+b²

靶心中心

这里添加靶心的对象,代码如下

engine.addForeObject({data: centerScopeData,reset() {Object.assign(this.data,{initTime: Date.now()})},redraw(data) {const { canvas, context: ctx, topBar } = datalet { x, y, r, initTime } = this.datalet paddingTop = topBar?.bottom || 0//绘制靶心ctx.strokeStyle = 'black'ctx.fillStyle = 'red'ctx.lineWidth = 1ctx.beginPath()ctx.arc(x, y + paddingTop, r, 0, Math.PI * 2)ctx.fill()ctx.stroke()//绘制得分ctx.fillStyle = 'white'ctx.textAlign = 'center'ctx.baseTextAlign = 'middle'ctx.font = ctx.font.replace(/^\d+/, 38)ctx.fillText(`得分${shotsData.shots.length}`, x, y + paddingTop)//绘制计时let timer = Math.trunc((Date.now()-initTime)/1000)ctx.font = ctx.font.replace(/^\d+/, 32)ctx.fillText(`${timer}`, x, y + paddingTop + 48)}
})

就连这个方法addNewShot()实现也很简单,代码如下

/**
*	添加新的针箭对象
*/
addNewShot(){const { gameData, engine } = this//调用引擎创建的一个对象方法    let shot = engine.createObject({//...return shot
}

在这里会发现,引擎添加的游戏对象,都有data,reset(),redraw()的属性和方法,都看懂了吧,
实现过程都写在游戏引擎模块文件里,怎样实现的呢,对此感兴趣的可以看看项目源码研究看看

开始游戏

调用方法restart(),整个游戏就能运行起来了,如下代码
代码如下

/**
*	重新开始游戏
*/
restart(){const { engine } = this//重置游戏对象engine.reset()//运行游戏engine.run()//重置游戏结束状态this.isGameEnd = false
}

游戏引擎会处理其它实现的细节,基本的绘制流程无需我们操心,这样用实现起来会方便一些,

游戏项目

写完整个项目,就可以运行测试了,

运行效果动图如下,点击屏幕任意位置就可以发射
请添加图片描述
想看项目源码 请点击这里,在资源一栏下有个名称为 见缝插针游戏源码,

请放心下载,感谢支持❤

如果是在手机上浏览此文章的,可能看不到资源一栏下的项目源码,在电脑上浏览器看就能看到了

tu2

相关文章:

【见缝插针】射击类游戏-微信小程序项目开发流程详解

还记得小时候玩过的见缝插针游戏吗&#xff0c;比一比看谁插得针比较多&#xff0c;可有趣了&#xff0c;当然了&#xff0c;通过它可以训练自己的手速反应&#xff0c;以及射击水平&#xff0c;把握时机&#xff0c;得分越高就越有成就感&#xff0c;相信小朋友们会喜欢它的&a…...

flutter开发实战-TweenSequence实现动画序列

flutter开发实战-TweenSequence实现动画序列 一、TweenSequence TweenSequence是允许创建一个Animation由一系列补间动画来确定值&#xff0c;每个TweenSequenceItem都有定义在动画的持续时间的权重确定动画间隔。 TweenSequence 动画组类TweenSequenceItem 用来定义每一个动…...

Flowable 外部表单

内置表单需要在每个节点中去配置&#xff0c;当如果多个节点使用同一套表单属性就要配置多次比较麻烦&#xff0c;修改的时候也要修改多次&#xff0c;外部表单可以定义一次&#xff0c;然后其它节点都去引用同一个表单属性。 外部表单需要定义一个.form后缀的文件。 外部表单…...

[mysql]索引优化-2

目录 一、分页查询优化1.根据自增且连续的主键排序的分页查询2.根据非主键字段排序的分页查询 二、Join关联查询优化1.嵌套循环连接 Nested-Loop Join(NLJ) 算法2.基于块的嵌套循环连接 Block Nested-Loop Join(BNL)算法 三、count(*)查询优化1.查询mysql自己维护的总行数2.sho…...

数据分析实战 | 泊松回归——航班数据分析

目录 一、数据及分析对象 二、目的及分析任务 三、方法及工具 四、数据读入 五、数据理解 六、数据准备 七、模型训练 八、模型评价 一、数据及分析对象 CSV文件&#xff1a;o-ring-erosion-only.csv 数据集链接&#xff1a;https://download.csdn.net/download/m0_7…...

Fliki AI:让视频创作更简单、更高效

在当今的数字时代&#xff0c;视频已经成为人们获取信息和娱乐的重要方式。无论是企业宣传、教育培训还是个人创作&#xff0c;视频都发挥着越来越重要的作用。然而&#xff0c;视频制作是一项复杂的工作&#xff0c;需要掌握一定的技能和经验。这对于初学者或没有专业视频制作…...

webGL编程指南 第五章 MultiTexture.html

我会持续更新关于wegl的编程指南中的代码。 当前的代码不会使用书中的缩写&#xff0c;每一步都是会展开写。希望能给后来学习的一些帮助 git代码地址 &#xff1a;空 上一章节中我们学习texParameteri的使用,这一章节中我们两个图片进行混合 <!DOCTYPE html> <htm…...

mysql8安装和驱动jar包下载

方式一&#xff1a;基于docker安装 下拉镜像 docker pull mysql:8.0.21 启动镜像 docker run -p 3307:3306 --name mysql -e MYSQL_ROOT_PASSWORDhadoop -d mysql:8.0.21 启动成功后&#xff0c;进入容器内部拷贝配置文件&#xff0c;到宿主主机 docker cp mysql:/etc/mysql…...

(SpringBoot)第五章:SpringBoot创建和使用

文章目录 一&#xff1a;Spring和SpringBoot&#xff08;1&#xff09;Spring已解决和未解决的问题&#xff08;2&#xff09;SpringBoot 二&#xff1a;Spring项目的创建&#xff08;1&#xff09;IDEA创建&#xff08;2&#xff09;网页端创建 三&#xff1a;项目目录介绍及运…...

Linux重定向

文章目录 1. 文件描述符分配规则2. 重定向接口dup2自定义shell重定向(补充) 3. 标准输出和标准错误4. 如何理解一切接文件 本章代码gitee地址&#xff1a;文件重定向 1. 文件描述符分配规则 文件描述符的分配规则是从0下标开始&#xff0c;寻址最小的没有使用的数组位置&#…...

Python之文件与文件夹操作及 pytest 测试习题

目录 1、文本文件读写基础。编写程序&#xff0c;在 当前目录下创建一个文本文件 test.txt&#xff0c;并向其中写入字符串 hello world。2、编写一个程序 demo.py&#xff0c;要求运行该程序后&#xff0c;生成 demo_new.py 文件&#xff0c;其中内容与demo.py 一样&#xff0…...

物联网:实现数据驱动决策,推动经济发展

开发物联网系统的意义主要体现在以下几个方面&#xff1a; 连接一切&#xff1a;物联网的目标是连接一切&#xff0c;将生活中的各种物理对象互联起来。通过物联网开发&#xff0c;我们可以实现各类设备的智能化&#xff0c;包括家居设备、交通工具、工业设备等。这将为人们提…...

Leetcode 2929. Distribute Candies Among Children II

Leetcode 2929. Distribute Candies Among Children II 1. 解题思路2. 代码实现 题目链接&#xff1a;2929. Distribute Candies Among Children II 1. 解题思路 这一题很惭愧&#xff0c;没能自力搞定&#xff0c;最后是看了大佬的思路之后才做出来的&#xff0c;唉…… 这…...

【面经】ES中分片是什么?副本是什么?

ES分片 分片是将一个索引切分为多个底层物理的Lucene索引&#xff0c;这些被切分出来的每个部分称为一个分片。 每个分片都是一个全功能且独立的索引&#xff0c;可由集群中的任何主机存储。 在创建索引时&#xff0c;用户可以指定其分片的数量。 默认情况下&#xff0c;每个索…...

【算法练习Day46】判断子序列不同的子序列

​&#x1f4dd;个人主页&#xff1a;Sherry的成长之路 &#x1f3e0;学习社区&#xff1a;Sherry的成长之路&#xff08;个人社区&#xff09; &#x1f4d6;专栏链接&#xff1a;练题 &#x1f3af;长路漫漫浩浩&#xff0c;万事皆有期待 文章目录 判断子序列不同的子序列总结…...

Java设计模式之访问者模式

目录 定义 结构 案例 优点 缺点 使用场景 扩展 分派 案例实现须知 动态分派 静态分派 双分派 定义 封装一些作用于某种数据结构中的各元素的操作&#xff0c;它可以在不改变这个数据结构的前提下定义作用于这些元素的新的操作。 结构 访问者模式包含以下主要角色…...

PySide/PYQT如何用Qt Designer和代码来设置文字属性,如何设置文字颜色?

文章目录 📖 介绍 📖🏡 环境 🏡📒 实现方法 📒📝 Qt Designer设置📝 代码📖 介绍 📖 本人介绍如何使用Qt Designer/代码来设置字体属性(包含字体颜色) 🏡 环境 🏡 本文使用Pyside6来进行演示📒 实现方法 📒 📝 Qt Designer设置 首先打开Qt De…...

ubuntu 设置最大带宽

背景 近日做实验&#xff0c;需要限制一些机子的带宽以达到模拟的效果。在网上搜索了一阵子&#xff0c;结合自己实操的经验&#xff0c;潦草写下这篇文章&#xff0c;供自己与有需要的人参考。 环境&#xff1a; Ubuntu 22.04.1 LTS 安装 wondershaper 和 speedtest-cli w…...

如何在 Python 中执行 MySQL 结果限制和分页查询

Python MySQL 限制结果 限制结果数量 示例 1: 获取您自己的 Python 服务器 选择 “customers” 表中的前 5 条记录&#xff1a; import mysql.connectormydb mysql.connector.connect(host"localhost",user"您的用户名",password"您的密码"…...

Django配置文件,request,链接mysql方法,Orm简介

三板斧问题(views.py) HttpResponse # 返回的是字符串render # 渲染一个HTML静态文件&#xff0c;模板文件redirect # 重定向的 在视图文件中得视图函数必须要接收一个形参request&#xff0c;并且&#xff0c;视图函数也要有返回值&#xff…...

别再只画静态图了!用Qt QChart实现可交互波形图的5个高级技巧

别再只画静态图了&#xff01;用Qt QChart实现可交互波形图的5个高级技巧 在数据可视化领域&#xff0c;静态图表已经无法满足现代应用对用户体验的严苛要求。想象一下&#xff0c;当用户面对一个温度监测系统时&#xff0c;如果只能被动地观看一条固定不变的曲线&#xff0c;而…...

Canvas动画实战:用requestAnimationFrame打造会飘动的彩虹云朵

1. 从静态到动态&#xff1a;理解Canvas动画基础 第一次接触Canvas动画时&#xff0c;我盯着静态的彩虹和云朵代码发呆——明明已经能用arc()画出完美圆弧&#xff0c;为什么我的云朵就是不会动&#xff1f;后来才发现&#xff0c;Canvas绘图就像在玻璃上画画&#xff0c;每次重…...

不止于本地文件:教你改造MinerU API,让它能直接解析网盘或远程服务器上的PDF/Word

突破本地限制&#xff1a;用MinerU构建云端文档解析引擎的实战指南 当技术团队需要从海量PDF和Word文档中提取关键信息时&#xff0c;传统方案往往要求先将文件下载到本地再处理。这种模式在云存储时代显得笨拙且低效——想象一下&#xff0c;当你的文档分散在OSS、S3或企业网盘…...

【动力心法】别把 PWM 当成魔法!撕碎理想执行器的线性幻觉,论“静摩擦”与“前馈补偿”的绝对镇压

摘要&#xff1a;在纯粹的数学世界里&#xff0c;只要有输入&#xff0c;就必然有输出。但在由钢铁、齿轮、密封圈和润滑油构成的物理世界中&#xff0c;能量必须先缴纳极其昂贵的“过路费”。无数软件开发者迷信于 PID 的数学反馈&#xff0c;天真地以为微小的误差能换来微小的…...

一键修改文件创建 修改 访问时间,这款小工具太方便 小巧无广告

今天再给大家带来一款吾爱原创的轻量小工具 ——文件时间编辑器&#xff0c;由 Thebzk 开发&#xff0c;整个软件只有 376 KB&#xff0c;小巧便携&#xff0c;功能纯粹。 软件下载地址 操作也非常简单&#xff1a;选中需要修改的文件或文件夹&#xff0c;自定义设置好想要的…...

SAMD微控制器安全Flash存储库设计与实践

1. 项目概述SAMD_SafeFlashStorage 是一款专为 SAMD21&#xff08;如 Arduino Zero、MKR系列&#xff09;和 SAMD51&#xff08;如 Adafruit Metro M4、Arduino MKR VIDOR 4000&#xff09;微控制器设计的安全型闪存数据存储库。它并非简单复刻&#xff0c;而是对原始 cmaglie/…...

【CTFshow-pwn系列】03_栈溢出【pwn 062】详解:受限缓冲区下的极简 Shellcode 注入与利用实战

本文仅用于技术研究&#xff0c;禁止用于非法用途。 Author: 枷锁 在上一关&#xff08;pwn 061&#xff09;中&#xff0c;我们利用程序主动泄露的栈基址&#xff0c;通过 gets 函数毫无限制地在内存中挥洒 Payload。但 CTF 的竞技场绝不会永远如此慷慨。 来到 PWN 062&#x…...

LangChain进阶(二)RAG与真实应用落地

RAG与真实应用落地...

别再只盯着天气预报了!用翻斗式雨量传感器DIY一个家庭小气象站(附数据记录方案)

家庭气象站DIY指南&#xff1a;用翻斗式雨量传感器打造智能微气候监测系统 清晨被雨声唤醒时&#xff0c;你是否好奇过自家阳台的精确降雨量&#xff1f;传统天气预报只能提供区域性的粗略数据&#xff0c;而家庭微气候往往存在显著差异。现在&#xff0c;只需一个翻斗式雨量传…...

分布式锁的实现,选Redis还是ZooKeeper?

一、问题场景&#xff1a;为什么测试工程师需要关注分布式锁&#xff1f;在分布式系统中&#xff0c;库存超卖、定时任务重复执行、数据覆盖等典型缺陷&#xff0c;往往源于分布式锁失效。例如&#xff1a;测试环境中&#xff0c;两个服务节点同时判定库存为1并完成扣减定时任务…...