maltadirk 发表于 2022-10-4 11:10

Unity踩坑记录(1)换装系统之坑:重新绑定之后的骨骼排序问题

Unity踩坑记录(1)换装系统之坑:重新绑定之后的骨骼排序问题

最近我的游戏正在开发换装系统,最开始的时候我在资源商店买了个资源包,以这个资源包为基础开发了换装系统,一切顺利,顺利得我以为换装系统根本没有难点。


但是,年轻的开发者呦,Unity会给每个抱有这种想法的开发者一个大比斗。当我想要再新建模一些资源导入Unity来作为换装部件的时候,两个大坑正在等着我。
第一:使用maya绑定的骨骼模型导入到Unity之后,根骨骼节点不是root。
第二:重新导入unity之后的蒙皮模型对应的骨骼索引会有变化
第一个问题的解决过程:
我觉得资源包里的发型都不够个性,所以我就新建模了一个发型,打算导入到换装系统。




由于我使用的是购买得来的资源包,所以我直接把骨骼导入到maya中,然后对头发做了绑定。
等我把模型再导入回Unity,就看到了下面的景象:


这位仁兄你肚皮上的毛长得挺性感的。
经过很久的排查,我终于从资源上发现了问题出在哪里。
每个导入的骨骼模型的资源都会自动在模型子物体上添加一个SkinnedMeshRenderer组件,从这里我发现,别人的根骨骼都是root,而我做的头发的根骨骼是head。


这就导致我的头发在设置骨骼的时候,定位到了胯部。
但是我在从maya导出模型的时候,明明把每一根骨骼都选中了的。
这个问题没有困扰我太久,因为我没有选择在maya这一个软件上吊死。我尝试使用max和blender都做了一次绑定,这两个软件导出的模型的根骨骼都是正确的。
所以这个问题的结论是,别使用maya做绑定!至于max和blender,我推荐使用blender,max的绑定功能真的难用。
第二个问题的解决过程:
虽然根骨骼正确了,但是我没有高兴几秒,因为现在它的效果是这样的:


它的权重乱了,乱得一塌糊涂。
明明骨骼树都是一样的,为啥会乱。在经过一白天的搜索资料之后,终于明白了,原来骨骼导出再重新导入之后,虽然在层级视图中看起来和导出之前是一样的,但是在骨骼数组中的排序早就乱了。
蒙皮后的mesh文件里面有这样一项属性:BlendIndices
顶点就是通过这个属性来找到对应的骨骼的,所以在换装加载mesh的时候需要重新把这个属性和人物的骨骼树对应。


至于重新对应的方法我是从这个地方找到的:
https://forum.unity.com/threads/assetpostprocessor-skinned-mesh-renderer-bone-index-reorder-solved.401872/

但是这个网址的分享也是个大坑,如果你直接把他给的代码复制到了项目中,Unity会直接把所有的骨骼模型都重新导入一遍,然后你就会发现原来的所有模型都乱套了。别问我怎么知道了,都是泪。
所以我这里把没有毒副作用的代码放在这里了。
//方法:根据人物(source)的骨骼,对服装(rend)的骨骼进行重新排序,然后重设模型的骨骼索引
void Process(SkinnedMeshRenderer rend , SkinnedMeshRenderer source)
{

if (rend == null) return;

//骨骼列表
List<Transform> tList = new List<Transform>(rend.bones);
List<Transform> nList = new List<Transform>();

foreach(var sel in source.bones)
{
Transform T = tList.Find((x) => x.name == sel.name);
if (T != null) nList.Add(T);
}

Dictionary<int, int> remap = new Dictionary<int, int>();
for (int i = 0; i < rend.bones.Length; i++)
{
remap = nList.IndexOf(rend.bones);
}

//remap bone weight indexes
BoneWeight[] bw = rend.sharedMesh.boneWeights;
for (int i = 0; i < bw.Length; i++)
{
bw.boneIndex0 = remap.boneIndex0];
bw.boneIndex1 = remap.boneIndex1];
bw.boneIndex2 = remap.boneIndex2];
bw.boneIndex3 = remap.boneIndex3];
}

//重新映射bindposes
Matrix4x4[] bp = new Matrix4x4;
for (int i = 0; i < bp.Length; i++)
{
bp] = rend.sharedMesh.bindposes;
}

//分配新的数据
rend.sharedMesh.boneWeights = bw;
rend.sharedMesh.bindposes = bp;
}终于我的换装搞定了。

Mecanim 发表于 2022-10-4 11:13

这种方法是根据boneMatrix数组序去换BI、BW,其实按理来讲应该可以直接重排新导入的boneMatrix数组吧,毕竟骨骼比顶点少得多,而且也不用改sharedMesh了。

JoshWindsor 发表于 2022-10-4 11:19

?小黑子,你干嘛[好奇]

xiangtingsl 发表于 2022-10-4 11:23

你食不食油饼

KaaPexei 发表于 2022-10-4 11:24

或许你这个办法会更好,但是我不想再受这个问题的折磨了[思考]

ainatipen 发表于 2022-10-4 11:31

用magic cloth 插件换装,不用自己处理

Arzie100 发表于 2022-10-4 11:33

我只记得这个插件是个物理布料插件了,没想起来它还有换装

IT圈老男孩1 发表于 2022-10-4 11:42

看吧,露出鸡了吧

量子计算9 发表于 2022-10-4 11:43

我其实是ikun
页: [1]
查看完整版本: Unity踩坑记录(1)换装系统之坑:重新绑定之后的骨骼排序问题