Cocos Creator 3.0 3D 模型换装及组合动画实现方案
>>PIE
引擎选择
在游戏引擎的选择上,我们之所以确定使用 Cocos Creator 3.0,主要是因为其以下几个优势:
2D & 3D 融合:Cocos Creator 3.0 实现游戏 2D 与 3D 能力的融合,在一定程度上已基本满足 PIE 所需要的 2D UI 及 3D 人物的游戏支持。
开源:Cocos 的一大特征就是开源且免费,既能够大幅度降低项目初期的试错成本,也提供给游戏引擎定制的能力。PIE 的实际业务场景相对比较复杂,因此能够根据业务需求对引擎进行特定的调整对于技术团队而言尤其重要。
良好的生态:Cocos 的技术生态相对来说完善,具备独立的技术论坛且活跃度足够,包括官方文档、各类博客在内的学习资源丰富,开发者能够快速入门并实践。同时官方对于 Cocos 也具备稳定的迭代周期,能够保证引入项目之后的稳定性。
3D 模型
目前 Cocos Creator 支持 FBX 和 glTF 两种格式的模型文件,通过对比,我们选择了 FBX 模型文件来实现我们的功能。对 FBX 模型的使用方法不太熟悉的同学,可以先移步 Cocos Creator 模型资源官方文档进行了解,上面介绍得比较详细。链接地址:
http://docs.cocos.com/creator/3.0/manual/zh/asset/mesh.html
FBX 模型结构
FBX 资源文件
包含纹理贴图、材质球、Mesh、骨骼动画和骨骼信息:
FBX 文件生成的预制体
预制体节点结构:
节点的组件结构:
从图中可以看出来,clothes 节点中 3D 属性相关的组件主要是 cc.SkinnedMeshRenderer,这个组件下有 Materials、Mesh 和 Skeleton 三个组件绑定了文件资源中的贴图,材质球和骨骼信息资源。
问题
在调研使用 FBX 模型生成的人物进行换装和播放多个动作时,遇到了如下几个问题:
只更换节点下的 Materials 组件,只能达到变换 clothes 颜色的效果,不能更换 clothes 的样式。
更换 Mesh 组件,会对整个模型的绑定关系有影响,整个人物的显示会出现问题。
人物模型在播放第二个动作时,会将第一个动作先停掉,直接开始播放第二个动作,不能同时播放多个动作。
解决方案
思路
最核心的思路就是拆分,本来是用一个模型组成的节点,拆分成多个模型,每个需要替换的部位都独立拆分成一个模型,所有部位模型的骨骼信息相同,只保留自身部位的材质,然后控制好层级,就能达到和使用一个模型同样的效果。
>>拆分前
>>拆分后
上面是拆分前和拆分后人物在 Creator 中的节点结构。
拆分前人物形象只由一个 FBX 文件生成,root 节点下是各个部位的节点。
拆分后由一个空节点 user_famale 作为根节点,根节点下的 hair-root,cloth-root,body-root 和 face-root 是拆分后各部位的父节点的占位节点,每个父节点下会添加一个的 FBX 生成的模型节点,total、hair、cloth、body 和 face 都是由 FBX 模型生成的 prefab 挂载在对应的父节点上(total-root 也是单独一个 FBX 生成的预制体,这里保存了这个人物的所有动画信息)。
由上面两种方式实现的 3D 人物,展示效果是一致的,具体如下图:
同一人物下的不同部位使用的 FBX 模型的骨骼信息必须是一样的,比如上面的人物模型,hair-root,cloth-root,body-root 和 face-root 这几个部位的对应的模型节点使用的 FBX 模型,需要使用同样的骨骼信息。
换装实现步骤
确定人物结构
按照换装需求将人物拆分成不同的部位,比如需要换衣服的,则 cloth 分出一个节点;需要换发型的,则 hair 分出一个部位,下图是实现的例子的拆分图。
关键代码
实现了一个 Role.ts 脚本组件,绑定在上面所说的 Role 节点上,以下是换装主要代码逻辑 changePart 的实现:
import { _decorator, Component, Node, Prefab, instantiate } from 'cc';
const { ccclass, property } = _decorator;
@ccclass('Role')
export class Role extends Component {
//换装节点
@property(Node) totalRoot!: Node
@property(Node) clothRoot!: Node
@property(Node) hairRoot!: Node
@property(Node) faceRoot!: Node
//换装资源
@property({ type: [Prefab] }) clothPrefabs: Prefab[] = []
@property({ type: [Prefab] }) hairPrefabs: Prefab[] = []
@property({ type: [Prefab] }) facePrefabs: Prefab[] = []
clothIdx = 0
hairIdx = 0
faceIdx = 0
start () {
// [3]
}
changePart(partPrefabs: Prefab[], partName: string, partRoot: Node) {
//更换部件
let partIdx = 0
if (partName === 'cloth') {
this.clothIdx += 1
partIdx = this.clothIdx
}
if (partName === 'hair') {
this.hairIdx += 1
partIdx = this.hairIdx
}
if (partName === 'face') {
this.faceIdx += 1
partIdx = this.faceIdx
}
const partNode = instantiate(partPrefabs[partIdx % partPrefabs.length])
partRoot.removeAllChildren()
partRoot.addChild(partNode)
}
其他
这种拆分的方式能够支持多个部位同时播放动画,但本质上没有解决同一个模型不能同时播放两个动画的问题,这里是不同部位的模型节点同时播放动画。
资源大小分析
这种拆分的方法对资源来说主要是每个部位的模型都需要有一个统一的骨骼信息,但是,如果不拆分,整个 FBX 模型的每个部位也会有自己独立的骨骼信息,所以拆分的方式和不拆分的方式资源基本一般大。
效果预览
总结
开始技术调研的时候 Creator 3.0 刚发布,以为不需要拆分,只需要替换节点下的材质和 Mesh 就能实现换装,踩了一些坑,再进一步探索后才找到这个方案。
这个方案是开发同学和设计同学共同研究探索出来,起初对 Cocos Creator 3D 的能力都不太熟悉,非常感谢设计同学的帮助。
Creator 3.0 的发布后,Cocos 对 3D 方面的支持有了巨大的提升,希望 Cocos 在 3D 方面越做越好。
「Cocos Star Meetings」上海站本周六(9月11日)下午14:00即将在上海闵行利丰广场-2号楼 WeWork 空间1楼会议室正式开始!大城小胖 x 贝塔,携手乐府互娱夏凯强、梦求游戏魏军燕、微狸科技夏羊群、以及我们的老朋友 SuperSuRaccoon 将围绕游戏开发和 Cocos 引擎使用带来一系列技术干货。活动限额80人,赶快戳【阅读原文】或扫描下方二维码报名吧!
往期精彩