Unity中动画文件压缩优化

2020-01-20  本文已影响0人  Francis_Rose

这两天简单看了一下Unity的动画文件的优化,主要参考下面两篇
(https://blog.uwa4d.com/archives/Optimization_Animation.html) Unity动画文件优化探究
(https://blog.csdn.net/u013917120/article/details/79824448) Unity实用案例之——动画压缩
(https://www.cnblogs.com/wayland/p/7131055.html) unity的animation动画资源压缩

其中压缩的主要思路有两点:

1. 移除scale曲线

foreach(AnimationClip animClip in tAnimationClipList)
{
    foreach(EditorCurveBinding curveBinding in AnimationUtility.GetCurveBindings(animClip))
    {
        string tName = curveBinding.propertyName.ToLower();
        if(tName.Contains("scale"))
        {
            AnimationUtility.SetEditorCurve(animClip, curveBinding, null);
  }
 }
 CompressAnimationClip(animClip);
}

2. 压缩精度

public  static  void CompressAnimationClip(AnimationClip _clip) 
{
    AnimationClipCurveData[] tCurveArr = AnimationUtility.GetAllCurves(_clip); 
    Keyframe tKey; 
    Keyframe[] tKeyFrameArr; 
    for(int i = 0; i < tCurveArr.Length; ++i) 
    { 
        AnimationClipCurveData tCurveData = tCurveArr[i]; 
        if(tCurveData.curve == null || tCurveData.curve.keys == null)
        { 
            continue; 
        }
        tKeyFrameArr = tCurveData.curve.keys; 
        for(int j = 0; j < tKeyFrameArr.Length; j++) 
        { 
            tKey = tKeyFrameArr[j]; 
            tKey.value = float.Parse(tKey.value.ToString("f3")); //#.### 
            tKey.inTangent = float.Parse(tKey.inTangent.ToString("f3")); 
            tKey.outTangent = float.Parse(tKey.outTangent.ToString("f3")); 
            tKeyFrameArr[j] = tKey; 
        } 
        tCurveData.curve.keys = tKeyFrameArr; 
        _clip.SetCurve(tCurveData.path, tCurveData.type, tCurveData.propertyName, tCurveData.curve); 
    } 
}

在做完这两步后,几个关键的值确实要小了,文件大小也减小了一些。

image.png image.png

取了一个动画片段的压缩前后的数据(如上两图的对比,上面是压缩前,下面是压缩后),可以看到确有改善。

实际运用中会有几个关键的点,状态机中的动画与motion指定的动画的名字上可能不同,所以直接从controller中获取animationClips会有两个问题:

1. 名字不匹配,那么在重新绑定的时候可能绑定不上;

2. 直接new操作得到的AnimationClip即使用了压缩的操作,有可能得到的也要比直接的FBX文件中的文件要大。

然后试着直接从FBX文件中提出.anim文件(Unity中直接操作的快捷键是Ctrl+D),然后单独压缩,会发现实际确实会小很多,所以,解决思路就有了。

static void ExtractAnimationAndSave(string path)
{
    var controller = AssetDatabase.LoadAssetAtPath<AnimatorController>(path);
    if (controller == null || controller.layers == null)
    {
        return;
    }

    var fileInfo = new FileInfo(path);
    string folder = fileInfo.DirectoryName;

    var oldClipMap = new Dictionary<string, string>();

    // 提取状态机中每个状态的motion
    for (var i = 0; i < controller.layers.Length; ++i)
    {
        var stateMachine = controller.layers[layerIndex].stateMachine;
        if(stateMachine == null)
        {
            continue;
        }
        var childStates = animatorStateMachine.states;
        if(childStates == null)
        {
            continue;
        }

        for(var index = 0; index < childStates.Length; ++index)
        {
            var motion = childStates[childStateIndex].state.motion;
            if(motion == null)
            {
                continue;
            }

            if (motion is BlendTree)
            {  
                var motionBlendTree = motion as BlendTree;
                if(motionBlendTree == null || motionBlendTree.children == null)
                {
                    continue;
                }

                foreach(var child in motionBlendTree.children)
                {
                    oldClipMap.Add(child.motion.name, child.motion.name);
                }
            }
            else
            {
                oldClipMap.Add(childStates[childStateIndex].state.name, motion.name);
            }
        }
    }
    var newClipMap = new Dictionary<string, AnimationClip>();

    // 从.FBX文件中提取AnimationClip文件
    // 参考https://www.cnblogs.com/jietian331/p/7264367.html)
    foreach (var clip in oldClipMap)
    {Path.Combine(path1, path2)
        string fbxPath = $"{folder.Replace('\\', '/')}/{controller.name + "@" + clip.Value + ".FBX"}";
        fbxPath = Path.Combine("Assets", fbxPath.Substring(Application.dataPath.Length + 1)).Replace('\\', '/');

        AnimationClip src = AssetDatabase.LoadAssetAtPath<AnimationClip>(fbxPath);
        AnimationClip newClip = new AnimationClip();
        EditorUtility.CopySerialized(src, newClip);

        string animPath = $"{folder.Replace('\\', '/')}/{newClip.name + ".anim"}";
        animPath = Path.Combine("Assets", animPath.Substring(Application.dataPath.Length + 1)).Replace('\\', '/');

        if (!File.Exists(animPath))
        {
            AssetDatabase.CreateAsset(newClip, animPath);
            AnimationCompress.CompressAnimationiClip(newClip);
            AssetDatabase.SaveAssets();//保存修改
            newClipMap.Add(clip.Key, newClip);
         }
     }

      if (newClipMap.Count > 0)
      {
          // 重新绑定
          RebindAnimatorController(controller, newClipMap);
      }

       AssetDatabase.SaveAssets();//保存修改
}
上一篇下一篇

猜你喜欢

热点阅读