找回密码
 立即注册
查看: 330|回复: 8

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

[复制链接]
发表于 2022-10-4 11:10 | 显示全部楼层 |阅读模式
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[bw.boneIndex0];
bw.boneIndex1 = remap[bw.boneIndex1];
bw.boneIndex2 = remap[bw.boneIndex2];
bw.boneIndex3 = remap[bw.boneIndex3];
}

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

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

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

×
发表于 2022-10-4 11:13 | 显示全部楼层
这种方法是根据boneMatrix数组序去换BI、BW,其实按理来讲应该可以直接重排新导入的boneMatrix数组吧,毕竟骨骼比顶点少得多,而且也不用改sharedMesh了。
发表于 2022-10-4 11:19 | 显示全部楼层
?小黑子,你干嘛[好奇]
发表于 2022-10-4 11:23 | 显示全部楼层
你食不食油饼
发表于 2022-10-4 11:24 | 显示全部楼层
或许你这个办法会更好,但是我不想再受这个问题的折磨了[思考]
发表于 2022-10-4 11:31 | 显示全部楼层
用magic cloth 插件换装,不用自己处理
发表于 2022-10-4 11:33 | 显示全部楼层
我只记得这个插件是个物理布料插件了,没想起来它还有换装
发表于 2022-10-4 11:42 | 显示全部楼层
看吧,露出鸡了吧
发表于 2022-10-4 11:43 | 显示全部楼层
我其实是ikun
懒得打字嘛,点击右侧快捷回复 【右侧内容,后台自定义】
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Unity开发者联盟 ( 粤ICP备20003399号 )

GMT+8, 2024-7-4 09:27 , Processed in 0.093444 second(s), 26 queries .

Powered by Discuz! X3.5 Licensed

© 2001-2024 Discuz! Team.

快速回复 返回顶部 返回列表