zoukankan      html  css  js  c++  java
  • 【原】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); } } } }
  • 相关阅读:
    sql 查询某个字段出现的次数
    Spark性能优化指导及总结
    数据结构与算法基础-排序
    数据仓库中数据模型之拉链表
    Hive over()窗口函数及应用实例
    dubbo 分布式服务框架
    netty 网络框架
    实现JavaScript继承
    【ThoughtWorks西安】澳洲业务线招聘大量C#开发工程师
    使用Docker搭建自己的GitLab服务
  • 原文地址:https://www.cnblogs.com/hengsoft/p/10283628.html
Copyright © 2011-2022 走看看