ResourcesComponent.cs 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435
  1. using System;
  2. using System.Collections.Generic;
  3. using System.IO;
  4. using System.Linq;
  5. using System.Text;
  6. using UnityEngine;
  7. #if UNITY_EDITOR
  8. using UnityEditor;
  9. #endif
  10. namespace ET
  11. {
  12. public class ABInfoAwakeSystem : AwakeSystem<ABInfo, string, AssetBundle>
  13. {
  14. public override void Awake(ABInfo self, string abName, AssetBundle a)
  15. {
  16. self.AssetBundle = a;
  17. self.Name = abName;
  18. self.RefCount = 1;
  19. }
  20. }
  21. public class ABInfo : Entity
  22. {
  23. public string Name { get; set; }
  24. public int RefCount { get; set; }
  25. public AssetBundle AssetBundle;
  26. public override void Dispose()
  27. {
  28. if (this.IsDisposed)
  29. {
  30. return;
  31. }
  32. base.Dispose();
  33. //Log.Debug($"desdroy assetbundle: {this.Name}");
  34. if (this.AssetBundle != null)
  35. {
  36. this.AssetBundle.Unload(true);
  37. }
  38. this.RefCount = 0;
  39. this.Name = "";
  40. }
  41. }
  42. // 用于字符串转换,减少GC
  43. public static class AssetBundleHelper
  44. {
  45. public static readonly Dictionary<int, string> IntToStringDict = new Dictionary<int, string>();
  46. public static readonly Dictionary<string, string> StringToABDict = new Dictionary<string, string>();
  47. public static readonly Dictionary<string, string> BundleNameToLowerDict = new Dictionary<string, string>()
  48. {
  49. { "StreamingAssets", "StreamingAssets" }
  50. };
  51. // 缓存包依赖,不用每次计算
  52. public static Dictionary<string, string[]> DependenciesCache = new Dictionary<string, string[]>();
  53. public static string IntToString(this int value)
  54. {
  55. string result;
  56. if (IntToStringDict.TryGetValue(value, out result))
  57. {
  58. return result;
  59. }
  60. result = value.ToString();
  61. IntToStringDict[value] = result;
  62. return result;
  63. }
  64. public static string StringToAB(this string value)
  65. {
  66. string result;
  67. if (StringToABDict.TryGetValue(value, out result))
  68. {
  69. return result;
  70. }
  71. result = value + ".unity3d";
  72. StringToABDict[value] = result;
  73. return result;
  74. }
  75. public static string IntToAB(this int value)
  76. {
  77. return value.IntToString().StringToAB();
  78. }
  79. public static string BundleNameToLower(this string value)
  80. {
  81. string result;
  82. if (BundleNameToLowerDict.TryGetValue(value, out result))
  83. {
  84. return result;
  85. }
  86. result = value.ToLower();
  87. BundleNameToLowerDict[value] = result;
  88. return result;
  89. }
  90. public static string[] GetDependencies(string assetBundleName)
  91. {
  92. string[] dependencies = new string[0];
  93. if (DependenciesCache.TryGetValue(assetBundleName,out dependencies))
  94. {
  95. return dependencies;
  96. }
  97. if (!Define.IsAsync)
  98. {
  99. #if UNITY_EDITOR
  100. dependencies = AssetDatabase.GetAssetBundleDependencies(assetBundleName, true);
  101. #endif
  102. }
  103. else
  104. {
  105. dependencies = ResourcesComponent.AssetBundleManifestObject.GetAllDependencies(assetBundleName);
  106. }
  107. DependenciesCache.Add(assetBundleName, dependencies);
  108. return dependencies;
  109. }
  110. public static string[] GetSortedDependencies(string assetBundleName)
  111. {
  112. Dictionary<string, int> info = new Dictionary<string, int>();
  113. List<string> parents = new List<string>();
  114. CollectDependencies(parents, assetBundleName, info);
  115. string[] ss = info.OrderBy(x => x.Value).Select(x => x.Key).ToArray();
  116. return ss;
  117. }
  118. public static void CollectDependencies(List<string> parents, string assetBundleName, Dictionary<string, int> info)
  119. {
  120. parents.Add(assetBundleName);
  121. string[] deps = GetDependencies(assetBundleName);
  122. foreach (string parent in parents)
  123. {
  124. if (!info.ContainsKey(parent))
  125. {
  126. info[parent] = 0;
  127. }
  128. info[parent] += deps.Length;
  129. }
  130. foreach (string dep in deps)
  131. {
  132. if (parents.Contains(dep))
  133. {
  134. throw new Exception($"包有循环依赖,请重新标记: {assetBundleName} {dep}");
  135. }
  136. CollectDependencies(parents, dep, info);
  137. }
  138. parents.RemoveAt(parents.Count - 1);
  139. }
  140. }
  141. public class ResourcesComponent : Entity
  142. {
  143. public static AssetBundleManifest AssetBundleManifestObject { get; set; }
  144. private readonly Dictionary<string, Dictionary<string, UnityEngine.Object>> resourceCache = new Dictionary<string, Dictionary<string, UnityEngine.Object>>();
  145. private readonly Dictionary<string, ABInfo> bundles = new Dictionary<string, ABInfo>();
  146. public override void Dispose()
  147. {
  148. if (this.IsDisposed)
  149. {
  150. return;
  151. }
  152. base.Dispose();
  153. foreach (var abInfo in this.bundles)
  154. {
  155. abInfo.Value.Dispose();
  156. }
  157. this.bundles.Clear();
  158. this.resourceCache.Clear();
  159. }
  160. public UnityEngine.Object GetAsset(string bundleName, string prefab)
  161. {
  162. Dictionary<string, UnityEngine.Object> dict;
  163. if (!this.resourceCache.TryGetValue(bundleName.BundleNameToLower(), out dict))
  164. {
  165. throw new Exception($"not found asset: {bundleName} {prefab}");
  166. }
  167. UnityEngine.Object resource = null;
  168. if (!dict.TryGetValue(prefab, out resource))
  169. {
  170. throw new Exception($"not found asset: {bundleName} {prefab}");
  171. }
  172. return resource;
  173. }
  174. public void UnloadBundle(string assetBundleName)
  175. {
  176. assetBundleName = assetBundleName.BundleNameToLower();
  177. string[] dependencies = AssetBundleHelper.GetSortedDependencies(assetBundleName);
  178. //Log.Debug($"-----------dep unload {assetBundleName} dep: {dependencies.ToList().ListToString()}");
  179. foreach (string dependency in dependencies)
  180. {
  181. this.UnloadOneBundle(dependency);
  182. }
  183. }
  184. private void UnloadOneBundle(string assetBundleName)
  185. {
  186. assetBundleName = assetBundleName.BundleNameToLower();
  187. ABInfo abInfo;
  188. if (!this.bundles.TryGetValue(assetBundleName, out abInfo))
  189. {
  190. throw new Exception($"not found assetBundle: {assetBundleName}");
  191. }
  192. //Log.Debug($"---------- unload one bundle {assetBundleName} refcount: {abInfo.RefCount - 1}");
  193. --abInfo.RefCount;
  194. if (abInfo.RefCount > 0)
  195. {
  196. return;
  197. }
  198. this.bundles.Remove(assetBundleName);
  199. this.resourceCache.Remove(assetBundleName);
  200. abInfo.Dispose();
  201. //Log.Debug($"cache count: {this.cacheDictionary.Count}");
  202. }
  203. /// <summary>
  204. /// 同步加载assetbundle
  205. /// </summary>
  206. /// <param name="assetBundleName"></param>
  207. /// <returns></returns>
  208. public void LoadBundle(string assetBundleName)
  209. {
  210. assetBundleName = assetBundleName.ToLower();
  211. string[] dependencies = AssetBundleHelper.GetSortedDependencies(assetBundleName);
  212. //Log.Debug($"-----------dep load {assetBundleName} dep: {dependencies.ToList().ListToString()}");
  213. foreach (string dependency in dependencies)
  214. {
  215. if (string.IsNullOrEmpty(dependency))
  216. {
  217. continue;
  218. }
  219. this.LoadOneBundle(dependency);
  220. }
  221. }
  222. public void AddResource(string bundleName, string assetName, UnityEngine.Object resource)
  223. {
  224. Dictionary<string, UnityEngine.Object> dict;
  225. if (!this.resourceCache.TryGetValue(bundleName.BundleNameToLower(), out dict))
  226. {
  227. dict = new Dictionary<string, UnityEngine.Object>();
  228. this.resourceCache[bundleName] = dict;
  229. }
  230. dict[assetName] = resource;
  231. }
  232. public void LoadOneBundle(string assetBundleName)
  233. {
  234. //Log.Debug($"---------------load one bundle {assetBundleName}");
  235. ABInfo abInfo;
  236. if (this.bundles.TryGetValue(assetBundleName, out abInfo))
  237. {
  238. ++abInfo.RefCount;
  239. return;
  240. }
  241. if (!Define.IsAsync)
  242. {
  243. string[] realPath = null;
  244. #if UNITY_EDITOR
  245. realPath = AssetDatabase.GetAssetPathsFromAssetBundle(assetBundleName);
  246. foreach (string s in realPath)
  247. {
  248. string assetName = Path.GetFileNameWithoutExtension(s);
  249. UnityEngine.Object resource = AssetDatabase.LoadAssetAtPath<UnityEngine.Object>(s);
  250. AddResource(assetBundleName, assetName, resource);
  251. }
  252. abInfo = EntityFactory.CreateWithParent<ABInfo, string, AssetBundle>(this, assetBundleName, null);
  253. this.bundles[assetBundleName] = abInfo;
  254. #endif
  255. return;
  256. }
  257. string p = Path.Combine(PathHelper.AppHotfixResPath, assetBundleName);
  258. AssetBundle assetBundle = null;
  259. if (File.Exists(p))
  260. {
  261. assetBundle = AssetBundle.LoadFromFile(p);
  262. }
  263. else
  264. {
  265. p = Path.Combine(PathHelper.AppResPath, assetBundleName);
  266. assetBundle = AssetBundle.LoadFromFile(p);
  267. }
  268. if (assetBundle == null)
  269. {
  270. throw new Exception($"assets bundle not found: {assetBundleName}");
  271. }
  272. if (!assetBundle.isStreamedSceneAssetBundle)
  273. {
  274. // 异步load资源到内存cache住
  275. UnityEngine.Object[] assets = assetBundle.LoadAllAssets();
  276. foreach (UnityEngine.Object asset in assets)
  277. {
  278. AddResource(assetBundleName, asset.name, asset);
  279. }
  280. }
  281. abInfo = EntityFactory.CreateWithParent<ABInfo, string, AssetBundle>(this, assetBundleName, assetBundle);
  282. this.bundles[assetBundleName] = abInfo;
  283. }
  284. /// <summary>
  285. /// 异步加载assetbundle
  286. /// </summary>
  287. /// <param name="assetBundleName"></param>
  288. /// <returns></returns>
  289. public async ETTask LoadBundleAsync(string assetBundleName)
  290. {
  291. assetBundleName = assetBundleName.ToLower();
  292. string[] dependencies = AssetBundleHelper.GetSortedDependencies(assetBundleName);
  293. // Log.Debug($"-----------dep load {assetBundleName} dep: {dependencies.ToList().ListToString()}");
  294. foreach (string dependency in dependencies)
  295. {
  296. if (string.IsNullOrEmpty(dependency))
  297. {
  298. continue;
  299. }
  300. await this.LoadOneBundleAsync(dependency);
  301. }
  302. }
  303. public async ETTask LoadOneBundleAsync(string assetBundleName)
  304. {
  305. ABInfo abInfo;
  306. if (this.bundles.TryGetValue(assetBundleName, out abInfo))
  307. {
  308. ++abInfo.RefCount;
  309. return;
  310. }
  311. //Log.Debug($"---------------load one bundle {assetBundleName}");
  312. if (!Define.IsAsync)
  313. {
  314. string[] realPath = null;
  315. #if UNITY_EDITOR
  316. realPath = AssetDatabase.GetAssetPathsFromAssetBundle(assetBundleName);
  317. foreach (string s in realPath)
  318. {
  319. string assetName = Path.GetFileNameWithoutExtension(s);
  320. UnityEngine.Object resource = AssetDatabase.LoadAssetAtPath<UnityEngine.Object>(s);
  321. AddResource(assetBundleName, assetName, resource);
  322. }
  323. abInfo = EntityFactory.CreateWithParent<ABInfo, string, AssetBundle>(this, assetBundleName, null);
  324. this.bundles[assetBundleName] = abInfo;
  325. #endif
  326. return;
  327. }
  328. string p = Path.Combine(PathHelper.AppHotfixResPath, assetBundleName);
  329. AssetBundle assetBundle = null;
  330. if (!File.Exists(p))
  331. {
  332. p = Path.Combine(PathHelper.AppResPath, assetBundleName);
  333. }
  334. using (AssetsBundleLoaderAsync assetsBundleLoaderAsync = EntityFactory.Create<AssetsBundleLoaderAsync>(this.Domain))
  335. {
  336. assetBundle = await assetsBundleLoaderAsync.LoadAsync(p);
  337. }
  338. if (assetBundle == null)
  339. {
  340. throw new Exception($"assets bundle not found: {assetBundleName}");
  341. }
  342. if (!assetBundle.isStreamedSceneAssetBundle)
  343. {
  344. // 异步load资源到内存cache住
  345. UnityEngine.Object[] assets;
  346. using (AssetsLoaderAsync assetsLoaderAsync = EntityFactory.Create<AssetsLoaderAsync, AssetBundle>(this.Domain, assetBundle))
  347. {
  348. assets = await assetsLoaderAsync.LoadAllAssetsAsync();
  349. }
  350. foreach (UnityEngine.Object asset in assets)
  351. {
  352. AddResource(assetBundleName, asset.name, asset);
  353. }
  354. }
  355. abInfo = EntityFactory.CreateWithParent<ABInfo, string, AssetBundle>(this, assetBundleName, assetBundle);
  356. this.bundles[assetBundleName] = abInfo;
  357. }
  358. public string DebugString()
  359. {
  360. StringBuilder sb = new StringBuilder();
  361. foreach (ABInfo abInfo in this.bundles.Values)
  362. {
  363. sb.Append($"{abInfo.Name}:{abInfo.RefCount}\n");
  364. }
  365. return sb.ToString();
  366. }
  367. }
  368. }