还记得小时候玩过的见缝插针游戏吗,比一比看谁插得针比较多,可有趣了,当然了,通过它可以训练自己的手速反应,以及射击水平,把握时机,得分越高就越有成就感,相信小朋友们会喜欢它的,游戏实现原理看似简单,实则包含了数学中几何知识,接下来讲一讲实现过程吧。
这篇文章主要是用微信小程序项目做的,需要用微信开发者工具打开,
打开微信开发者工具,选择创建小程序,项目名称自己填写,例如miniprogram-shoot-scope
,
如下图,依次选择即可
如果要选创建小游戏项目来做,也是可以的,实现步骤大同小异,
可以将小程序的游戏源码改写到小游戏项目中,有兴趣可以看看笔者写得这篇文章来做
【贪吃蛇】微信小程序的游戏转到小游戏上实现方法详解)
接下来,创建一个游戏页面,文件路径是/pages/game/game
,
打开布局文件/pages/game/game.wxml
,
在里面放一个画布组件(Canvas元素)就可以了,内容如下
<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.gameData
if (newShotData.speed > 0) return
newShotData.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.data
shots.length = 0 //重置时候,将所有钉上的针箭都清除
},
//实现绘制方法
redraw(data) {
const { canvas, context: ctx, topBar } = data
let { 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 + paddingTop
let x2 = Math.cos(radian) * c + x
//调用对象的绘制方法,传入的是针箭的两端坐标点
item.redraw(x, y + paddingTop, x2, y2)
})
//旋转位置更新
let offset = speed / 10
this.data.offset = offset
this.data.angle = (angle + offset) % 360
}
})
在初中数学课上才有讲
勾股定理
哦,可见学好数学对做游戏来说是多么重要
这里添加在底部针箭的对象,代码如下
const that = this
//调用引擎的添加背景对象方法,绘制底部的针箭
engine.addBgObject({
data: newShotData,
reset() {
this.data.y = startY
this.data.speed = 0 //重置时候,速度为0表示不动
shot?.reset()
},
redraw(data) {
const { canvas, topBar } = data
//其中y就是移动位置,通过改变这个值可实现上下移动射击
let { y, speed } = this.data
let x1 = canvas.width / 2
let y1 = y + centerScopeData.y + centerScopeData.r
let y2 = y1 + shot.data.linerLength
//调用对象的绘制方法
shot.redraw(x1, y1, x1, y2)
if (speed > 0) {
//当y的值还是大于0时,说明它还没有钉到靶中心,继续移动
if (y >= (topBar?.bottom || 0)) {
this.data.y -= speed
return
}
//执行到这里,说明它刚刚钉上靶中心,就设置它在靶中心的角度以及偏移位置
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 = startY
this.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 } = data
let { x, y, r, initTime } = this.data
let paddingTop = topBar?.bottom || 0
//绘制靶心
ctx.strokeStyle = 'black'
ctx.fillStyle = 'red'
ctx.lineWidth = 1
ctx.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
}
游戏引擎会处理其它实现的细节,基本的绘制流程无需我们操心,这样用实现起来会方便一些,
写完整个项目,就可以运行测试了,
运行效果动图如下,点击屏幕任意位置就可以发射
想看项目源码 请点击这里,在资源一栏下有个名称为 见缝插针游戏源码,
请放心下载,感谢支持❤
如果是在手机上浏览此文章的,可能看不到资源一栏下的项目源码,在电脑上浏览器看就能看到了