原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的主要内容,如果未能解决你的问题,请参考以下文章