Unity动画优化
最近在做動畫優化插件,花了一周時間寫代碼調試效果,總算在大幅降低內存的基礎上做出了美術同學滿意的效果,目前優化過的動畫文件是未優化的動畫文件的7.5%左右的內存,寫篇博客記錄一下。
1、為什么要做動畫優化:
? ? 動畫文件在游戲包體和內存中占的比例僅次于貼圖,優化好動畫文件,就優化了一小半游戲內存。拿我們正在開發的游戲舉例,優化后的包體和內存比優化前減少了一百多兆,大概優化掉了五分之一的內存。
2、優化思路:
- 設置Animation.Compression:Animation.Compression默認選擇的是Optimal,AnimationPositionError默認0.2,AnimationRotationError默認0.1,AnimationScaleError大部分需要丟棄,所以默認就可以。具體值需要根據自己游戲調整,我們游戲由于戰斗需要拉近鏡頭所以對動畫精度要求較高,調的壓縮值比較小。
- 默認不開啟Resample Curves選項:Unity官方給的建議是開啟,但是開啟會增大內存,所以在動畫沒有問題的情況下建議關閉該選項。
- 減少幀信息的精度:將幀信息精度減少為小數點后四位,網上大多數文章也是給出的是小數點后三位,但是優化到小數點后三位導致我們游戲一些角色的披風抖動時會穿幫,所以精度優化到了小數點后四位。??
- 優化未改變的Rotation、Position序列幀和剔除Scale序列幀:如果需要用到Scale變化就在骨骼節點上加關鍵字區分。Position、Rotation有很多骨骼節點其實并未發生變化,但是由于我們Animation.Compression這兩個優化值設置的比較小,所以這里我們手動優化掉沒有變化的Position、Rotation序列幀,只留頭尾兩幀,保證Position和Rotation初始值正確,這么處理一下會降不少內存。
3、源碼:
using System.IO; using System.Linq; using Assets.Scripts.Common.Util; using UnityEditor; using UnityEngine; using FileUtil = Assets.Scripts.Common.Util.FileUtil;namespace Assets.Scripts.Editor.MenuItemEx {public class OptimizeFBX{private const float AnimationPositionError = 0.2f;private const float AnimationRotationError = 0.1f;private const ModelImporterAnimationCompression Compression = ModelImporterAnimationCompression.Optimal;private const int DecimalAccuracy = 10000;private static string _fbxPath;[MenuItem("Assets/Optimize/Optimize FBX", false, 1000)]private static void OptimizeFbx(){EditorSettings.serializationMode = SerializationMode.ForceText;var objs = Selection.GetFiltered(typeof(Object), SelectionMode.DeepAssets);var count = objs.Length;var index = 0;foreach (var obj in objs){var isCancel = EditorUtility.DisplayCancelableProgressBar("優化FBX文件",string.Format("正在優化中...{0}/{1}", ++index, count), (float) index / count);if (isCancel){EditorUtility.ClearProgressBar();return;}OptimizeObject(obj);}EditorUtility.ClearProgressBar();}public static void OptimizeObject(Object obj){var path = AssetDatabase.GetAssetPath(obj);if (path != null && Path.GetExtension(path).ToLower() == ".fbx"){_fbxPath = path;OptimizeModleImporter(path);OptimizeAnimationClip(path);}}/// <summary>/// 設置fbx動畫導入格式,默認AnimationPositionError = 0.2,AnimationRotationError = 0.1,根據項目調整,值越低優化壓縮越少。/// </summary>/// <param name="fbxPath"></param>private static void OptimizeModleImporter(string fbxPath){var modelImporter = AssetImporter.GetAtPath(fbxPath) as ModelImporter;if (modelImporter != null){var isChange = false;if (Compression != modelImporter.animationCompression){isChange = true;modelImporter.animationCompression = Compression;modelImporter.animationPositionError = AnimationPositionError;modelImporter.animationRotationError = AnimationRotationError;modelImporter.resampleCurves = false;}else{if (!Mathf.Approximately(modelImporter.animationPositionError, AnimationPositionError)){isChange = true;modelImporter.animationPositionError = AnimationPositionError;}if (!Mathf.Approximately(modelImporter.animationRotationError, AnimationRotationError)){isChange = true;modelImporter.animationRotationError = AnimationRotationError;}if (modelImporter.resampleCurves){isChange = true;modelImporter.resampleCurves = false;}}if (isChange){modelImporter.SaveAndReimport();AssetDatabase.Refresh();}}}private static void OptimizeAnimationClip(string fbxPath){var objs = AssetDatabase.LoadAllAssetsAtPath(fbxPath);var index = 0;var folderPath = string.Empty;foreach (var o in objs){if (o is AnimationClip){if (o.name == "__preview__Take 001")continue;if (index++ == 0) //只在第一次創建文件夾{folderPath = _fbxPath.Substring(0, _fbxPath.LastIndexOf('.'));FileUtil.RecreateDirectory(PathUtil.AssetPathToAbsolutePath(folderPath)); //刪除原來文件夾,重新創建新文件夾}OptimizeAnimationCurveData(o as AnimationClip, folderPath);}}}/// <summary>/// 優化動畫片段,刪除不需要的序列幀,并降低幀信息的精度/// </summary>/// <param name="clip"></param>/// <param name="folderPath"></param>/// <returns></returns>public static bool OptimizeAnimationCurveData(AnimationClip clip, string folderPath){if (clip == null){Debug.LogError("No Clip error, please tell zhanglingyun" + "? " + _fbxPath);return false;}var curveDatas = AnimationUtility.GetAllCurves(clip, true);if (curveDatas == null || curveDatas.Length == 0){Debug.LogError("No AnimationClipCurveData error, please tell zhanglingyun!" + "? " + _fbxPath + "? " +clip.name);return false;}var newClip = new AnimationClip();EditorUtility.CopySerialized(clip, newClip);newClip.name = clip.name;newClip.ClearCurves();foreach (var dt in curveDatas){var nodeName = dt.path.ToLower().Split('/').Last();// 進行過濾if (IsFilterCurveData(dt, nodeName))continue;var keys = dt.curve.keys;for (var i = 0; i < keys.Length; i++){keys[i].time = Mathf.Round(keys[i].time * DecimalAccuracy) / DecimalAccuracy;keys[i].value = Mathf.Round(keys[i].value * DecimalAccuracy) / DecimalAccuracy;keys[i].outTangent = Mathf.Round(keys[i].outTangent * DecimalAccuracy) / DecimalAccuracy;keys[i].inTangent = Mathf.Round(keys[i].inTangent * DecimalAccuracy) / DecimalAccuracy;}//過濾位移值沒有變化的幀動畫//因為幀信息有初始位置,所有要保留頭尾兩幀,如果全部刪除會出現初始位置為默認值的問題if (IsFilterApproximateKeyFrame(ref keys)){var newKeys = new Keyframe[2];newKeys[0] = keys[0];newKeys[1] = keys[keys.Length - 1];keys = newKeys;}dt.curve.keys = keys;//設置新數據newClip.SetCurve(dt.path, dt.type, dt.propertyName, dt.curve);}AssetDatabase.CreateAsset(newClip, folderPath + @"/" + newClip.name + ".anim");AssetDatabase.Refresh();return true;}/// <summary>/// 過濾值一樣的序列幀/// </summary>/// <param name="keys"></param>/// <returns></returns>private static bool IsFilterApproximateKeyFrame(ref Keyframe[]keys){for (var i = 0; i < keys.Length - 1; i++){if (Mathf.Abs(keys[i].value - keys[i + 1].value) > 0 ||Mathf.Abs(keys[i].outTangent - keys[i + 1].outTangent) > 0|| Mathf.Abs(keys[i].inTangent - keys[i + 1].inTangent) > 0){return false;}}return true;}/// <summary>/// 動畫默認不導出Scale序列幀,除非該節點包含scale關鍵詞(加scale關鍵詞表示該節點需要進行scale變換)/// </summary>/// <param name="dt"></param>/// <returns></returns>private static bool IsFilterCurveData(AnimationClipCurveData dt, string nodeName){if (dt.propertyName.ToLower().Contains("scale") && !nodeName.Contains("scale"))return true;return false;}} }4、優化對比圖:
優化前:
? ? ? ? ? ? ? ??
優化后:
? ? ? ? ? ? ? ??
參考鏈接:https://blog.uwa4d.com/archives/Optimization_Animation.html
總結
- 上一篇: 【GDSOI2017】魔兽争霸 x
- 下一篇: 2020年中级数据库系统工程师考试笔记1