原Unity 骨骼节点对象优化,AnimatorUtility.OptimizeTransformHierarchy

Posted hengsoft

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了原Unity 骨骼节点对象优化,AnimatorUtility.OptimizeTransformHierarchy相关的知识,希望对你有一定的参考价值。

关键接口:AnimatorUtility.OptimizeTransformHierarchy

需求:角色模型换装,角色模型由多个部位组合而成,暴露的骨骼节点非常多,可以通过AnimatorUtility.OptimizeTransformHierarchy接口进行优化。

但是 Unity 提供的接口AnimatorUtility.OptimizeTransformHierarchy里面有一些坑,我在这里进行讲解。

接口定义:public static void OptimizeTransformHierarchy(GameObject go, string[] exposedTransforms);

对于该接口Unity的注释是:

 This function will remove all transform hierarchy under GameObject, the animator
  will write directly transform matrices into the skin mesh matrices saving alot of CPU cycles.

意思就是使用该接口将会删除目标对象下的所有transform组件,然后animator将写入transform移动矩阵到skin mesh矩阵中以节约大量cpu时间。

简单解释也就是:将需要更新的transform对象保留,不需要的去掉,省掉很多不必要的计算。

1:骨骼全展开

技术分享图片

2:骨骼用OptimizeTransformHierarchy优化后,显示指定骨骼对象

技术分享图片

 

该接口使用,有几个条件,在此会列出:

//condition: 1: Animator togather with Render 2: Optimize is selected 3:all bones trans right 4: bone name can‘t contain space char

1: Animator 组件和SkinnedMeshRenderer必须在同一个对象中。

技术分享图片

2:OptimizeTransformHierarchy 接口的参数 exposedTransforms是 需要暴露的骨骼名称数组,并且暴露骨骼名称不能带空格,否则模型会出现显示问题(unity自己接口这样,我也没办法)

3:模型fbx设置Optimize选项必须勾选

技术分享图片

4:对SkinnedMeshRenderer中参数bones进行赋值时,必须保证对应transform存在,因为优化接口会删掉所有对象,然后从新生成骨骼节点对象。如果骨骼对象下有挂点,需要对挂点进行保存,优化完成后再对挂点或者其他额外模型进行还原。

以下是粘贴的核心代码,用于组合一个角色的多个部位,重新合成mesh,骨骼,材质,暴露指定骨骼节点对象(只暴露挂点)

private void CombineMesh(bool comine)
        {
       
    //所有部位的SkinnedMeshRenderer对象,用于合成一个SkinnedMeshRenderer
    var smrArr = GetAllBodySMR(); List<Transform> bonesdArr = new List<Transform>(); List<Material> matArr = new List<Material>(); combineInstanceArr.Clear(); for (int i = 0; i < smrArr.Count; i++) { var smrItem = smrArr[i]; if (smrItem == null) continue; if(smrItem.sharedMesh == null) { Debug.LogError("合并mesh失败 sharedMesh=null i=" + i); continue; }
matArr.AddRange(smrItem.sharedMaterials);
for (int j = 0; j < smrItem.sharedMesh.subMeshCount; j++) { var ci = new CombineInstance(); ci.mesh = smrItem.sharedMesh; ci.subMeshIndex = j; combineInstanceArr.Add(ci); } for (int k = 0; k < smrItem.bones.Length; k++) { var bone = smrItem.bones[k]; if(m_skeletonTransDic.ContainsKey(bone.name)) { if (m_skeletonTransDic[bone.name] == null) { m_skeletonTransDic.Remove(bone.name); } else { bonesdArr.Add(m_skeletonTransDic[bone.name]); } } } } if (m_wholeBodyRanderer != null) { GameObject.DestroyImmediate(m_wholeBodyRanderer); } //todo:get SkinnedMeshRenderer togather with Animator,because OptimizeTransformHierarchy need to do that. Transform animatorTrans = m_objHostModel.GetComponentInChildren<Animator>().transform; m_wholeBodyRanderer = animatorTrans.gameObject.AddComponent<SkinnedMeshRenderer>(); m_wholeBodyRanderer.sharedMesh = new Mesh(); m_wholeBodyRanderer.sharedMesh.CombineMeshes(combineInstanceArr.ToArray(), comine, false); m_wholeBodyRanderer.bones = bonesdArr.ToArray(); m_wholeBodyRanderer.sharedMaterials = matArr.ToArray(); //Bone Optimize: use interface, AnimatorUtility.OptimizeTransformHierarchy //condition: 1: Animator togather with Render 2: Optimize is selected 3:all bones trans right 4: bone name can‘t contain space char Transform[] allBoneTrans = m_wholeBodyRanderer.gameObject.GetComponentsInChildren<Transform>(false); List<string> exposedTransformsName = new List<string>(); Dictionary<string, Transform> rootGuaBoneDict = new Dictionary<string, Transform>(); bool isAllGuaBoneRight = true;// if has only one bone for (int i = 0; i < allBoneTrans.Length; i++) { //属于挂点 if (allBoneTrans[i].name.Contains("gua_")) { Transform rootBone = allBoneTrans[i].parent; if (!rootBone.name.Contains(" ")) { if (!exposedTransformsName.Contains(rootBone.name)) { rootGuaBoneDict.Add(rootBone.name, allBoneTrans[i]); exposedTransformsName.Add(rootBone.name); } } else { Debug.LogError("错误!挂点骨骼名称不能包含空格!不将对该模型进行骨骼优化。 骨骼名称:" + rootBone.name); isAllGuaBoneRight = false; } } } if (isAllGuaBoneRight) { foreach(var item in rootGuaBoneDict) { item.Value.SetParent(null); } //优化暴露的骨骼节点对象,只显示挂点相关的
AnimatorUtility.OptimizeTransformHierarchy(m_wholeBodyRanderer.gameObject, exposedTransformsName.ToArray()); //transforms has been clear, reset all hook transforms m_skeletonTransDic.Clear(); allBoneTrans = animatorTrans.gameObject.GetComponentsInChildren<Transform>(false); for (int i = 0; i < allBoneTrans.Length; i++) { m_skeletonTransDic.Add(allBoneTrans[i].name, allBoneTrans[i]); } //恢復挂点 foreach (var item in rootGuaBoneDict) { Transform guaBone = m_wholeBodyRanderer.transform.parent.Find(item.Key); if (guaBone != null) { //Debug.Log("quosin:test 恢复挂点:" + guaBone.name); item.Value.SetParent(guaBone); } else { Debug.LogError("错误!挂点无法恢復!Bone name:" + item.Key); } } } }

 







以上是关于原Unity 骨骼节点对象优化,AnimatorUtility.OptimizeTransformHierarchy的主要内容,如果未能解决你的问题,请参考以下文章

Unity骨骼动画如何优化资源

Unity 实现 角色的换装

unity 骨骼物理 头发 布料模拟

unity 骨骼物理 头发 布料模拟

Unity3D之Mecanim动画系统学习笔记:IK(反向动力学)动画

unity, Collider2D.attachedRigidbody