Cocos Creator 3.7 开发一款3D台球游戏
Hello大家好,一转眼学习Cocos已经两年多了,经过努力,终于从萌新小白进阶为疑似程序猿……
转入正题,话说今年年初突然有做一个3D台球游戏的想法。说起缘由是因为当年想学习游戏开发的时候,不知应该选择什么引擎。一次无意间网上看到博毅创为 blake老师的趣味桌球小游戏制作教学,从此入坑。今年刚过完年还没开工,闲来无事,想研究研究物理反射线的原理,一开始只是当个demo来做,后来索性一想何不干脆做个台球游戏。于是用了几个月的空余时间(有空就写点,三天打鱼两天晒网)开发出这么一款作品。
欢迎大家来体验
下面分享一些游戏开发过程的心得,和技术上的实现。
游戏引擎:Cocos Creator 3.7.3
该项目源码已发布
下载地址:
CocosStore
先发几个截图看看效果 怎么样 是不是还可以
导入模型:
首先,万丈高楼平地起,我们先找到需要的资源,是3D就离不开模型了。
台球就是引擎提供的Sphere 球体
为每个球体添加刚体组件和碰撞组件
球体的一些物理属性
台球的纹理贴图
球桌的模型:
制作球杆:
既然是台球,就一定要有个球杆,总不能用手指去怼。首先按照游戏的逻辑。肯定是玩家发球时,球杆要围绕母球旋转,以此来作为打球角度,然后还要有一个发球力度。 一步一步来,我们先找到一个球杆的模型,我们需要将-z轴作为球杆的正方向,并将球杆往后挪,让它对齐父节点原点的合适位置。 往z方向挪动一定距离是因为要跟母球有一定间隔,这个距离起码要大于球体的半径。因为每次发球,球杆要移动到白球的位置,这样做是避免母球遮住一部分球杆,视觉上就不好看了。然后让球杆倾斜一定角度 我这里设置的x轴旋转-8°。
有了球杆我们发球时候让他跑到母球的位置,然后用onTouch事件返回的 touch.getDelta()方法来判断玩家是往左还是往右移动,以此控制球杆旋转角度,再用场景摄像机跟随球杆旋转,这样就有了打球的第一视角效果。
//触摸注册事件
this.touch.on(Node.EventType.TOUCH_START, this.onTouchStart, this);
this.touch.on(Node.EventType.TOUCH_MOVE, this.onTouchMove, this);
this.touch.on(Node.EventType.TOUCH_END, this.onTouchEnd, this);
this.touch.on(Node.EventType.TOUCH_CANCEL, this.onTouchEnd, this);
不过光判断左右还不行,这样一来我们只能给球杆一个固定的旋转加成,要么是正要么是负,玩家在控制上无法根据滑动大小调整打球角度,很影响体验。我们还需要给他个判断。根据玩家手指滑动的大小写一些算法来控制球杆旋转角度,可以达到一个能微调的效果。
以下是相关代码。关键地方都加了注释。(代码包含上下移动的逻辑)
private onTouchStart(touch: EventTouch) {
if (GameApp.Instance.Game_state != GAME_STATE.MOVE) {
return
}
const location = touch.getUILocation()
//当玩家按下时候 move_Tag移动标记等于触摸起始位置
const pos2 = this.node.getComponent(UITransform).convertToNodeSpaceAR(new Vec3(location.x, location.y, 0))
this.move_Tag.setPosition(pos2)
//按下时候显示反射线的一些逻辑 事件发布
EventMgr.Instance.Emit(constant.EVENT_NAME.onTouch, null)
}
private onTouchMove(touch: EventTouch) {
if (GameApp.Instance.Game_state != GAME_STATE.MOVE) {
return
}
let _dir: number = -1
const location = touch.getUILocation()
//左右滑动
const getD_X: number = touch.getDeltaX()
//上下滑动
const getD_Y: number = touch.getDeltaY()
//默认是记录左右滑动的
this.x_y = getD_X
//只要长度 防止负数
const xAbs: number = Math.abs(getD_X)
const yAbs: number = Math.abs(getD_Y)
//x和y的距离大小判断玩家是倾向于上下还是左右滑动
//this.isMove是判断当前状态是松开了还是正在滑动中
//如果正在滑动中 对上下还是左右不做判断 防止游戏移动中控制冲突
//如果正在滑动中 this.x_y = getD_X 默认就是左右移动
if ((xAbs < yAbs) && (!this.isMove)) {
//如果x距离小于y就是上下滑动了。
this.isUP = true
this.x_y = getD_Y
}
if (this.isUP) {
this.x_y = getD_Y
}
//判断本次移动方向跟上次是否一样
if (this.x_y > 0) {
_dir = 1
} else if (this.x_y < 0) {
_dir = 0
} else {
_dir = this.dir
}
//正在移动中
this.isMove = true
//如果上次的移动方向跟这次不一致 move_Tag 移动标记换到新位置计算移动距离
if (this.dir != _dir) {
this.dir = _dir
const pos = this.node.getComponent(UITransform).convertToNodeSpaceAR(new Vec3(location.x, location.y, 0))
this.move_Tag.setPosition(pos)
}
//实际移动距离
const pos2 = this.move_Tag.getComponent(UITransform).convertToNodeSpaceAR(new Vec3(location.x, location.y, 0))
if (!this.isUP) {
//如果是左右移动
//限制左右移动角度
//球杆移动角度 除以的数越大移动的越慢
let d: number = pos2.x / 10
if (d > 150) {
d = 150
} else if (d < -150) {
d = -150
}
//把角度传给球杆
EventMgr.Instance.Emit(constant.EVENT_NAME.touchMove, d)
} else {
//如果是上下平移
//偏移量的数非常小 除以的数越大移动的越慢
let yCoord: number = pos2.y / 10
let add: number = GameApp.Instance.upPosCamera
//限制一下摄像机Y的偏移 就是上下最大可以移动多少
if (yCoord > 0) {
add -= 0.08
} else {
add += 0.08
}
if (add > 8) {
add = 8
} else if (add < 3) {
add = 3
}
//如果是上下移动只要改变摄像机的偏移y坐标就可以了
GameApp.Instance.upPosCamera = add
GameApp.Instance.ThirdCamera.init(GameApp.Instance.club, GameApp.Instance.club, new Vec3(0, add, 15), 0.02, 0.03)
}
}
private onTouchEnd(touch: EventTouch) {
if (GameApp.Instance.Game_state != GAME_STATE.MOVE) {
return
}
//没有移动
this.isMove = false
//向上默认为否
this.isUP = false
//球杆停止旋转
EventMgr.Instance.Emit(constant.EVENT_NAME.endTouch, 0)
}
计算发球方向:
在线性代数中,获取两点方向,需要用B的坐标减A的坐标。得到的就是A到B的方向。也就是说随着球杆的旋转,用白球位置减掉球杆坐标得到的就是发球方向了,这个方向单位化在乘以一个速度,得到的就是母球的线性速度。
摄像机跟球杆旋转的代码
private SetFollowTrackRotation() {
//摄像机跟随目标旋转
if (!this.target && !this.lookAt) {
return
}
let u = Vec3.multiplyScalar(new Vec3(), Vec3.UP, this.positionOffset.y);
let f = Vec3.multiplyScalar(new Vec3(), this.target.forward, this.positionOffset.z);
let pos = Vec3.add(new Vec3(), this.target.position, u);
Vec3.subtract(pos, pos, f);
this.node.position = VectorTool.SmoothDampV3(this.node.position, pos, this.velocity, this.moveSmooth, 100000, 0.02);
this.forwardView = Vec3.subtract(this.forwardView, this.node.position, this.target.getWorldPosition());
this.node.lookAt(this.target.worldPosition);
}
发球的方向
//发球方向 用白球的坐标减去球杆坐标 这里startPos是球杆头部一个节点 比球杆原点要靠后一小段距离。
this.dir = Vector3.sub(this.whiteBall.worldPosition, this.club.getChildByName("startPos").getWorldPosition())
this.dir.normalize()
//方向乘以发球力度得到线性速度 因为台球是平面移动的所以Y方向速度为0
this.dir = new Vec3(this.dir.x * this.ballForce, 0, this.dir.z * this.ballForce)
//设置白球线性速度
this.whiteBody.setLinearVelocity(this.dir)
有了发球的线性速度。我们只需要控制力度条,传入一个ballForce。在球杆击球动画结束时把这个线性速度传给白球就实现了打球的操作了。
好了, 到此为止已经实现了球杆的移动。我们来看看效果
今天先写到这里,下次讲解球杆力度条、出杆动画、瞄准线的制作。如果觉得文章对您有帮助,欢迎关注我。
周无邪 微信:tuya7078