阅读 2557

🎮 半小时开发3D博饼小游戏 | 文末在线玩

我正在参加中秋创意投稿大赛,详情请看:中秋创意投稿大赛

博饼(跋饼),是起源于福建厦门的中秋传统活动,随之传播至闽南地区。始于清初,是郑成功屯兵驻兵时为解士兵的中秋相思之情、激励鼓舞士气而发明的。于是,一代一代传下来,就成了如今闽南地区独具特色的民间习俗

一年一度的中秋节又到了,在闽南地区,中秋节最期待的节目应该就是博饼了,毕竟去年博到的纸巾牙膏牙刷差不多都用完了,急需一波补给。然而疫情尚未结束,很多小伙伴在家里也想体验一下博饼的乐趣。

今天跟大家分享如何半小时快速开发一个3D博饼游戏。 先体验一下最终效果

image.png

step.1 准备工作

开发工具

我们使用CocosCreator来进行DEMO开发,CocosCreator是一款优秀的国产游戏开发引擎,目前刚发布了3.3版本。我们先在官网下载最新版CocosCreator CocosCreator3.3

下载后打开新建一个3D空项目:

image.png

素材资源

我们需要一个骰子和碗的3D模型,可以到sketchfab上搜索关键词Dice、bowl找到一个你喜欢的免费模型

image.png

CocosCreator目前支持GLTF/FBX两种格式,选择任意一种下载即可

音效资源

我们需要一个背景音乐、骰子撞击声、中奖/未中奖提示音效。 这里推荐一个非常棒的国外音乐/音效网站www.epidemicsound.com/ 虽然大部分音效收费,但我们可以通过network请求抓到文件地址(这个网站比较良心,没有做加密)

image.png

step.2 构建场景

资源都准备好了,我们就可以开始构建场景了。 首先把下载好的碗模型拖到场景中

image.png 右侧属性面板找到材质选项,调整材质的颜色,金属强度,粗糙度等,让模型更有质感

image.png

为了让材质的表现更加真实,我们加上一个天空盒,并勾选上useIBL选项,材质就有了反射环境的效果:

image.png 天空盒资源:

模型处理好了,但不具备物理属性,我们需要再给模型加上物理组件,一个静态刚体组件和网格碰撞器,使其具有物理碰撞效果:

image.png

由于碗的造型特殊,MeshCollider是根据模型的顶点自动生成的网格碰撞器,一般的造型建议使用方形碰撞器/球形碰撞器等组合性能更高

接下来把骰子模型拉进场景,调整材质属性,让模型质感更强一些

image.png

给骰子也加上刚体和碰撞器(骰子是动态的,刚体类型选择Dynamic)

image.png

至此场景就构建好啦,运行看一下效果:

2.gif

step.3 游戏逻辑

接下来就是游戏逻辑的部分了,,新建一个脚本,挂在场景任意一个节点上(可以建一个空节点挂载)

image.png

打开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);
    }
}

复制代码

运行看看效果:

23.gif

如果只是作为工具使用,到这步就收工啦,如果希望程序判断结果,需要再加上博饼规则的判断。 我们需要知道骰子停止运动后哪个面朝上,可以通过法线夹角判断,这里我用了更容易理解的一种方法:

image.png

给模型加上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版:

image.png

Have fun!