Unity自动提取优化Fbx模型动画文件,并生成ab包文件大小对比信息

1 篇文章 0 订阅

参考文章:https://cloud.tencent.com/developer/article/1833109
最近项目需求需要对工程中的动画文件进行优化,经分析发现,工程存在两种动画文件,一种是后缀为.anim的普通Animation文件,还有一种是.fbx的模型动画文件,本文主要针对于fbx动画文件的优化,普通Animation文件优化空间不大。通过查阅网上资料,基本上优化思路也都一致,由于网上的各种教程并没有教怎么去自动提取并绑定fbx中的动画,因此本人记录一下自身总结出来的方法,优化思路大致分为两点(经过研究发现还有第三点思路,等我测试完毕整理后再发出来,大致思路是直接检测动画中是否存在没有变化的帧曲线数据,如果存在就去掉,感觉也是一个很大的优化项):

一、缩短动画曲线数据产生的浮点数精度
因为最终打包动画文件都会转成assetbundle二进制格式文件,不管怎么缩短浮点数精度,最终都是占用一个float的空间(之所以看起来减少了文件大小,是因为unityEditor下存储的是text格式,所以会导致文件表面上减小了),那究竟是怎么达到压缩效果的呢?
原来,通过降低浮点数位数后,将曲线上过于接近的数值(例如相差数值出现在小数点4位以后)直接变为一致,可以产生更多的const曲线,从而让引擎达到更高效存储的效果,进而达到所谓的“压缩”结果。缩短float类型的精度,导致动画文件内点的位置发生了变化,引起Constant Curve和Dense Curve的数量也有可能发生变化,最终可能导致动画的点更稀疏,而连续相同的点更多了。所以Dense Curve是减少了,Constant Curve是增多了,总的内存是减小了。

代码如下:
Tips:网上有的文章说获取动画Clip的EditorCurveBinding有两种方法,非Legacy(选中动画的Inspector面板的右上角点击…切换为Debug模式就可以看到Legacy选项了)动画使用GetCurveBindings、GetEditorCurve和SetEditorCurve方法,Legacy动画要使用GetObjectReferenceCurveBindings、GetObjectReferenceCurve和SetObjectReferenceCurve方法。但是经过本人亲测,GetCurveBindings这套方式两种动画皆适用,而且另一套方法由于返回值不一样,导致使用起来极为繁琐,有知道这两套方法详细区别的大佬麻烦在评论区告知一下,感激不尽。

        /// <summary>
        /// 压缩浮点数精度
        /// </summary>
        /// <param name="clip"></param>
        private static void CompressAnimFloatNum(AnimationClip clip)
        {
            if (clip == null) return;
            EditorCurveBinding[] curveBindings = AnimationUtility.GetCurveBindings(clip);

            foreach (var curveBinding in curveBindings)
            {
                var curve = AnimationUtility.GetEditorCurve(clip, curveBinding);
                if (curve == null || curve.keys == null)
                {
                    var path = AssetDatabase.GetAssetPath(clip);
                    OptimizeAnimTools_Auto.optimizedPaths.Remove(path);
                    Debug.LogError("资源发生错误,重新导入,path:" + path);
                    AssetDatabase.ImportAsset(path);
                    return;
                }

                var keyFrames = curve.keys;
                for (int i = 0; i < keyFrames.Length; i++)
                {
                    var key = keyFrames[i];
                    key.value = float.Parse(key.value.ToString("f" + limitFloatNum));
                    key.inTangent = float.Parse(key.inTangent.ToString("f" + limitFloatNum));
                    key.outTangent = float.Parse(key.outTangent.ToString("f" + limitFloatNum));
                    key.inWeight = float.Parse(key.inWeight.ToString("f" + limitFloatNum));
                    key.outWeight = float.Parse(key.outWeight.ToString("f" + limitFloatNum));
                    keyFrames[i] = key;
                }

                curve.keys = keyFrames;
                //优化普通Animation动画有时会出现错误修改未发生变化的scale值,一番踩坑之后,发现调用一次这个就好了,希望有懂的大佬可以解答一下,感激不尽!
                AnimationUtility.SetEditorCurve(clip, curveBinding, curve);
                clip.SetCurve(curveBinding.path, curveBinding.type, curveBinding.propertyName, curve);
            }
        }

二、去除fbx动画文件中生成的冗余Scale曲线数据
从下图可以看到,fbx文件中的动画几乎每一个节点都生成了Scale曲线数据,然而fbx动画基本却不会用到Scale曲线来做动画(为了以防万一,如果需要用到这种方式来优化,请向美术同事确认是否没有用过Scale缩放来做动画),所以这部分数据是可以去除的,这部分优化程度比较大,最好尽可能优化掉。还需注意一点,这种优化方式仅限于fbx中的模型动画,如果是UI上的普通Animation动画,是不需要优化Scale曲线的,因为Unity不会生成冗余的Scale数据,而且美术同学很可能会在普通Animation动画中使用Scale缩放。
Fbx中动画编辑预览图

代码如下:

        /// <summary>
        /// 删除scale曲线(一般用于模型动画)相关
        /// </summary>
        /// <param name="clip"></param>
        private static void ReduceScaleKey(AnimationClip clip)
        {
            EditorCurveBinding[] curveBindings = AnimationUtility.GetCurveBindings(clip);
            foreach (EditorCurveBinding curveBinding in curveBindings)
            {
                string name = curveBinding.propertyName.ToLower();
                if (name.Contains("scale"))
                {
                    AnimationUtility.SetEditorCurve(clip, curveBinding, null);
                }
            }
        }

以上介绍完了动画的两种优化方式,接下来开始介绍怎么从Fbx文件中提取出包含的Aniamtion动画文件,并重新绑定优化新创建的Aniamtion动画文件。
因为Fbx文件在Unity中是只读的,内含的动画文件也是只读的,修改动画的结果实际上是保存在Library/metadata,并不会修改Fbx文件,也就是说这个修改是本地化的操作,无法放入版本管理。fbx中的animation是read-only的,要编辑需要将动画文件复制出来,可以选中fbx中的动画文件,ctrl+D复制一份出来,可以跟优化后的animation文件做个对比,复制出的文件是可以编辑的,但是作为程序员,我们肯定是希望自动化操作的。从Fbx拷贝出动画后记得一定要重新绑定新生成的Animation文件,一般Fbx动画都会生成一个AnimatorController来控制动画,修改其中的引用就好了,代码中生成的逻辑是按照我们自己项目的文件结构来的,新的Animation会输出到Fbx所在目录的Animation文件夹下,如有需要,可以自行修改,代码如下:

        /// <summary>
        /// 从Fbx文件中拷贝动画并优化和重新绑定引用
        /// </summary>
        /// <param name="path">fbx文件的相对路径</param>
        private static void CopyClipAndCompress_FromFbx(string path)
        {
            Object[] objs = AssetDatabase.LoadAllAssetsAtPath(path);
            //fbx相关的模型prefeb所在文件夹相对路径(根据自己项目的文件架构自行修改路径相关代码)
            string modelDirectoryPath = ConvertToAssetPath(new DirectoryInfo(path).Parent.Parent.FullName);
            //提取出的Animation动画输出到父目录的父目录下的Animation文件夹下
            string outAnimDirectoryPath = $"{modelDirectoryPath}/Animation";
            if (!AssetDatabase.IsValidFolder(outAnimDirectoryPath))
            {
                AssetDatabase.CreateFolder(modelDirectoryPath, "Animation");
            }

            string fbxFileName = Path.GetFileName(path);
            var animatorGuids = AssetDatabase.FindAssets("t:AnimatorController", new[] {modelDirectoryPath});
            if (animatorGuids == null || animatorGuids.Length == 0)
            {
                Debug.LogError($"从{fbxFileName} 提取动画失败,没有找到{modelDirectoryPath}目录下的AnimatorController文件!");
                return;
            }

            foreach (var _obj in objs)
            {
                if (_obj is AnimationClip && !_obj.name.Contains("__preview__"))
                {
                    EditorUtility.DisplayProgressBar($"从{fbxFileName}提取动画", _obj.name, 0);

                    //重新绑定AnimatorController引用
                    bool isBindSuccess = ReBindAnimatorController(path, _obj as AnimationClip, animatorGuids, outAnimDirectoryPath,out AnimationClip newAnimationClip);
                    if (!isBindSuccess)
                    {
                        Debug.LogError($"从{fbxFileName} 提取动画 {_obj.name} 失败,没有找到对应的AnimatorController引用!");
                        continue;
                    }
                    if (newAnimationClip == null || newAnimationClip.length==0)
                    {
                        Debug.LogError($"从{fbxFileName} 提取动画 {_obj.name} 失败,未能成功生成Animation文件!");
                        continue;
                    }
                }
            }
        }
        
        /// <summary>
        /// 重新绑定AnimatorController引用
        /// </summary>
        private static bool ReBindAnimatorController(string fbxPath, AnimationClip animationClip, string[] animatorGuids, string outAnimDirectoryPath,out AnimationClip newClip)
        {
            newClip = null;
            bool isBindSuccess = false;
            for (int i = 0; i < animatorGuids.Length; i++)
            {
                var animatorPath = AssetDatabase.GUIDToAssetPath(animatorGuids[i]);
                string newAnimatorPath = $"{outAnimDirectoryPath}/{Path.GetFileName(animatorPath)}";

                //把对应的AnimatorController文件也移动到animation输出文件夹
                AssetDatabase.MoveAsset(animatorPath, newAnimatorPath);

                AnimatorController animatorController = AssetDatabase.LoadAssetAtPath<AnimatorController>(newAnimatorPath);
                if (animatorController == null)
                {
                    return false;
                }

                var animLayers = animatorController.layers;
                foreach (var animLayer in animLayers)
                {
                    foreach (var animatorState in animLayer.stateMachine.states)
                    {
                        if (animatorState.state.motion == null)
                        {
                            Debug.LogError($"{animatorPath}的动画状态{animatorState.state.name}没有绑定动画引用");
                            continue;
                        }

                        if (AssetDatabase.AssetPathToGUID(AssetDatabase.GetAssetPath(animatorState.state.motion)) ==
                            AssetDatabase.AssetPathToGUID(fbxPath) || animatorState.state.motion.name == animationClip.name)
                        {
                            if (newClip == null)
                            {
                                newClip = new AnimationClip();
                                EditorUtility.CopySerialized(animationClip, newClip);

                                string outAnimPath = $"{outAnimDirectoryPath}/{animationClip.name}.anim";

                                //优化提取出来的animation动画
                                ReduceScaleKey(newClip);
                                CompressAnimFloatNum(newClip);

                                OptimizeAnimToolsAuto.optimizedPaths.Add(outAnimPath);
                                AssetDatabase.CreateAsset(newClip, outAnimPath);

                                Debug.Log($"从{Path.GetFileName(fbxPath)} 提取动画 {animationClip.name} 输出到 {outAnimPath} 成功!");
                            }

                            isBindSuccess = true;
                            animatorState.state.motion = newClip;
                        }
                    }
                }
            }

            return isBindSuccess;
        }

还有监听导入时自动优化的代码,需要自取:

using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using UnityEditor;
using UnityEngine;

namespace EditorTools
{
    /// <summary>
    /// 监听导入资源时自动优化文件
    /// </summary>
    public class OptimizeAnimTools_Auto : AssetPostprocessor
    {
        /// <summary>
        /// 当前已经导入的文件,用于防止重复导入
        /// </summary>
        public static List<string> optimizedPaths = new List<string>();

        //资源导入之后
        private static void OnPostprocessAllAssets(string[] imported, string[] deleted, string[] moved, string[] movedFromAssetPaths)
        {
            AssetDatabase.Refresh();
            int length = imported.Length;
            for (int i = 0; i < length; i++)
            {
                // Debug.LogError("导入:"+imported[i]);
                var obj = AssetDatabase.LoadMainAssetAtPath(imported[i]);
                if (obj != null && (imported[i].EndsWith(".anim", StringComparison.OrdinalIgnoreCase)
                                     ||imported[i].EndsWith(".fbx", StringComparison.OrdinalIgnoreCase))
                                && !optimizedPaths.Contains(imported[i]))
                {
                    EditorUtility.DisplayProgressBar("导入时自动优化动画文件", imported[i], (float) i / length);
                    OptimizeAnimTools.OptimizeAssetFile(obj);
                }
            }
            AssetDatabase.SaveAssets();
            AssetDatabase.Refresh();

            EditorUtility.ClearProgressBar();
        }
    }
}

最后,直接奉献我的Editor工具脚本全部代码吧,方便一些人可以直接复制使用:

using System;
using System.Collections.Generic;
using System.IO;
using System.Security.AccessControl;
using System.Text;
using UnityEditor;
using UnityEditor.Animations;
using UnityEngine;
using UnityEngine.Profiling;
using Object = UnityEngine.Object;

namespace EditorTools
{
    /// <summary>
    /// 优化动画文件
    /// 通过降低float精度,去除无用的scale曲线,从而降低动画的存储占用、内存占用和加载时间.
    /// </summary>
    public class OptimizeAnimTools : EditorWindow
    {
        /// <summary>
        /// 限制动画精度位数
        /// </summary>
        private const int limitFloatNum = 3;

        [MenuItem("Assets/动画优化(选中文件或文件夹)/优化压缩普通Animation文件")]
        static void OptimizeAssetFileTools()
        {
            try
            {
                if (Selection.assetGUIDs.Length <= 0)
                {
                    EditorUtility.DisplayDialog("提示", "请先选择至少一个文件或文件夹!!! ", "OK");
                    return;
                }

                for (int i = 0; i < Selection.assetGUIDs.Length; i++)
                {
                    Debug.Log("当前选中的文件或文件夹:" + AssetDatabase.GUIDToAssetPath(Selection.assetGUIDs[i]));
                }

                OptimizeAnimToolsAuto.optimizedPaths.Clear();
                Object[] selectionAsset = Selection.GetFiltered(typeof(object), SelectionMode.DeepAssets);
                int length = selectionAsset.Length;
                for (int i = 0; i < length; i++)
                {
                    string path = AssetDatabase.GetAssetPath(selectionAsset[i]);
                    if (string.IsNullOrEmpty(path) || Tools.EditorTools.isDirectoryPath(path) || !path.EndsWith(".anim"))
                    {
                        continue;
                    }

                    EditorUtility.DisplayProgressBar("优化animation文件", path, (float) i / length);

                    OptimizeAssetFile(selectionAsset[i]);
                }

                AssetDatabase.Refresh();

                OptimizeAnimToolsAuto.optimizedPaths.Clear();
                AssetDatabase.SaveAssets();
                EditorUtility.UnloadUnusedAssetsImmediate();
                EditorUtility.ClearProgressBar();

                Debug.Log("-----------------优化完成-----------------");
            }
            catch (Exception e)
            {
                EditorUtility.ClearProgressBar();
                Debug.LogException(e);
                throw;
            }
        }

        [MenuItem("Assets/动画优化(选中文件或文件夹)/提取并优化fbx中的动画")]
        static void OptimizeAssetFileToolsFromFbx()
        {
            try
            {
                if (Selection.assetGUIDs.Length <= 0)
                {
                    EditorUtility.DisplayDialog("提示", "请先选择至少一个文件或文件夹!!! ", "OK");
                    return;
                }

                for (int i = 0; i < Selection.assetGUIDs.Length; i++)
                {
                    Debug.Log("当前选中的文件或文件夹:" + AssetDatabase.GUIDToAssetPath(Selection.assetGUIDs[i]));
                }

                OptimizeAnimToolsAuto.optimizedPaths.Clear();
                Object[] selectionAsset = Selection.GetFiltered(typeof(object), SelectionMode.DeepAssets);
                int length = selectionAsset.Length;
                for (int i = 0; i < length; i++)
                {
                    string path = AssetDatabase.GetAssetPath(selectionAsset[i]);
                    if (string.IsNullOrEmpty(path) || Tools.EditorTools.isDirectoryPath(path) || !path.EndsWith(".FBX"))
                    {
                        continue;
                    }

                    OptimizeAssetFile(selectionAsset[i]);
                }

                OptimizeAnimToolsAuto.optimizedPaths.Clear();
                AssetDatabase.SaveAssets();
                AssetDatabase.Refresh();

                EditorUtility.UnloadUnusedAssetsImmediate();
                EditorUtility.ClearProgressBar();

                Debug.Log("-----------------优化完成-----------------");
            }
            catch (Exception e)
            {
                EditorUtility.ClearProgressBar();
                Debug.LogException(e);
                throw;
            }
        }

        [MenuItem("Assets/动画优化(选中文件或文件夹)/提取优化fbx中的动画并生成ab包对比信息")]
        public static void OptimizeAssetFileToolsAndCompareFromFbx()
        {
            if (Selection.assetGUIDs.Length <= 0)
            {
                EditorUtility.DisplayDialog("提示", "请先选择至少一个文件或文件夹!!! ", "OK");
                return;
            }

            Dictionary<string, string> outInfos_origin = AndroidBuild.exportResBundle(BuildAssetBundleOptions.ChunkBasedCompression, ".controller", true);
            Dictionary<string, long> outFileSizeInfos_orgin = new Dictionary<string, long>();
            foreach (var outInfo in outInfos_origin)
            {
                outFileSizeInfos_orgin.Add(outInfo.Key, EditorUtil.GetFileZie(outInfo.Value));
            }

            OptimizeAssetFileToolsFromFbx();

            Dictionary<string, string> outInfos_optimize = AndroidBuild.exportResBundle(BuildAssetBundleOptions.ChunkBasedCompression, ".controller", true);

            StringBuilder sb = new StringBuilder();

            long totalOriginSize = 0;
            long totalOptimizeSize = 0;
            foreach (var outInfo in outInfos_origin)
            {
                totalOriginSize += outFileSizeInfos_orgin[outInfo.Key] / 1024;
                sb.Append($"{outInfo.Key}    ab大小变化:{outFileSizeInfos_orgin[outInfo.Key]}B    -->    ");
                if (outInfos_optimize.ContainsKey(outInfo.Key))
                {
                    long optimizedSize = EditorUtil.GetFileZie(outInfos_optimize[outInfo.Key]);
                    totalOptimizeSize += (outFileSizeInfos_orgin[outInfo.Key] - optimizedSize) / 1024;
                    sb.AppendLine($"{optimizedSize}B    =    {(outFileSizeInfos_orgin[outInfo.Key] - optimizedSize) / 1024}KB");
                    AssetDatabase.DeleteAsset(EditorUtil.ConvertToAssetPath(outInfos_optimize[outInfo.Key]));
                }
                else
                {
                    sb.AppendLine();
                }
            }

            sb.Insert(0, $"AssetBundle包文件初始总大小:{totalOriginSize}KB    总共优化大小:{totalOptimizeSize}KB\n\n");
            string filePath = AssetDatabase.GUIDToAssetPath(Selection.assetGUIDs[0]) + "/优化前后生成的ab文件信息对比.txt";
            Debug.Log($"----------优化完成----------\nab文件对比信息输出路径:{filePath}");
            File.WriteAllText(filePath, sb.ToString());

            AssetDatabase.SaveAssets();
            EditorUtility.UnloadUnusedAssetsImmediate();
            EditorUtility.ClearProgressBar();
            AssetDatabase.Refresh();
        }

        /// <summary>
        /// 优化资源文件
        /// </summary>
        /// <param name="obj"></param>
        public static void OptimizeAssetFile(Object obj)
        {
            if (obj == null) return;
            var path = AssetDatabase.GetAssetPath(obj);

            OptimizeAnimToolsAuto.optimizedPaths.Add(path);

            //如果是动画文件直接优化
            if (obj is AnimationClip)
            {
                long originFileSize = GetFileZie(path);
                long originMemorySize = GetMemorySize(obj);

                AnimationClip clip = obj as AnimationClip;
                // 普通animation文件不会存在多余无用的scale曲线数据
                // ReduceScaleKey(clip);
                CompressAnimFloatNum(clip);

                Debug.Log(
                    $"{path}    优化前后 FileSize:{originFileSize}-->{GetFileZie(path)}    MemorySize:{originMemorySize}-->{GetMemorySize(obj)}>");
            }
            else if (path.EndsWith(".fbx", StringComparison.OrdinalIgnoreCase))
            {
                //fbx文件先提取动画再优化
                CopyClipAndCompress_FromFbx(path);
            }
        }

        /// <summary>
        /// 从Fbx文件中拷贝动画并优化和重新绑定引用
        /// </summary>
        /// <param name="path">fbx文件的相对路径</param>
        private static void CopyClipAndCompress_FromFbx(string path)
        {
            Object[] objs = AssetDatabase.LoadAllAssetsAtPath(path);
            //fbx相关的模型prefeb所在文件夹相对路径(根据自己项目的文件架构自行修改路径相关代码)
            string modelDirectoryPath = ConvertToAssetPath(new DirectoryInfo(path).Parent.Parent.FullName);
            //提取出的Animation动画输出到父目录的父目录下的Animation文件夹下
            string outAnimDirectoryPath = $"{modelDirectoryPath}/Animation";
            if (!AssetDatabase.IsValidFolder(outAnimDirectoryPath))
            {
                AssetDatabase.CreateFolder(modelDirectoryPath, "Animation");
            }

            string fbxFileName = Path.GetFileName(path);
            var animatorGuids = AssetDatabase.FindAssets("t:AnimatorController", new[] {modelDirectoryPath});
            if (animatorGuids == null || animatorGuids.Length == 0)
            {
                Debug.LogError($"从{fbxFileName} 提取动画失败,没有找到{modelDirectoryPath}目录下的AnimatorController文件!");
                return;
            }

            foreach (var _obj in objs)
            {
                if (_obj is AnimationClip && !_obj.name.Contains("__preview__"))
                {
                    EditorUtility.DisplayProgressBar($"从{fbxFileName}提取动画", _obj.name, 0);

                    //重新绑定AnimatorController引用
                    bool isBindSuccess = ReBindAnimatorController(path, _obj as AnimationClip, animatorGuids, outAnimDirectoryPath,out AnimationClip newAnimationClip);
                    if (!isBindSuccess)
                    {
                        Debug.LogError($"从{fbxFileName} 提取动画 {_obj.name} 失败,没有找到对应的AnimatorController引用!");
                        continue;
                    }
                    if (newAnimationClip == null || newAnimationClip.length==0)
                    {
                        Debug.LogError($"从{fbxFileName} 提取动画 {_obj.name} 失败,未能成功生成Animation文件!");
                        continue;
                    }
                }
            }
        }

        /// <summary>
        /// 重新绑定AnimatorController引用
        /// </summary>
        private static bool ReBindAnimatorController(string fbxPath, AnimationClip animationClip, string[] animatorGuids, string outAnimDirectoryPath,out AnimationClip newClip)
        {
            newClip = null;
            bool isBindSuccess = false;
            for (int i = 0; i < animatorGuids.Length; i++)
            {
                var animatorPath = AssetDatabase.GUIDToAssetPath(animatorGuids[i]);
                string newAnimatorPath = $"{outAnimDirectoryPath}/{Path.GetFileName(animatorPath)}";

                //把对应的AnimatorController文件也移动到animation输出文件夹
                AssetDatabase.MoveAsset(animatorPath, newAnimatorPath);

                AnimatorController animatorController = AssetDatabase.LoadAssetAtPath<AnimatorController>(newAnimatorPath);
                if (animatorController == null)
                {
                    return false;
                }

                var animLayers = animatorController.layers;
                foreach (var animLayer in animLayers)
                {
                    foreach (var animatorState in animLayer.stateMachine.states)
                    {
                        if (animatorState.state.motion == null)
                        {
                            Debug.LogError($"{animatorPath}的动画状态{animatorState.state.name}没有绑定动画引用");
                            continue;
                        }

                        if (AssetDatabase.AssetPathToGUID(AssetDatabase.GetAssetPath(animatorState.state.motion)) ==
                            AssetDatabase.AssetPathToGUID(fbxPath) || animatorState.state.motion.name == animationClip.name)
                        {
                            if (newClip == null)
                            {
                                newClip = new AnimationClip();
                                EditorUtility.CopySerialized(animationClip, newClip);

                                string outAnimPath = $"{outAnimDirectoryPath}/{animationClip.name}.anim";

                                //优化提取出来的animation动画
                                ReduceScaleKey(newClip);
                                CompressAnimFloatNum(newClip);

                                OptimizeAnimToolsAuto.optimizedPaths.Add(outAnimPath);
                                AssetDatabase.CreateAsset(newClip, outAnimPath);

                                Debug.Log($"从{Path.GetFileName(fbxPath)} 提取动画 {animationClip.name} 输出到 {outAnimPath} 成功!");
                            }

                            isBindSuccess = true;
                            animatorState.state.motion = newClip;
                        }
                    }
                }
            }

            return isBindSuccess;
        }

        /// <summary>
        /// 压缩浮点数精度
        /// </summary>
        /// <param name="clip"></param>
        private static void CompressAnimFloatNum(AnimationClip clip)
        {
            if (clip == null) return;
            EditorCurveBinding[] curveBindings = AnimationUtility.GetCurveBindings(clip);

            foreach (var curveBinding in curveBindings)
            {
                var curve = AnimationUtility.GetEditorCurve(clip, curveBinding);
                if (curve == null || curve.keys == null)
                {
                    var path = AssetDatabase.GetAssetPath(clip);
                    OptimizeAnimToolsAuto.optimizedPaths.Remove(path);
                    Debug.LogError("资源发生错误,将重新导入,path:" + path);
                    AssetDatabase.ImportAsset(path);
                    return;
                }

                var keyFrames = curve.keys;
                for (int i = 0; i < keyFrames.Length; i++)
                {
                    var key = keyFrames[i];
                    key.value = float.Parse(key.value.ToString("f" + limitFloatNum));
                    key.inTangent = float.Parse(key.inTangent.ToString("f" + limitFloatNum));
                    key.outTangent = float.Parse(key.outTangent.ToString("f" + limitFloatNum));
                    key.inWeight = float.Parse(key.inWeight.ToString("f" + limitFloatNum));
                    key.outWeight = float.Parse(key.outWeight.ToString("f" + limitFloatNum));
                    keyFrames[i] = key;
                }

                curve.keys = keyFrames;
                //有时候会出现错误修改普通animation动画未发生变化的scale值,一番踩坑之后,发现调用一次这个就好了,希望有懂的大佬可以解答一下,感激不尽!QQ:943166811
                AnimationUtility.SetEditorCurve(clip, curveBinding, curve);
                clip.SetCurve(curveBinding.path, curveBinding.type, curveBinding.propertyName, curve);
            }
        }

        /// <summary>
        /// 删除scale曲线(一般用于骨骼动画)相关
        /// </summary>
        /// <param name="clip"></param>
        private static void ReduceScaleKey(AnimationClip clip)
        {
            EditorCurveBinding[] curveBindings = AnimationUtility.GetCurveBindings(clip);
            foreach (EditorCurveBinding curveBinding in curveBindings)
            {
                string name = curveBinding.propertyName.ToLower();
                if (name.Contains("scale"))
                {
                    AnimationUtility.SetEditorCurve(clip, curveBinding, null);
                }
            }
        }
        
        ///-------------下面是用到的工具方法----------------------

	    /// <summary>
	    /// 绝对路径转化成Unity资源目录相对路径
	    /// </summary>
	    /// <returns></returns>
	    public static string ConvertToAssetPath(string str)
	    {
	        if (string.IsNullOrEmpty(str))
	        {
	            return string.Empty;
	        }
	        str = str.Replace('\\', '/');
	        str = str.Substring(str.IndexOf("Assets"));
	        return str;
	    }
	    
	    /// <summary>
	    /// Unity资源目录相对路径转化成系统绝对路径
	    /// </summary>
	    /// <returns></returns>
	    public static string ConvertToExplorePath(string str)
	    {
	        if (string.IsNullOrEmpty(str))
	        {
	            return string.Empty;
	        }
	        DirectoryInfo directoryInfo=new DirectoryInfo(str);
	        return directoryInfo.FullName;
	    }

		/// <summary>
	    /// 获取文件大小
	    /// </summary>
	    /// <returns></returns>
	    public static long GetFileZie(string path)
	    {
	        FileInfo fi = new FileInfo(path);
	        return fi.Length;
	    }
	
	    /// <summary>
	    /// 获取内存占用大小
	    /// </summary>
	    /// <returns></returns>
	    public static long GetMemorySize(UnityEngine.Object obj)
	    {
	        return Profiler.GetRuntimeMemorySizeLong(obj);
	    }

		/// <summary>
        /// 路径是否是文件夹
        /// </summary>
        public static bool isDirectoryPath(string path)
        {
            if (File.GetAttributes(path).CompareTo(FileAttributes.Directory) == 0)
            {
                return true;
            }

            return false;
        }

		/// <summary>
	    /// 选中的文件或文件夹打ab包
	    /// </summary>
	    /// <param name="op"></param>
	    /// <param name="filterSuffix"></param>
	    /// <param name="isExportDefaultPath"> 默认生成的ab文件路径为源文件路径</param>
	    public static Dictionary<string, string> exportResBundle(BuildAssetBundleOptions op, string filterSuffix = null, bool isExportDefaultPath = false)
	    {
	        //<源文件名字,ab文件路径>
	        Dictionary<string, string> outInfo = new Dictionary<string, string>();
	        var guiDs = Selection.assetGUIDs;
	        if (guiDs == null || guiDs.Length == 0)
	        {
	            Debug.LogError("please select a object or a folder");
	            return outInfo;
	        }
	
	        string selPath = null;
	        if (!isExportDefaultPath)
	        {
	            selPath = EditorUtility.OpenFolderPanel("select out directory", new DirectoryInfo(AssetDatabase.GUIDToAssetPath(Selection.assetGUIDs[0])).Parent.FullName, "");
	            if (selPath.Length <= 0)
	            {
	                isExportDefaultPath = true;
	            }
	        }
	
	        Object[] selectionAsset = Selection.GetFiltered(typeof(object), SelectionMode.DeepAssets);
	        int length = selectionAsset.Length;
	        for (int i = 0; i < length; i++)
	        {
	            var obj = selectionAsset[i];
	            string path = AssetDatabase.GetAssetPath(obj);
	            Debug.LogError("path 111:"+path);
	            if (string.IsNullOrEmpty(path) || EditorTools.Tools.EditorTools.isDirectoryPath(path) || (filterSuffix != null && !path.EndsWith(filterSuffix)))
	            {
	                continue;
	            }
	
	            Debug.LogError("path 222:"+path);
	            if (isExportDefaultPath)
	            {
	                selPath = new DirectoryInfo(path).Parent.FullName;
	            }
	
	            var compressType = op == BuildAssetBundleOptions.ChunkBasedCompression ? "lz4" : "nocompress";
	            var outPath = $"{selPath}/{obj.name}_{compressType}.bundle";
	            outInfo.Add(obj.name, outPath);
	
	            var ret = BuildPipeline.BuildAssetBundle(obj, null, outPath, op | BuildAssetBundleOptions.CollectDependencies, BuildTarget.Android);
	            if (ret)
	            {
	                D.Log("export bundle successful! " + outPath);
	            }
	            else
	            {
	                D.Error("export bundle failed");
	            }
	        }
	
	        return outInfo;
	    }
    }
}
  • 3
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 5
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值