动画(AnimationClip)压缩

手机游戏开发者 2024-9-15 15:10:18 62 0 来自 中国
FileSize

  • FileInfo.Length取得的文件大小
  • 可以在操纵体系的文件体系中看到
MemorySize

  • Profiler.GetRuntimeMemorySize取得的内存大小
  • 可以在Profiler中通过采样看到
  • 分别在真机和Editor下举行了采样
BlobSize

  • 反射取得的AnimationClipStats.size二进制大小
  • 表现在AnimationClip的Inspector的面板上
赤色框内便是BlobSize,在我的明确,FileSize是指文件在硬盘中占的大小,BlobSize是从文件反序列化出来的对象的二进制大小。Editor下的MemorySize不光有序列化后的内存大小,还维护了一份原文件的内存。就像我们在Editor下加载一张Texture内存是双份一样,而真机下就约便是BlobSize。真机下的MemorySize和Inspector里的BlobSize非常靠近,BlobSize可以以为是真机上的内存大小,这个大小更有参考意义
同时,我也对去除Scale曲线的方法举行了实行。下图这个动画文件原来Inspector中Scale的值为4,即有Scale曲线,原始文件BlobSize为10.2KB,去除Scale曲线后,Blob Size变为7.4KB,以是BlobSize减小了27%。
Curve淘汰导致内存减小

从上面的实行可以看出来,只裁剪动画文件的压缩精度,没有引起Curve淘汰。BlobSize是不会有任何厘革的,因为每个浮点数固定占32bit。而文件大小、AB大小、Editor下的内存大小,压缩精度后不管有没有引起Curve的厘革,都会变小。
裁剪动画文件的精度,意味着点的位置发生了厘革,以是Constant Curve和Dense Curve的数目也有大概发生厘革。由于是裁剪精度以是动画的点更希奇了,而连续雷同的点更多了。以是Dense Curve是淘汰了,Constant Curve是增多了,总的内存是减小了。
Constant Curve只须要最左边的点就可以形貌一个曲线段。
4.png 只裁剪精度使BlobSize减小的实例

裁剪精度前,大小为2.2kb,ScaleCurve为0, ConstantCurve为4(57.1%),Stream(使用Optimal模式这部门数据将储存为Dense)为3(42.9%)。
裁剪精度后,大小为2.1kb,ConstantCurve为7(100%),Stream为0(0%)。裁剪完精度后导致ConstantCurve增长了3,Stream(Optimal模式下即为Dense)淘汰了3,BlobSize减小了0.1kb。
6.png 因此,可以看出,通过精度优化降低内存的方式,着实质是将曲线上过于靠近的数值(例如相差数值出现在小数点4位以后)直接变为划一,从而使部门曲线变为constant曲线来降低内存斲丧。
取BlobSize代码

AnimationClip aniClip = AssetDatabase.LoadAssetAtPath<AnimationClip> (path); var fileInfo = new System.IO.FileInfo(path); Debug.Log(fileInfo.Length);//FileSize Debug.Log(Profiler.GetRuntimeMemorySize (aniClip));//MemorySize Assembly asm = Assembly.GetAssembly(typeof(Editor)); MethodInfo getAnimationClipStats = typeof(AnimationUtility).GetMethod("GetAnimationClipStats", BindingFlags.Static | BindingFlags.NonPublic); Type aniclipstats = asm.GetType("UnityEditor.AnimationClipStats"); FieldInfo sizeInfo = aniclipstats.GetField ("size", BindingFlags.Public | BindingFlags.Instance); var stats = getAnimationClipStats.Invoke(null, new object[]{aniClip}); Debug.Log(EditorUtility.FormatBytes((int)sizeInfo.GetValue(stats)));//BlobSize 工具代码

末了附上工具的代码和扼要使用阐明,选中想要优化的文件夹或文件,右键Animation->裁剪浮点数去除Scale。
//****************************************************************************////  File:      OptimizeAnimationClipTool.cs////  Copyright (c) SuiJiaBin//// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A// PARTICULAR PURPOSE.////****************************************************************************using System;using System.Collections.Generic;using UnityEngine;using System.Reflection;using UnityEditor;using System.IO;using UnityEngine.Profiling;namespace EditorTool{    class AnimationOpt    {        static Dictionary<uint, string> _FLOAT_FORMAT;        static MethodInfo getAnimationClipStats;        static FieldInfo sizeInfo;        static object[] _param = new object[1];        static AnimationOpt()        {            _FLOAT_FORMAT = new Dictionary<uint, string>();            for (uint i = 1; i < 6; i++)            {                _FLOAT_FORMAT.Add(i, "f" + i.ToString());            }            Assembly asm = Assembly.GetAssembly(typeof(Editor));            getAnimationClipStats = typeof(AnimationUtility).GetMethod("GetAnimationClipStats", BindingFlags.Static | BindingFlags.NonPublic);            Type aniclipstats = asm.GetType("UnityEditor.AnimationClipStats");            sizeInfo = aniclipstats.GetField("size", BindingFlags.Public | BindingFlags.Instance);        }        AnimationClip _clip;        string _path;        public string path { get { return _path; } }        public long originFileSize { get; private set; }        public int originMemorySize { get; private set; }        public int originInspectorSize { get; private set; }        public long optFileSize { get; private set; }        public int optMemorySize { get; private set; }        public int optInspectorSize { get; private set; }        public AnimationOpt(string path, AnimationClip clip)        {            _path = path;            _clip = clip;            _GetOriginSize();        }        void _GetOriginSize()        {            originFileSize = _GetFileZie();            originMemorySize = _GetMemSize();            originInspectorSize = _GetInspectorSize();        }        void _GetOptSize()        {            optFileSize = _GetFileZie();            optMemorySize = _GetMemSize();            optInspectorSize = _GetInspectorSize();        }        long _GetFileZie()        {            FileInfo fi = new FileInfo(_path);            return fi.Length;        }        int _GetMemSize()        {            return Profiler.GetRuntimeMemorySize(_clip);        }        int _GetInspectorSize()        {            _param[0] = _clip;            var stats = getAnimationClipStats.Invoke(null, _param);            return (int)sizeInfo.GetValue(stats);        }        void _OptmizeAnimationScaleCurve()        {            if (_clip != null)            {                //去除scale曲线                foreach (EditorCurveBinding theCurveBinding in AnimationUtility.GetCurveBindings(_clip))                {                    string name = theCurveBinding.propertyName.ToLower();                    if (name.Contains("scale"))                    {                        AnimationUtility.SetEditorCurve(_clip, theCurveBinding, null);                        Debug.LogFormat("关闭{0}的scale curve", _clip.name);                    }                }            }        }        void _OptmizeAnimationFloat_X(uint x)        {            if (_clip != null && x > 0)            {                //浮点数精度压缩到f3                AnimationClipCurveData[] curves = null;                curves = AnimationUtility.GetAllCurves(_clip);                Keyframe key;                Keyframe[] keyFrames;                string floatFormat;                if (_FLOAT_FORMAT.TryGetValue(x, out floatFormat))                {                    if (curves != null && curves.Length > 0)                    {                        for (int ii = 0; ii < curves.Length; ++ii)                        {                            AnimationClipCurveData curveDate = curves[ii];                            if (curveDate.curve == null || curveDate.curve.keys == null)                            {                                //Debug.LogWarning(string.Format("AnimationClipCurveData {0} don't have curve; Animation name {1} ", curveDate, animationPath));                                continue;                            }                            keyFrames = curveDate.curve.keys;                            for (int i = 0; i < keyFrames.Length; i++)                            {                                key = keyFrames;                                key.value = float.Parse(key.value.ToString(floatFormat));                                key.inTangent = float.Parse(key.inTangent.ToString(floatFormat));                                key.outTangent = float.Parse(key.outTangent.ToString(floatFormat));                                keyFrames = key;                            }                            curveDate.curve.keys = keyFrames;                            _clip.SetCurve(curveDate.path, curveDate.type, curveDate.propertyName, curveDate.curve);                        }                    }                }                else                {                    Debug.LogErrorFormat("现在不支持{0}位浮点", x);                }            }        }        public void Optimize(bool scaleOpt, uint floatSize)        {            if (scaleOpt)            {                _OptmizeAnimationScaleCurve();            }            _OptmizeAnimationFloat_X(floatSize);            _GetOptSize();        }        public void Optimize_Scale_Float3(bool scaleOpt)        {            Optimize(scaleOpt, 3);        }        public void LogOrigin()        {            _logSize(originFileSize, originMemorySize, originInspectorSize);        }        public void LogOpt()        {            _logSize(optFileSize, optMemorySize, optInspectorSize);        }        public void LogDelta()        {        }        void _logSize(long fileSize, int memSize, int inspectorSize)        {            Debug.LogFormat("{0} \nSize=[ {1} ]", _path, string.Format("FSize={0} ; Mem->{1} ; inspector->{2}",                EditorUtility.FormatBytes(fileSize), EditorUtility.FormatBytes(memSize), EditorUtility.FormatBytes(inspectorSize)));        }    }    public class OptimizeAnimationClipTool    {        static List<AnimationOpt> _AnimOptList = new List<AnimationOpt>();        static List<string> _Errors = new List<string>();        static int _Index = 0;        [MenuItem("Assets/Animation/裁剪浮点数去除Scale(会删除Clip Scale动画,慎用)")]        public static void OptimizeScale()        {            _AnimOptList = FindAnims();            if (_AnimOptList.Count > 0)            {                _Index = 0;                _Errors.Clear();                //EditorApplication.update = ScanAnimationClip;                ScanAnimationClip(true);            }        }        [MenuItem("Assets/Animation/裁剪浮点数")]        public static void Optimize()        {            _AnimOptList = FindAnims();            if (_AnimOptList.Count > 0)            {                _Index = 0;                _Errors.Clear();                //EditorApplication.update = ScanAnimationClip;                ScanAnimationClip(true);            }        }        private static void ScanAnimationClip(bool scaleOpt)        {            AnimationOpt _AnimOpt = _AnimOptList[_Index];            bool isCancel = EditorUtility.DisplayCancelableProgressBar("优化AnimationClip", _AnimOpt.path, (float)_Index / (float)_AnimOptList.Count);            _AnimOpt.Optimize_Scale_Float3(scaleOpt);            _Index++;            if (isCancel || _Index >= _AnimOptList.Count)            {                EditorUtility.ClearProgressBar();                Debug.Log(string.Format("--优化完成--    错误数目: {0}    总数目: {1}/{2}    错误信息↓:\n{3}\n----------输出完毕----------", _Errors.Count, _Index, _AnimOptList.Count, string.Join(string.Empty, _Errors.ToArray())));                Resources.UnloadUnusedAssets();                GC.Collect();                AssetDatabase.SaveAssets();                EditorApplication.update = null;                _AnimOptList.Clear();                _cachedOpts.Clear();                _Index = 0;            }        }        static Dictionary<string, AnimationOpt> _cachedOpts = new Dictionary<string, AnimationOpt>();        static AnimationOpt _GetNewAOpt(string path)        {            AnimationOpt opt = null;            if (!_cachedOpts.ContainsKey(path))            {                AnimationClip clip = AssetDatabase.LoadAssetAtPath<AnimationClip>(path);                if (clip != null)                {                    opt = new AnimationOpt(path, clip);                    _cachedOpts[path] = opt;                }            }            return opt;        }        static List<AnimationOpt> FindAnims()        {            string[] guids = null;            List<string> path = new List<string>();            List<AnimationOpt> assets = new List<AnimationOpt>();            UnityEngine.Object[] objs = Selection.GetFiltered(typeof(object), SelectionMode.Assets);            if (objs.Length > 0)            {                for (int i = 0; i < objs.Length; i++)                {                    if (objs.GetType() == typeof(AnimationClip))                    {                        string p = AssetDatabase.GetAssetPath(objs);                        AnimationOpt animopt = _GetNewAOpt(p);                        if (animopt != null)                            assets.Add(animopt);                    }                    else                        path.Add(AssetDatabase.GetAssetPath(objs));                }                if (path.Count > 0)                    guids = AssetDatabase.FindAssets(string.Format("t:{0}", typeof(AnimationClip).ToString().Replace("UnityEngine.", "")), path.ToArray());                else                    guids = new string[] { };            }            for (int i = 0; i < guids.Length; i++)            {                string assetPath = AssetDatabase.GUIDToAssetPath(guids);                AnimationOpt animopt = _GetNewAOpt(assetPath);                if (animopt != null)                    assets.Add(animopt);            }            return assets;        }    }}
您需要登录后才可以回帖 登录 | 立即注册

Powered by CangBaoKu v1.0 小黑屋藏宝库It社区( 冀ICP备14008649号 )

GMT+8, 2024-10-18 22:36, Processed in 0.178425 second(s), 35 queries.© 2003-2025 cbk Team.

快速回复 返回顶部 返回列表