BundleContext.cs 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359
  1. using XGame.Editor.Asset;
  2. using XGame.Framework.Asset.Addressable.Data;
  3. using System;
  4. using System.Collections.Generic;
  5. using System.Linq;
  6. using UnityEditor;
  7. using UnityEngine;
  8. using XGame.Framework.i18n;
  9. namespace XGame.Editor.Build.AssetBundles
  10. {
  11. /// <summary>
  12. /// assbundle的上下文
  13. /// </summary>
  14. public class BundleContext
  15. {
  16. public readonly string ResStaticDir = $"/{PathDefine.ResStaticName}/";
  17. public readonly string UiPrefabDir = "/UI/Prefabs/";
  18. public readonly string EditorDir = "/Editor/";
  19. public readonly string ResourcesDir = "/Resources/";
  20. public readonly string PrefabExt = ".prefab";
  21. public readonly string ShaderExt = ".shader";
  22. /// <summary>
  23. /// key:guid
  24. /// value:AssetInfo
  25. /// </summary>
  26. private Dictionary<string, AssetInfo> _assetInfoMap;
  27. /// <summary>
  28. /// key:assetPath
  29. /// value:guid
  30. /// </summary>
  31. private Dictionary<string, string> _assetPathToGuidMap;
  32. public Dictionary<long, AssetBundleData> bundleDataMap;
  33. /// <summary>
  34. /// key:bundleId
  35. /// value:dependencies
  36. /// </summary>
  37. private Dictionary<long, string[]> _bundleDependenciesMap;
  38. public HashSet<string> bundleAssetPaths;
  39. public Dictionary<long, uint[]> dependenciesMap;
  40. #region Addressable资源
  41. public string[] assetsRoots;
  42. /// <summary>
  43. /// 等待处理的资源
  44. /// </summary>
  45. public HashSet<string> waittingAssetPaths;
  46. #endregion
  47. /// <summary>
  48. /// 贴图资源没有依赖,缓存一份贴图路径数据节省一些判断
  49. /// </summary>
  50. public HashSet<string> allTexturePaths;
  51. public AssetBundleNameMode bundleNameMode;
  52. /// <summary>
  53. /// 是否合并shader
  54. /// </summary>
  55. public bool isMergeShader;
  56. /// <summary>
  57. /// ab分组名字
  58. /// key:assetPath
  59. /// value:groupName
  60. /// </summary>
  61. public Dictionary<string, string> groupNameMap;
  62. /// <summary>
  63. /// 排序好的AssetBundle数据
  64. /// </summary>
  65. private List<AssetBundleData> _bundleDataLst;
  66. /// <summary>
  67. /// 排序好的AssetBundle数据
  68. /// </summary>
  69. public List<AssetBundleData> BundleDataLst => _bundleDataLst;
  70. public BundleContext(Dictionary<string, AssetInfo> assetInfoMap)
  71. {
  72. _assetInfoMap = assetInfoMap;
  73. bundleDataMap = new Dictionary<long, AssetBundleData>();
  74. _bundleDependenciesMap = new Dictionary<long, string[]>();
  75. bundleAssetPaths = new HashSet<string>();
  76. dependenciesMap = new Dictionary<long, uint[]>();
  77. waittingAssetPaths = new HashSet<string>();
  78. allTexturePaths = new HashSet<string>();
  79. groupNameMap = new Dictionary<string, string>();
  80. Init();
  81. }
  82. private void Init()
  83. {
  84. _assetPathToGuidMap = new Dictionary<string, string>();
  85. foreach (var guid in _assetInfoMap.Keys)
  86. {
  87. var assetPath = AssetDatabase.GUIDToAssetPath(guid);
  88. if (!string.IsNullOrEmpty(assetPath))
  89. {
  90. _assetPathToGuidMap.Add(assetPath, guid);
  91. }
  92. }
  93. }
  94. #region addressable
  95. public bool IsAddressableAsset(string guid)
  96. {
  97. return _assetInfoMap.ContainsKey(guid);
  98. }
  99. public bool TryGetAddressableNameByGuid(string guid, out string addressableName)
  100. {
  101. if (_assetInfoMap.TryGetValue(guid, out var info))
  102. {
  103. addressableName = info.addressableName;
  104. return true;
  105. }
  106. addressableName = string.Empty;
  107. return false;
  108. }
  109. public bool TryGetAddressableNameByPath(string assetPath, out string addressableName)
  110. {
  111. if (_assetPathToGuidMap.TryGetValue(assetPath, out var guid))
  112. {
  113. addressableName = _assetInfoMap[guid].addressableName;
  114. return true;
  115. }
  116. addressableName = string.Empty;
  117. return false;
  118. }
  119. /// <summary>
  120. /// key:assetPath
  121. /// value:addressableName
  122. /// </summary>
  123. /// <returns></returns>
  124. public Dictionary<string, string> GetAddressableInfoMap()
  125. {
  126. var map = new Dictionary<string, string>();
  127. foreach(var item in _assetPathToGuidMap)
  128. {
  129. var assetInfo = _assetInfoMap[item.Value];
  130. if (!string.IsNullOrEmpty(assetInfo.relativePath))
  131. {
  132. //忽略Resources的资源
  133. continue;
  134. }
  135. map.Add(item.Key, assetInfo.addressableName);
  136. }
  137. return map;
  138. }
  139. /// <summary>
  140. /// 资源路径是否有效
  141. /// </summary>
  142. /// <param name="assetPath"></param>
  143. /// <returns></returns>
  144. public bool IsValidPath(string assetPath)
  145. {
  146. if (string.IsNullOrEmpty(assetPath))
  147. {
  148. return false;
  149. }
  150. foreach (var root in assetsRoots)
  151. {
  152. if (assetPath.StartsWith(root))
  153. {
  154. return true;
  155. }
  156. }
  157. return false;
  158. }
  159. #endregion
  160. /// <summary>
  161. /// 支持addressable加载
  162. /// 多语言的单独分文件夹
  163. /// 根据分组配置划分文件夹
  164. /// originGuid 用于生成bundleId,一般使用guid
  165. /// 默认为资源所属文件夹、图集为*.spriteatlas、单资源打包则为资源自己的guid
  166. /// </summary>
  167. /// <param name="assetPaths"></param>
  168. /// <param name="originGuid"></param>
  169. public void AddAddressableBundle(string[] assetPaths, string originGuid)
  170. {
  171. string groupName;
  172. var firstAsset = assetPaths[0];
  173. var langFlag = AddressableHelper.GetLanguageTypeByAssetPath(firstAsset);
  174. if (langFlag == LanguageType.NONE)
  175. {
  176. // 取Group配置
  177. groupNameMap.TryGetValue(firstAsset, out groupName);
  178. }
  179. else
  180. {
  181. groupName = langFlag.ToString().ToLower();
  182. }
  183. var originName = bundleNameMode == AssetBundleNameMode.Guid ? originGuid : AssetDatabase.GUIDToAssetPath(originGuid);
  184. var bundleId = Crc32.GetCrc32(originName);
  185. var count = assetPaths.Length;
  186. var addressableNames = new string[count];
  187. var isTextureBundle = true;
  188. for (var i = 0; i < count; i++)
  189. {
  190. var assetPath = assetPaths[i];
  191. if (isTextureBundle && allTexturePaths.Contains(assetPath) == false)
  192. { // 有一个资源非贴图
  193. isTextureBundle = false;
  194. }
  195. if (TryGetAddressableNameByPath(assetPath, out var addressableName) == false)
  196. {
  197. Debug.LogWarning($"Asset can't find addressableName. Path:{assetPath}");
  198. addressableName = assetPath;
  199. }
  200. addressableNames[i] = addressableName;
  201. // 移除等待
  202. waittingAssetPaths.Remove(assetPath);
  203. }
  204. var bundle = new AssetBundleData()
  205. {
  206. assetBundleName = string.IsNullOrEmpty(groupName) ? bundleId.ToString() : $"{groupName}/{bundleId}",
  207. assetBundleVariant = Framework.Asset.Define.BUNDLE_VARIANT,
  208. assetNames = assetPaths,
  209. addressableNames = addressableNames,
  210. bundleId = bundleId,
  211. originBundleName = originName,
  212. isTextureBundle = isTextureBundle,
  213. bundleType = AssetBundleType.Addressable
  214. };
  215. TryAddAssetBundle(ref bundle);
  216. }
  217. /// <summary>
  218. /// 普通的ab包,不支持addressable加载
  219. /// 全部生成在根目录下,不支持分组
  220. /// originName 用于生成bundleId,一般使用guid
  221. /// 默认为资源所属文件夹、图集为*.spriteatlas、单资源打包则为资源自己的guid
  222. /// </summary>
  223. /// <param name="assetPaths"></param>
  224. /// <param name="originName"></param>
  225. /// <param name="isDependency"></param>
  226. /// <returns></returns>
  227. public AssetBundleData AddRawAssetBundle(string[] assetPaths, string originName, bool isDependency)
  228. {
  229. if (bundleNameMode == AssetBundleNameMode.AssetPath && isDependency == false)
  230. {
  231. var path = AssetDatabase.GUIDToAssetPath(originName);
  232. if (string.IsNullOrEmpty(path))
  233. {
  234. Log.Error($"AddRawAssetBundle originName is not guid. originName:{originName}");
  235. }
  236. else
  237. {
  238. originName = path;
  239. }
  240. }
  241. var bundleId = Crc32.GetCrc32(originName);
  242. var bundle = new AssetBundleData()
  243. {
  244. assetBundleName = bundleId.ToString(),
  245. assetBundleVariant = Framework.Asset.Define.BUNDLE_VARIANT,
  246. assetNames = assetPaths,
  247. addressableNames = assetPaths,
  248. bundleId = bundleId,
  249. originBundleName = originName,
  250. isTextureBundle = IsTexturePaths(assetPaths),
  251. bundleType = isDependency ? AssetBundleType.Dependency : AssetBundleType.Raw
  252. };
  253. TryAddAssetBundle(ref bundle);
  254. return bundle;
  255. }
  256. private bool IsTexturePaths(string[] assetPaths)
  257. {
  258. foreach(var assetPath in assetPaths)
  259. {
  260. if (allTexturePaths.Contains(assetPath) == false)
  261. {
  262. return false;
  263. }
  264. }
  265. return true;
  266. }
  267. private void TryAddAssetBundle(ref AssetBundleData bundle)
  268. {
  269. var bundleId = bundle.bundleId;
  270. bundleAssetPaths.UnionWith(bundle.assetNames);
  271. if (bundleDataMap.ContainsKey(bundleId))
  272. {
  273. var lastName = bundleDataMap[bundleId].originBundleName;
  274. var nextName = bundle.originBundleName;
  275. Debug.LogError($"[XBuild] AssetBundleId重复. Id:{bundleId} LastName:{lastName} LastPath:{AssetDatabase.GUIDToAssetPath(lastName)} NextName:{nextName} NextPath:{AssetDatabase.GUIDToAssetPath(nextName)}");
  276. return;
  277. }
  278. bundleDataMap.Add(bundleId, bundle);
  279. }
  280. private List<string> tempLst = new List<string>();
  281. public string[] ToArrayBySort(HashSet<string> assetPaths)
  282. {
  283. if (assetPaths.Count > 1)
  284. {
  285. tempLst.AddRange(assetPaths);
  286. tempLst.Sort((a, b) => string.Compare(a, b, StringComparison.OrdinalIgnoreCase));
  287. var array = tempLst.ToArray();
  288. tempLst.Clear();
  289. return array;
  290. }
  291. return assetPaths.ToArray();
  292. }
  293. /// <summary>
  294. /// 将所有AssetBundleData转成List并排序
  295. /// </summary>
  296. public List<AssetBundleData> SortAssetBundleDatas()
  297. {
  298. _bundleDataLst = bundleDataMap.Values.ToList();
  299. _bundleDataLst.Sort(CompareAssetBundle);
  300. return _bundleDataLst;
  301. }
  302. public int CompareAssetBundle(AssetBundleData bundleA, AssetBundleData bundleB)
  303. {
  304. if (bundleNameMode == AssetBundleNameMode.AssetPath)
  305. {
  306. var guidA = AssetDatabase.AssetPathToGUID(bundleA.originBundleName);
  307. var guidB = AssetDatabase.AssetPathToGUID(bundleB.originBundleName);
  308. //有assetPath的bundle排前面
  309. var idx_0 = string.IsNullOrEmpty(guidA) == false ? -1 : 1;
  310. var idx_1 = string.IsNullOrEmpty(guidB) == false ? -1 : 1;
  311. var remain = idx_0 - idx_1;
  312. if (remain != 0)
  313. {
  314. return remain;
  315. }
  316. }
  317. else
  318. {
  319. var pathA = AssetDatabase.GUIDToAssetPath(bundleA.originBundleName);
  320. var pathB = AssetDatabase.GUIDToAssetPath(bundleB.originBundleName);
  321. //有assetPath的bundle排前面
  322. var idx_0 = string.IsNullOrEmpty(pathA) == false ? -1 : 1;
  323. var idx_1 = string.IsNullOrEmpty(pathB) == false ? -1 : 1;
  324. var remain = idx_0 - idx_1;
  325. if (remain != 0)
  326. {
  327. return remain;
  328. }
  329. if (idx_0 == -1)
  330. {
  331. return string.Compare(pathA, pathB, StringComparison.OrdinalIgnoreCase);
  332. }
  333. }
  334. return string.Compare(bundleA.originBundleName, bundleB.originBundleName, StringComparison.OrdinalIgnoreCase);
  335. }
  336. public System.Diagnostics.Stopwatch bundleDependenciesSw = new System.Diagnostics.Stopwatch();
  337. public string[] GetDependencies(uint bundleId, string[] assetNames)
  338. {
  339. if (_bundleDependenciesMap.TryGetValue(bundleId, out var dependencies) == false)
  340. {
  341. bundleDependenciesSw.Start();
  342. dependencies = AssetDatabase.GetDependencies(assetNames, true);
  343. bundleDependenciesSw.Stop();
  344. _bundleDependenciesMap.Add(bundleId, dependencies);
  345. }
  346. return dependencies;
  347. }
  348. }
  349. }