我正在参加中秋创意投稿大赛,详情请看:中秋创意投稿大赛
博饼(跋饼),是起源于福建厦门的中秋传统活动,随之传播至闽南地区。始于清初,是郑成功屯兵驻兵时为解士兵的中秋相思之情、激励鼓舞士气而发明的。于是,一代一代传下来,就成了如今闽南地区独具特色的民间习俗
一年一度的中秋节又到了,在闽南地区,中秋节最期待的节目应该就是博饼了,毕竟去年博到的纸巾牙膏牙刷差不多都用完了,急需一波补给。然而疫情尚未结束,很多小伙伴在家里也想体验一下博饼的乐趣。
可是家里没有骰子怎么办?
有骰子没有大红碗怎么办?
有碗有骰子,朋友们没法聚在一起怎么办?
勇敢牛牛不怕苦难,没有船咱自己造,今天跟大家分享如何半小时快速开发一个3D博饼游戏。
step.1 准备工作
开发工具
我们使用CocosCreator来进行DEMO开发,CocosCreator是一款优秀的国产游戏开发引擎,目前刚发布了3.3版本。我们先在官网下载最新版CocosCreator
CocosCreator3.3
下载后打开新建一个3D空项目:
素材资源
我们需要一个骰子和碗的3D模型,可以到sketchfab上搜索关键词Dice、bowl找到一个你喜欢的免费模型
CocosCreator目前支持GLTF/FBX两种格式,选择任意一种下载即可
音效资源
我们需要一个背景音乐、骰子撞击声、中奖/未中奖提示音效。
这里推荐一个非常棒的国外音乐/音效网站www.epidemicsound.com/
虽然大部分音效收费,但我们可以通过network请求抓到文件地址(这个网站比较良心,没有做加密)
step.2 构建场景
资源都准备好了,我们就可以开始构建场景了。
首先把下载好的碗模型拖到场景中
右侧属性面板找到材质选项,调整材质的颜色,金属强度,粗糙度等,让模型更有质感
为了让材质的表现更加真实,我们加上一个天空盒,并勾选上useIBL选项,材质就有了反射环境的效果:
天空盒资源:
模型处理好了,但不具备物理属性,我们需要再给模型加上物理组件,一个静态刚体组件和网格碰撞器,使其具有物理碰撞效果:
由于碗的造型特殊,MeshCollider是根据模型的顶点自动生成的网格碰撞器,一般的造型建议使用方形碰撞器/球形碰撞器等组合性能更高
接下来把骰子模型拉进场景,调整材质属性,让模型质感更强一些
给骰子也加上刚体和碰撞器(骰子是动态的,刚体类型选择Dynamic)
至此场景就构建好啦,运行看一下效果:
step.3 游戏逻辑
接下来就是游戏逻辑的部分了,,新建一个脚本,挂在场景任意一个节点上(可以建一个空节点挂载)
打开Main.ts,加上简单的交互操作,点击屏幕把骰子固定在碗的上空,拖动屏幕移动骰子,松开放置骰子:
@property(Node)
public dices: Node = null;
start () {
this.dice = this.dices.children; // 保存获取到的6个骰子节点
systemEvent.on(SystemEvent.EventType.TOUCH_START, this.onTouchStart, this);
systemEvent.on(SystemEvent.EventType.TOUCH_MOVE, this.onTouchMove, this);
systemEvent.on(SystemEvent.EventType.TOUCH_END, this.onTouchEnd, this);
}
onTouchStart (e) {
for (let i = 0; i < this.dice.length; i++) {
let dice = this.dice[i];
let rb = dice.getComponent(RigidBody); // 遍历骰子节点,获取节点上的刚体组件
rb.setLinearVelocity(new Vec3(0,0,0)); // 把刚体的速度设置为0
setTimeout(() => {
rb.type = ERigidBodyType.KINEMATIC;
// 把刚体类型设置为运动学刚体,动态刚体不能通过脚本干预运动方式,在操控骰子时要先把刚体类型设置为KINEMATIC
}, 0);
// 刚体类型修改在当前帧不生效,所以加了个setTimeout强制在下一帧执行
dice.setPosition(dice.position.x, 3, dice.position.z); //只改变Y的值,骰子定位到正上方
}
}
onTouchMove (e) {
// 拖拽骰子
var delta = e.getDelta();
for (var i = 0; i < this.dice.length; i++) {
let dice = this.dice[i];
dice.setPosition(dice.position.x - delta.y / 200, 3, dice.position.z - delta.x / 200);
}
}
onTouchEnd () {
// 释放骰子,并加上一个随机的速度和角速度冲量
for (var i = 0; i < this.dice.length; i++) {
let dice = this.dice[i];
let rb = dice.getComponent(RigidBody);
setTimeout(() => {
let r = (Math.random() - 0.5) * this.power;
let ry = -(Math.random() + 0.2) * this.power;
let rt = (Math.random() - 0.5) * this.power * 20;
rb.type = ERigidBodyType.DYNAMIC; // 将RigidBody设置回动态
rb.applyImpulse(new Vec3(r, ry, r), new Vec3(0,0,0));
rb.applyTorque(new Vec3(rt, rt, rt));
}, 0);
}
}
复制代码
运行看看效果:
如果只是作为工具使用,到这步就收工啦,如果希望程序判断结果,需要再加上博饼规则的判断。
我们需要知道骰子停止运动后哪个面朝上,可以通过法线夹角判断,这里我用了更容易理解的一种方法:
给模型加上6个空的子节点,分别定为到每个面的中心位置,这样在翻滚结束后我们只要判断哪个节点的Y值最大,就是朝上的那个面了
step.4 细节完善
没有音效就没有灵魂,骰子碰撞到碗时加上一个撞击的音效,这里有一点细节,音效的音量应该根据撞击的力度动态调整,同时过滤同一个节点连续碰撞时间过短的音效播放,否则一堆骰子高速碰撞,音效会比较杂。关键代码:
start () {
this.rb = this.getComponent(RigidBody);
this.vel = new Vec3(0,0,0);
let Cld = this.getComponent(Collider);
Cld.on('onCollisionEnter', this.onCollision, this); // 碰撞事件监听
}
private onCollision (event: ICollisionEvent) {
let curTime = new Date().getTime();
if (curTime - this.lastTime > 50) {
// 过渡50ms内的连续碰撞
this.rb.getLinearVelocity(this.vel); // 获取碰撞时刚体的相对速度
this.audioSource.volume = Math.pow(Vec3.len(this.vel), 2) / 100; //根据相对速度设置音量
this.audioSource.play();
}
this.lastTime = curTime;
}
复制代码
最后再完善一些操作提示性的细节,博饼DEMO就完成啦
final DEMO演示
桌面版:中秋博饼3D
H5版:
Have fun!