AssetBundleCollector.cs 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using UnityEditor;
  5. using UnityEngine;
  6. namespace YooAsset.Editor
  7. {
  8. [Serializable]
  9. public class AssetBundleCollector
  10. {
  11. /// <summary>
  12. /// 收集路径
  13. /// 注意:支持文件夹或单个资源文件
  14. /// </summary>
  15. public string CollectPath = string.Empty;
  16. /// <summary>
  17. /// 收集器的GUID
  18. /// </summary>
  19. public string CollectorGUID = string.Empty;
  20. /// <summary>
  21. /// 收集器类型
  22. /// </summary>
  23. public ECollectorType CollectorType = ECollectorType.MainAssetCollector;
  24. /// <summary>
  25. /// 寻址规则类名
  26. /// </summary>
  27. public string AddressRuleName = nameof(AddressByFileName);
  28. /// <summary>
  29. /// 打包规则类名
  30. /// </summary>
  31. public string PackRuleName = nameof(PackDirectory);
  32. /// <summary>
  33. /// 过滤规则类名
  34. /// </summary>
  35. public string FilterRuleName = nameof(CollectAll);
  36. /// <summary>
  37. /// 资源分类标签
  38. /// </summary>
  39. public string AssetTags = string.Empty;
  40. /// <summary>
  41. /// 用户自定义数据
  42. /// </summary>
  43. public string UserData = string.Empty;
  44. /// <summary>
  45. /// 收集器是否有效
  46. /// </summary>
  47. public bool IsValid()
  48. {
  49. if (AssetDatabase.LoadAssetAtPath<UnityEngine.Object>(CollectPath) == null)
  50. return false;
  51. if (CollectorType == ECollectorType.None)
  52. return false;
  53. if (AssetBundleCollectorSettingData.HasAddressRuleName(AddressRuleName) == false)
  54. return false;
  55. if (AssetBundleCollectorSettingData.HasPackRuleName(PackRuleName) == false)
  56. return false;
  57. if (AssetBundleCollectorSettingData.HasFilterRuleName(FilterRuleName) == false)
  58. return false;
  59. return true;
  60. }
  61. /// <summary>
  62. /// 检测配置错误
  63. /// </summary>
  64. public void CheckConfigError()
  65. {
  66. string assetGUID = AssetDatabase.AssetPathToGUID(CollectPath);
  67. if (string.IsNullOrEmpty(assetGUID))
  68. throw new Exception($"Invalid collect path : {CollectPath}");
  69. if (CollectorType == ECollectorType.None)
  70. throw new Exception($"{nameof(ECollectorType)}.{ECollectorType.None} is invalid in collector : {CollectPath}");
  71. if (AssetBundleCollectorSettingData.HasPackRuleName(PackRuleName) == false)
  72. throw new Exception($"Invalid {nameof(IPackRule)} class type : {PackRuleName} in collector : {CollectPath}");
  73. if (AssetBundleCollectorSettingData.HasFilterRuleName(FilterRuleName) == false)
  74. throw new Exception($"Invalid {nameof(IFilterRule)} class type : {FilterRuleName} in collector : {CollectPath}");
  75. if (AssetBundleCollectorSettingData.HasAddressRuleName(AddressRuleName) == false)
  76. throw new Exception($"Invalid {nameof(IAddressRule)} class type : {AddressRuleName} in collector : {CollectPath}");
  77. }
  78. /// <summary>
  79. /// 修复配置错误
  80. /// </summary>
  81. public bool FixConfigError()
  82. {
  83. bool isFixed = false;
  84. if (string.IsNullOrEmpty(CollectorGUID) == false)
  85. {
  86. string convertAssetPath = AssetDatabase.GUIDToAssetPath(CollectorGUID);
  87. if (string.IsNullOrEmpty(convertAssetPath))
  88. {
  89. Debug.LogWarning($"Collector GUID {CollectorGUID} is invalid and has been auto removed !");
  90. CollectorGUID = string.Empty;
  91. isFixed = true;
  92. }
  93. else
  94. {
  95. if (CollectPath != convertAssetPath)
  96. {
  97. CollectPath = convertAssetPath;
  98. isFixed = true;
  99. Debug.LogWarning($"Fix collect path : {CollectPath} -> {convertAssetPath}");
  100. }
  101. }
  102. }
  103. /*
  104. string convertGUID = AssetDatabase.AssetPathToGUID(CollectPath);
  105. if(string.IsNullOrEmpty(convertGUID) == false)
  106. {
  107. CollectorGUID = convertGUID;
  108. }
  109. */
  110. return isFixed;
  111. }
  112. /// <summary>
  113. /// 获取打包收集的资源文件
  114. /// </summary>
  115. public List<CollectAssetInfo> GetAllCollectAssets(CollectCommand command, AssetBundleCollectorGroup group)
  116. {
  117. // 注意:模拟构建模式下只收集主资源
  118. if (command.BuildMode == EBuildMode.SimulateBuild)
  119. {
  120. if (CollectorType != ECollectorType.MainAssetCollector)
  121. return new List<CollectAssetInfo>();
  122. }
  123. Dictionary<string, CollectAssetInfo> result = new Dictionary<string, CollectAssetInfo>(1000);
  124. // 检测是否为原生资源打包规则
  125. IPackRule packRuleInstance = AssetBundleCollectorSettingData.GetPackRuleInstance(PackRuleName);
  126. bool isRawFilePackRule = packRuleInstance.IsRawFilePackRule();
  127. // 检测原生资源包的收集器类型
  128. if (isRawFilePackRule && CollectorType != ECollectorType.MainAssetCollector)
  129. throw new Exception($"The raw file pack rule must be set to {nameof(ECollectorType)}.{ECollectorType.MainAssetCollector} : {CollectPath}");
  130. if (string.IsNullOrEmpty(CollectPath))
  131. throw new Exception($"The collect path is null or empty in group : {group.GroupName}");
  132. // 收集打包资源
  133. if (AssetDatabase.IsValidFolder(CollectPath))
  134. {
  135. string collectDirectory = CollectPath;
  136. string[] findAssets = EditorTools.FindAssets(EAssetSearchType.All, collectDirectory);
  137. foreach (string assetPath in findAssets)
  138. {
  139. if (IsValidateAsset(assetPath, isRawFilePackRule) && IsCollectAsset(assetPath))
  140. {
  141. if (result.ContainsKey(assetPath) == false)
  142. {
  143. var collectAssetInfo = CreateCollectAssetInfo(command, group, assetPath, isRawFilePackRule);
  144. result.Add(assetPath, collectAssetInfo);
  145. }
  146. else
  147. {
  148. throw new Exception($"The collecting asset file is existed : {assetPath} in collector : {CollectPath}");
  149. }
  150. }
  151. }
  152. }
  153. else
  154. {
  155. string assetPath = CollectPath;
  156. if (IsValidateAsset(assetPath, isRawFilePackRule) && IsCollectAsset(assetPath))
  157. {
  158. var collectAssetInfo = CreateCollectAssetInfo(command, group, assetPath, isRawFilePackRule);
  159. result.Add(assetPath, collectAssetInfo);
  160. }
  161. else
  162. {
  163. throw new Exception($"The collecting single asset file is invalid : {assetPath} in collector : {CollectPath}");
  164. }
  165. }
  166. // 检测可寻址地址是否重复
  167. if (command.EnableAddressable)
  168. {
  169. var addressTemper = new Dictionary<string, string>();
  170. foreach (var collectInfoPair in result)
  171. {
  172. if (collectInfoPair.Value.CollectorType == ECollectorType.MainAssetCollector)
  173. {
  174. string address = collectInfoPair.Value.Address;
  175. string assetPath = collectInfoPair.Value.AssetPath;
  176. if (address.StartsWith("Assets/") || address.StartsWith("assets/"))
  177. throw new Exception($"The address can not set asset path in collector : {CollectPath} \nAssetPath: {assetPath}");
  178. if (addressTemper.TryGetValue(address, out var existed) == false)
  179. addressTemper.Add(address, assetPath);
  180. else
  181. throw new Exception($"The address is existed : {address} in collector : {CollectPath} \nAssetPath:\n {existed}\n {assetPath}");
  182. }
  183. }
  184. }
  185. // 返回列表
  186. return result.Values.ToList();
  187. }
  188. private CollectAssetInfo CreateCollectAssetInfo(CollectCommand command, AssetBundleCollectorGroup group, string assetPath, bool isRawFilePackRule)
  189. {
  190. string address = GetAddress(command, group, assetPath);
  191. string bundleName = GetBundleName(command, group, assetPath);
  192. List<string> assetTags = GetAssetTags(group);
  193. CollectAssetInfo collectAssetInfo = new CollectAssetInfo(CollectorType, bundleName, address, assetPath, isRawFilePackRule, assetTags);
  194. // 注意:模拟构建模式下不需要收集依赖资源
  195. if (command.BuildMode == EBuildMode.SimulateBuild)
  196. collectAssetInfo.DependAssets = new List<string>();
  197. else
  198. collectAssetInfo.DependAssets = GetAllDependencies(assetPath);
  199. return collectAssetInfo;
  200. }
  201. private bool IsValidateAsset(string assetPath, bool isRawFilePackRule)
  202. {
  203. if (assetPath.StartsWith("Assets/") == false && assetPath.StartsWith("Packages/") == false)
  204. {
  205. UnityEngine.Debug.LogError($"Invalid asset path : {assetPath}");
  206. return false;
  207. }
  208. // 忽略文件夹
  209. if (AssetDatabase.IsValidFolder(assetPath))
  210. return false;
  211. // 忽略编辑器下的类型资源
  212. Type assetType = AssetDatabase.GetMainAssetTypeAtPath(assetPath);
  213. if (assetType == typeof(LightingDataAsset))
  214. return false;
  215. // 检测原生文件是否合规
  216. if (isRawFilePackRule)
  217. {
  218. string extension = EditorTools.RemoveFirstChar(System.IO.Path.GetExtension(assetPath));
  219. if (extension == EAssetFileExtension.unity.ToString() || extension == EAssetFileExtension.prefab.ToString() ||
  220. extension == EAssetFileExtension.fbx.ToString() || extension == EAssetFileExtension.mat.ToString() ||
  221. extension == EAssetFileExtension.controller.ToString() || extension == EAssetFileExtension.anim.ToString() ||
  222. extension == EAssetFileExtension.ttf.ToString() || extension == EAssetFileExtension.shader.ToString())
  223. {
  224. UnityEngine.Debug.LogWarning($"Raw file pack rule can not support file estension : {extension}");
  225. return false;
  226. }
  227. // 注意:原生文件只支持无依赖关系的资源
  228. /*
  229. string[] depends = AssetDatabase.GetDependencies(assetPath, true);
  230. if (depends.Length != 1)
  231. {
  232. UnityEngine.Debug.LogWarning($"Raw file pack rule can not support estension : {extension}");
  233. return false;
  234. }
  235. */
  236. }
  237. else
  238. {
  239. // 忽略Unity无法识别的无效文件
  240. // 注意:只对非原生文件收集器处理
  241. if (assetType == typeof(UnityEditor.DefaultAsset))
  242. {
  243. UnityEngine.Debug.LogWarning($"Cannot pack default asset : {assetPath}");
  244. return false;
  245. }
  246. }
  247. string fileExtension = System.IO.Path.GetExtension(assetPath);
  248. if (DefaultFilterRule.IsIgnoreFile(fileExtension))
  249. return false;
  250. return true;
  251. }
  252. private bool IsCollectAsset(string assetPath)
  253. {
  254. // 根据规则设置过滤资源文件
  255. IFilterRule filterRuleInstance = AssetBundleCollectorSettingData.GetFilterRuleInstance(FilterRuleName);
  256. return filterRuleInstance.IsCollectAsset(new FilterRuleData(assetPath));
  257. }
  258. private string GetAddress(CollectCommand command, AssetBundleCollectorGroup group, string assetPath)
  259. {
  260. if (command.EnableAddressable == false)
  261. return string.Empty;
  262. if (CollectorType != ECollectorType.MainAssetCollector)
  263. return string.Empty;
  264. IAddressRule addressRuleInstance = AssetBundleCollectorSettingData.GetAddressRuleInstance(AddressRuleName);
  265. string adressValue = addressRuleInstance.GetAssetAddress(new AddressRuleData(assetPath, CollectPath, group.GroupName, UserData));
  266. return adressValue;
  267. }
  268. private string GetBundleName(CollectCommand command, AssetBundleCollectorGroup group, string assetPath)
  269. {
  270. System.Type assetType = AssetDatabase.GetMainAssetTypeAtPath(assetPath);
  271. if (assetType == typeof(UnityEngine.Shader) || assetType == typeof(UnityEngine.ShaderVariantCollection))
  272. {
  273. // 获取着色器打包规则结果
  274. PackRuleResult packRuleResult = DefaultPackRule.CreateShadersPackRuleResult();
  275. return packRuleResult.GetMainBundleName(command.PackageName, command.UniqueBundleName);
  276. }
  277. else
  278. {
  279. // 获取其它资源打包规则结果
  280. IPackRule packRuleInstance = AssetBundleCollectorSettingData.GetPackRuleInstance(PackRuleName);
  281. PackRuleResult packRuleResult = packRuleInstance.GetPackRuleResult(new PackRuleData(assetPath, CollectPath, group.GroupName, UserData));
  282. return packRuleResult.GetMainBundleName(command.PackageName, command.UniqueBundleName);
  283. }
  284. }
  285. private List<string> GetAssetTags(AssetBundleCollectorGroup group)
  286. {
  287. List<string> tags = EditorTools.StringToStringList(group.AssetTags, ';');
  288. List<string> temper = EditorTools.StringToStringList(AssetTags, ';');
  289. tags.AddRange(temper);
  290. return tags;
  291. }
  292. private List<string> GetAllDependencies(string mainAssetPath)
  293. {
  294. string[] depends = AssetDatabase.GetDependencies(mainAssetPath, true);
  295. List<string> result = new List<string>(depends.Length);
  296. foreach (string assetPath in depends)
  297. {
  298. // 注意:排除主资源对象
  299. if (assetPath == mainAssetPath)
  300. continue;
  301. if (IsValidateAsset(assetPath, false))
  302. {
  303. result.Add(assetPath);
  304. }
  305. }
  306. return result;
  307. }
  308. }
  309. }