AssetBundleBuilderTools.cs 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215
  1. using System;
  2. using System.IO;
  3. using System.Collections;
  4. using System.Collections.Generic;
  5. using UnityEngine;
  6. using UnityEditor;
  7. using UnityEditor.Animations;
  8. namespace YooAsset.Editor
  9. {
  10. public static class AssetBundleBuilderTools
  11. {
  12. /// <summary>
  13. /// 检测所有损坏的预制体文件
  14. /// </summary>
  15. public static void CheckCorruptionPrefab(List<string> searchDirectorys)
  16. {
  17. if (searchDirectorys.Count == 0)
  18. throw new Exception("路径列表不能为空!");
  19. // 获取所有资源列表
  20. int checkCount = 0;
  21. int invalidCount = 0;
  22. string[] findAssets = EditorTools.FindAssets(EAssetSearchType.Prefab, searchDirectorys.ToArray());
  23. foreach (string assetPath in findAssets)
  24. {
  25. UnityEngine.Object prefab = AssetDatabase.LoadAssetAtPath(assetPath, typeof(UnityEngine.Object));
  26. if (prefab == null)
  27. {
  28. invalidCount++;
  29. Debug.LogError($"发现损坏预制件:{assetPath}");
  30. }
  31. EditorTools.DisplayProgressBar("检测预制件文件是否损坏", ++checkCount, findAssets.Length);
  32. }
  33. EditorTools.ClearProgressBar();
  34. if (invalidCount == 0)
  35. Debug.Log($"没有发现损坏预制件");
  36. }
  37. /// <summary>
  38. /// 检测所有动画控制器的冗余状态
  39. /// </summary>
  40. public static void FindRedundantAnimationState(List<string> searchDirectorys)
  41. {
  42. if (searchDirectorys.Count == 0)
  43. throw new Exception("路径列表不能为空!");
  44. // 获取所有资源列表
  45. int checkCount = 0;
  46. int findCount = 0;
  47. string[] findAssets = EditorTools.FindAssets(EAssetSearchType.RuntimeAnimatorController, searchDirectorys.ToArray());
  48. foreach (string assetPath in findAssets)
  49. {
  50. AnimatorController animator= AssetDatabase.LoadAssetAtPath<AnimatorController>(assetPath);
  51. if (FindRedundantAnimationState(animator))
  52. {
  53. findCount++;
  54. Debug.LogWarning($"发现冗余的动画控制器:{assetPath}");
  55. }
  56. EditorTools.DisplayProgressBar("检测冗余的动画控制器", ++checkCount, findAssets.Length);
  57. }
  58. EditorTools.ClearProgressBar();
  59. if (findCount == 0)
  60. Debug.Log($"没有发现冗余的动画控制器");
  61. else
  62. AssetDatabase.SaveAssets();
  63. }
  64. /// <summary>
  65. /// 清理所有材质球的冗余属性
  66. /// </summary>
  67. public static void ClearMaterialUnusedProperty(List<string> searchDirectorys)
  68. {
  69. if (searchDirectorys.Count == 0)
  70. throw new Exception("路径列表不能为空!");
  71. // 获取所有资源列表
  72. int checkCount = 0;
  73. int removedCount = 0;
  74. string[] findAssets = EditorTools.FindAssets(EAssetSearchType.Material, searchDirectorys.ToArray());
  75. foreach (string assetPath in findAssets)
  76. {
  77. Material mat = AssetDatabase.LoadAssetAtPath<Material>(assetPath);
  78. if (ClearMaterialUnusedProperty(mat))
  79. {
  80. removedCount++;
  81. Debug.LogWarning($"材质球已被处理:{assetPath}");
  82. }
  83. EditorTools.DisplayProgressBar("清理冗余的材质球", ++checkCount, findAssets.Length);
  84. }
  85. EditorTools.ClearProgressBar();
  86. if (removedCount == 0)
  87. Debug.Log($"没有发现冗余的材质球");
  88. else
  89. AssetDatabase.SaveAssets();
  90. }
  91. /// <summary>
  92. /// 清理无用的材质球属性
  93. /// </summary>
  94. private static bool ClearMaterialUnusedProperty(Material mat)
  95. {
  96. bool removeUnused = false;
  97. SerializedObject so = new SerializedObject(mat);
  98. SerializedProperty sp = so.FindProperty("m_SavedProperties");
  99. sp.Next(true);
  100. do
  101. {
  102. if (sp.isArray == false)
  103. continue;
  104. for (int i = sp.arraySize - 1; i >= 0; --i)
  105. {
  106. var p1 = sp.GetArrayElementAtIndex(i);
  107. if (p1.isArray)
  108. {
  109. for (int ii = p1.arraySize - 1; ii >= 0; --ii)
  110. {
  111. var p2 = p1.GetArrayElementAtIndex(ii);
  112. var val = p2.FindPropertyRelative("first");
  113. if (mat.HasProperty(val.stringValue) == false)
  114. {
  115. Debug.Log($"Material {mat.name} remove unused property : {val.stringValue}");
  116. p1.DeleteArrayElementAtIndex(ii);
  117. removeUnused = true;
  118. }
  119. }
  120. }
  121. else
  122. {
  123. var val = p1.FindPropertyRelative("first");
  124. if (mat.HasProperty(val.stringValue) == false)
  125. {
  126. Debug.Log($"Material {mat.name} remove unused property : {val.stringValue}");
  127. sp.DeleteArrayElementAtIndex(i);
  128. removeUnused = true;
  129. }
  130. }
  131. }
  132. }
  133. while (sp.Next(false));
  134. so.ApplyModifiedProperties();
  135. return removeUnused;
  136. }
  137. /// <summary>
  138. /// 查找动画控制器里冗余的动画状态机
  139. /// </summary>
  140. private static bool FindRedundantAnimationState(AnimatorController animatorController)
  141. {
  142. if (animatorController == null)
  143. return false;
  144. string assetPath = AssetDatabase.GetAssetPath(animatorController);
  145. // 查找使用的状态机名称
  146. List<string> usedStateNames = new List<string>();
  147. foreach (var layer in animatorController.layers)
  148. {
  149. foreach (var state in layer.stateMachine.states)
  150. {
  151. usedStateNames.Add(state.state.name);
  152. }
  153. }
  154. List<string> allLines = new List<string>();
  155. List<int> stateIndexList = new List<int>();
  156. using (StreamReader reader = File.OpenText(assetPath))
  157. {
  158. string content;
  159. while (null != (content = reader.ReadLine()))
  160. {
  161. allLines.Add(content);
  162. if (content.StartsWith("AnimatorState:"))
  163. {
  164. stateIndexList.Add(allLines.Count - 1);
  165. }
  166. }
  167. }
  168. List<string> allStateNames = new List<string>();
  169. foreach (var index in stateIndexList)
  170. {
  171. for (int i = index; i < allLines.Count; i++)
  172. {
  173. string content = allLines[i];
  174. content = content.Trim();
  175. if (content.StartsWith("m_Name"))
  176. {
  177. string[] splits = content.Split(':');
  178. string name = splits[1].TrimStart(' '); //移除前面的空格
  179. allStateNames.Add(name);
  180. break;
  181. }
  182. }
  183. }
  184. bool foundRedundantState = false;
  185. foreach (var stateName in allStateNames)
  186. {
  187. if (usedStateNames.Contains(stateName) == false)
  188. {
  189. Debug.LogWarning($"发现冗余的动画文件:{assetPath}={stateName}");
  190. foundRedundantState = true;
  191. }
  192. }
  193. return foundRedundantState;
  194. }
  195. }
  196. }