OptimizeAnimationClipTool.cs 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329
  1. //****************************************************************************
  2. //
  3. // File: OptimizeAnimationClipTool.cs
  4. //
  5. // Copyright (c) SuiJiaBin
  6. //
  7. // THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
  8. // ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
  9. // THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
  10. // PARTICULAR PURPOSE.
  11. //
  12. //****************************************************************************
  13. using System;
  14. using System.Collections.Generic;
  15. using UnityEngine;
  16. using System.Reflection;
  17. using UnityEditor;
  18. using System.IO;
  19. namespace EditorTool
  20. {
  21. class AnimationOpt
  22. {
  23. static Dictionary<uint, string> _FLOAT_FORMAT;
  24. static MethodInfo getAnimationClipStats;
  25. static FieldInfo sizeInfo;
  26. static object[] _param = new object[1];
  27. static AnimationOpt()
  28. {
  29. _FLOAT_FORMAT = new Dictionary<uint, string>();
  30. for (uint i = 1; i < 6; i++)
  31. {
  32. _FLOAT_FORMAT.Add(i, "f" + i.ToString());
  33. }
  34. Assembly asm = Assembly.GetAssembly(typeof(Editor));
  35. getAnimationClipStats = typeof(AnimationUtility).GetMethod("GetAnimationClipStats", BindingFlags.Static | BindingFlags.NonPublic);
  36. Type aniclipstats = asm.GetType("UnityEditor.AnimationClipStats");
  37. sizeInfo = aniclipstats.GetField("size", BindingFlags.Public | BindingFlags.Instance);
  38. }
  39. AnimationClip _clip;
  40. string _path;
  41. public string path { get { return _path; } }
  42. public long originFileSize { get; private set; }
  43. public int originMemorySize { get; private set; }
  44. public int originInspectorSize { get; private set; }
  45. public long optFileSize { get; private set; }
  46. public int optMemorySize { get; private set; }
  47. public int optInspectorSize { get; private set; }
  48. public AnimationOpt(string path, AnimationClip clip)
  49. {
  50. _path = path;
  51. _clip = clip;
  52. _GetOriginSize();
  53. }
  54. void _GetOriginSize()
  55. {
  56. originFileSize = _GetFileZie();
  57. originMemorySize = _GetMemSize();
  58. originInspectorSize = _GetInspectorSize();
  59. }
  60. void _GetOptSize()
  61. {
  62. optFileSize = _GetFileZie();
  63. optMemorySize = _GetMemSize();
  64. optInspectorSize = _GetInspectorSize();
  65. }
  66. long _GetFileZie()
  67. {
  68. FileInfo fi = new FileInfo(_path);
  69. return fi.Length;
  70. }
  71. int _GetMemSize()
  72. {
  73. return UnityEngine.Profiling.Profiler.GetRuntimeMemorySize(_clip);
  74. }
  75. int _GetInspectorSize()
  76. {
  77. _param[0] = _clip;
  78. var stats = getAnimationClipStats.Invoke(null, _param);
  79. return (int)sizeInfo.GetValue(stats);
  80. }
  81. void _OptmizeAnimationScaleCurve()
  82. {
  83. if (_clip != null)
  84. {
  85. //去除scale曲线
  86. foreach (EditorCurveBinding theCurveBinding in AnimationUtility.GetCurveBindings(_clip))
  87. {
  88. string name = theCurveBinding.propertyName.ToLower();
  89. if (name.Contains("scale"))
  90. {
  91. AnimationUtility.SetEditorCurve(_clip, theCurveBinding, null);
  92. Debug.LogFormat("关闭{0}的scale curve", _clip.name);
  93. }
  94. }
  95. }
  96. }
  97. void _OptmizeAnimationFloat_X(uint x)
  98. {
  99. if (_clip != null && x > 0)
  100. {
  101. //浮点数精度压缩到f3
  102. AnimationClipCurveData[] curves = null;
  103. curves = AnimationUtility.GetAllCurves(_clip);
  104. Keyframe key;
  105. Keyframe[] keyFrames;
  106. string floatFormat;
  107. if (_FLOAT_FORMAT.TryGetValue(x, out floatFormat))
  108. {
  109. if (curves != null && curves.Length > 0)
  110. {
  111. for (int ii = 0; ii < curves.Length; ++ii)
  112. {
  113. AnimationClipCurveData curveDate = curves[ii];
  114. if (curveDate.curve == null || curveDate.curve.keys == null)
  115. {
  116. //Debug.LogWarning(string.Format("AnimationClipCurveData {0} don't have curve; Animation name {1} ", curveDate, animationPath));
  117. continue;
  118. }
  119. keyFrames = curveDate.curve.keys;
  120. for (int i = 0; i < keyFrames.Length; i++)
  121. {
  122. key = keyFrames[i];
  123. key.value = float.Parse(key.value.ToString(floatFormat));
  124. key.inTangent = float.Parse(key.inTangent.ToString(floatFormat));
  125. key.outTangent = float.Parse(key.outTangent.ToString(floatFormat));
  126. keyFrames[i] = key;
  127. }
  128. curveDate.curve.keys = keyFrames;
  129. _clip.SetCurve(curveDate.path, curveDate.type, curveDate.propertyName, curveDate.curve);
  130. }
  131. }
  132. }
  133. else
  134. {
  135. Debug.LogErrorFormat("目前不支持{0}位浮点", x);
  136. }
  137. }
  138. }
  139. public void Optimize(bool scaleOpt, uint floatSize)
  140. {
  141. if (scaleOpt)
  142. {
  143. _OptmizeAnimationScaleCurve();
  144. }
  145. _OptmizeAnimationFloat_X(floatSize);
  146. _GetOptSize();
  147. }
  148. public void Optimize_Scale_Float3()
  149. {
  150. Optimize(true, 3);
  151. }
  152. public void LogOrigin()
  153. {
  154. _logSize(originFileSize, originMemorySize, originInspectorSize);
  155. }
  156. public void LogOpt()
  157. {
  158. _logSize(optFileSize, optMemorySize, optInspectorSize);
  159. }
  160. public void LogDelta()
  161. {
  162. }
  163. void _logSize(long fileSize, int memSize, int inspectorSize)
  164. {
  165. Debug.LogFormat("{0} \nSize=[ {1} ]", _path, string.Format("FSize={0} ; Mem->{1} ; inspector->{2}",
  166. EditorUtility.FormatBytes(fileSize), EditorUtility.FormatBytes(memSize), EditorUtility.FormatBytes(inspectorSize)));
  167. }
  168. }
  169. public class OptimizeAnimationClipTool
  170. {
  171. static List<AnimationOpt> _AnimOptList = new List<AnimationOpt>();
  172. static List<string> _Errors = new List<string>();
  173. static int _Index = 0;
  174. [MenuItem("Assets/Animation压缩")]
  175. public static void Optimize()
  176. {
  177. _AnimOptList = FindAnims();
  178. if (_AnimOptList.Count > 0)
  179. {
  180. _Index = 0;
  181. _Errors.Clear();
  182. EditorApplication.update = ScanAnimationClip;
  183. }
  184. }
  185. [MenuItem("Tools/Animation全部压缩")]
  186. public static void OptimizeAll()
  187. {
  188. _AnimOptList = FindAllAnims();
  189. if (_AnimOptList.Count > 0)
  190. {
  191. _Index = 0;
  192. _Errors.Clear();
  193. EditorApplication.update = ScanAnimationClip;
  194. }
  195. }
  196. private static void ScanAnimationClip()
  197. {
  198. AnimationOpt _AnimOpt = _AnimOptList[_Index];
  199. bool isCancel = EditorUtility.DisplayCancelableProgressBar("优化AnimationClip", _AnimOpt.path, (float)_Index / (float)_AnimOptList.Count);
  200. _AnimOpt.Optimize_Scale_Float3();
  201. _Index++;
  202. if (isCancel || _Index >= _AnimOptList.Count)
  203. {
  204. EditorUtility.ClearProgressBar();
  205. Debug.Log(string.Format("--优化完成-- 错误数量: {0} 总数量: {1}/{2} 错误信息↓:\n{3}\n----------输出完毕----------", _Errors.Count, _Index, _AnimOptList.Count, string.Join(string.Empty, _Errors.ToArray())));
  206. Resources.UnloadUnusedAssets();
  207. GC.Collect();
  208. AssetDatabase.SaveAssets();
  209. EditorApplication.update = null;
  210. _AnimOptList.Clear();
  211. _cachedOpts.Clear();
  212. _Index = 0;
  213. }
  214. }
  215. static Dictionary<string, AnimationOpt> _cachedOpts = new Dictionary<string, AnimationOpt>();
  216. static AnimationOpt _GetNewAOpt(string path)
  217. {
  218. AnimationOpt opt = null;
  219. if (!_cachedOpts.ContainsKey(path))
  220. {
  221. AnimationClip clip = AssetDatabase.LoadAssetAtPath<AnimationClip>(path);
  222. if (clip != null)
  223. {
  224. opt = new AnimationOpt(path, clip);
  225. _cachedOpts[path] = opt;
  226. }
  227. }
  228. return opt;
  229. }
  230. static List<AnimationOpt> FindAnims()
  231. {
  232. string[] guids = null;
  233. List<string> path = new List<string>();
  234. List<AnimationOpt> assets = new List<AnimationOpt>();
  235. UnityEngine.Object[] objs = Selection.GetFiltered(typeof(object), SelectionMode.Assets);
  236. if (objs.Length > 0)
  237. {
  238. for (int i = 0; i < objs.Length; i++)
  239. {
  240. if (objs[i].GetType() == typeof(AnimationClip))
  241. {
  242. string p = AssetDatabase.GetAssetPath(objs[i]);
  243. AnimationOpt animopt = _GetNewAOpt(p);
  244. if (animopt != null)
  245. assets.Add(animopt);
  246. }
  247. else
  248. path.Add(AssetDatabase.GetAssetPath(objs[i]));
  249. }
  250. if (path.Count > 0)
  251. guids = AssetDatabase.FindAssets(string.Format("t:{0}", typeof(AnimationClip).ToString().Replace("UnityEngine.", "")), path.ToArray());
  252. else
  253. guids = new string[] { };
  254. }
  255. for (int i = 0; i < guids.Length; i++)
  256. {
  257. string assetPath = AssetDatabase.GUIDToAssetPath(guids[i]);
  258. AnimationOpt animopt = _GetNewAOpt(assetPath);
  259. if (animopt != null)
  260. assets.Add(animopt);
  261. }
  262. return assets;
  263. }
  264. static List<AnimationOpt> FindAllAnims()
  265. {
  266. List<string> objList = new List<string>();
  267. List<string> animDir = new List<string>();
  268. animDir.Add("Assets/Artise/UI_Animation");
  269. animDir.Add("Assets/Resources/Prefabs/AnimationClips");
  270. animDir.Add("Assets/ShootGame/Animations");
  271. animDir.Add("Assets/ShootGame/Animators");
  272. //animDir.Add("Assets/ShootGame/Models");
  273. for (int i = 0; i < animDir.Count; ++i)
  274. {
  275. string filter = string.Format("t:{0}", typeof(AnimationClip).ToString().Replace("UnityEngine.", ""));
  276. string[] objs = AssetDatabase.FindAssets(filter, animDir.ToArray());
  277. if (objs.Length > 0)
  278. {
  279. objList.AddRange(objs);
  280. }
  281. }
  282. List<AnimationOpt> assets = new List<AnimationOpt>();
  283. for (int i = 0; i < objList.Count; i++)
  284. {
  285. string assetPath = AssetDatabase.GUIDToAssetPath(objList[i]);
  286. AnimationOpt animopt = _GetNewAOpt(assetPath);
  287. if (animopt != null)
  288. assets.Add(animopt);
  289. }
  290. return assets;
  291. }
  292. }
  293. }