原文地址 blog.csdn.net
游戏的换装, 一般分为 3 种. 换材质, 骨骼挂接, 共享骨骼. 用的比较多的是骨骼挂接和共享骨骼.
1. 骨骼挂接
没有动作的骨骼挂接, 适合武器.
有动作的骨骼挂接, 适合坐骑.
2. 共享骨骼
共享骨骼, 适合身体部件. 主模型 (身体) 包含整个骨骼, 部件模型只包含自己部分的骨骼, 应用的时候, 部件模型的骨骼共享主模型的骨骼. 这样的话, 只需要播放主模型的动画, 部件会跟着动.
Unity 的换装呢, 你要百度一下, 能搜出一堆文章, 方法也是多种多样. 有参考价值的却不多.
比如主流推荐的官方 demo 提供的换装方式. 合并 mesh, 多此一举又麻烦.
比如 avatar 的方式 ... 不适合 Legacy 动画
其实用以前端游的方式, 共享骨骼就行了, Ogre 引擎直接提供函数 OGRE::shareSkeletonInstanceWith() 来实现. 而 Unity 引擎没有这个函数, 怎么办呢, 自己写一个呗. 命名我也取一样的, 表示对 Unity 自己不封装这个函数的鄙视.
// 共享骨骼
public static void ShareSkeletonInstanceWith(SkinnedMeshRenderer selfSkin, GameObject target)
{
Transform[] newBones = new Transform[selfSkin.bones.Length];
for (int i = 0; i < selfSkin.bones.GetLength(0); ++i)
{
GameObject bone = selfSkin.bones[i].gameObject;
// 目标的SkinnedMeshRenderer.bones保存的只是目标mesh相关的骨骼,要获得目标全部骨骼,可以通过查找的方式.
newBones[i] = FindChildRecursion(target.transform, bone.name);
}
selfSkin.bones = newBones;
}
// 递归查找
public static Transform FindChildRecursion(Transform t, string name)
{
foreach (Transform child in t)
{
if (child.name == name)
{
return child;
}
else
{
Transform ret = FindChildRecursion(child, name);
if (ret != null)
return ret;
}
}
return null;
}
代码很简单, 原理我再结合 Unity 仔细分析下.
第一步 :
假设整个模型分两部分, 身体和翅膀, 身体作为主模型, 翅膀作为部件模型, 身体 30 根骨骼, 翅膀 6 根骨骼, 总数量 36 根骨骼.
导出身体模型的时候, 选中身体和整个骨骼 (注意是整个骨骼), 这样身体也包含了翅膀的骨骼.
导出翅膀模型的时候, 选中翅膀和翅膀骨骼 (翅膀不用导整个骨骼).
第二步:
模型有哪些骨骼, 可以放入 Unity 查看 root 下面的节点 , 这里有个注意的地方, 通过 SkinnedMeshRenderer.bones 查看的骨骼数量总是比通过 root 节点查看的数个数量少, 因为 SkinnedMeshRenderer.bones 保存的模型真正用到的骨骼, 而不是整个骨骼, 比如身体模型的 SkinnedMeshRenderer.bones 只有 30, 而不是 36.
第三步:
平级挂接 2 个模型到同一个父节点下测试, 播放身体模型的动画, 身体会动, 翅膀不会, 很正常, 因为动的是身体模型的骨骼, 翅膀模型的骨骼根本就没动. 接下来把翅膀模型的 6 个骨骼设置成身体模型对应的那 6 个骨骼的 transform, 再播放, 就发现翅膀也跟着动了, 所谓共享, 就是对应的把自己的骨骼设置成目标的骨骼.
函数调用很简单 ShareSkeletonInstanceWith(翅膀, 身体)