ResourcesComponent.cs 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445
  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 ResourcesComponentAwakeSystem: AwakeSystem<ResourcesComponent>
  142. {
  143. public override void Awake(ResourcesComponent self)
  144. {
  145. ResourcesComponent.Instance = self;
  146. }
  147. }
  148. public class ResourcesComponent : Entity
  149. {
  150. public static ResourcesComponent Instance;
  151. public static AssetBundleManifest AssetBundleManifestObject { get; set; }
  152. private readonly Dictionary<string, Dictionary<string, UnityEngine.Object>> resourceCache = new Dictionary<string, Dictionary<string, UnityEngine.Object>>();
  153. private readonly Dictionary<string, ABInfo> bundles = new Dictionary<string, ABInfo>();
  154. public override void Dispose()
  155. {
  156. if (this.IsDisposed)
  157. {
  158. return;
  159. }
  160. base.Dispose();
  161. foreach (var abInfo in this.bundles)
  162. {
  163. abInfo.Value.Dispose();
  164. }
  165. this.bundles.Clear();
  166. this.resourceCache.Clear();
  167. }
  168. public UnityEngine.Object GetAsset(string bundleName, string prefab)
  169. {
  170. Dictionary<string, UnityEngine.Object> dict;
  171. if (!this.resourceCache.TryGetValue(bundleName.BundleNameToLower(), out dict))
  172. {
  173. throw new Exception($"not found asset: {bundleName} {prefab}");
  174. }
  175. UnityEngine.Object resource = null;
  176. if (!dict.TryGetValue(prefab, out resource))
  177. {
  178. throw new Exception($"not found asset: {bundleName} {prefab}");
  179. }
  180. return resource;
  181. }
  182. public void UnloadBundle(string assetBundleName)
  183. {
  184. assetBundleName = assetBundleName.BundleNameToLower();
  185. string[] dependencies = AssetBundleHelper.GetSortedDependencies(assetBundleName);
  186. //Log.Debug($"-----------dep unload {assetBundleName} dep: {dependencies.ToList().ListToString()}");
  187. foreach (string dependency in dependencies)
  188. {
  189. this.UnloadOneBundle(dependency);
  190. }
  191. }
  192. private void UnloadOneBundle(string assetBundleName)
  193. {
  194. assetBundleName = assetBundleName.BundleNameToLower();
  195. ABInfo abInfo;
  196. if (!this.bundles.TryGetValue(assetBundleName, out abInfo))
  197. {
  198. throw new Exception($"not found assetBundle: {assetBundleName}");
  199. }
  200. //Log.Debug($"---------- unload one bundle {assetBundleName} refcount: {abInfo.RefCount - 1}");
  201. --abInfo.RefCount;
  202. if (abInfo.RefCount > 0)
  203. {
  204. return;
  205. }
  206. this.bundles.Remove(assetBundleName);
  207. this.resourceCache.Remove(assetBundleName);
  208. abInfo.Dispose();
  209. //Log.Debug($"cache count: {this.cacheDictionary.Count}");
  210. }
  211. /// <summary>
  212. /// 同步加载assetbundle
  213. /// </summary>
  214. /// <param name="assetBundleName"></param>
  215. /// <returns></returns>
  216. public void LoadBundle(string assetBundleName)
  217. {
  218. assetBundleName = assetBundleName.ToLower();
  219. string[] dependencies = AssetBundleHelper.GetSortedDependencies(assetBundleName);
  220. //Log.Debug($"-----------dep load {assetBundleName} dep: {dependencies.ToList().ListToString()}");
  221. foreach (string dependency in dependencies)
  222. {
  223. if (string.IsNullOrEmpty(dependency))
  224. {
  225. continue;
  226. }
  227. this.LoadOneBundle(dependency);
  228. }
  229. }
  230. public void AddResource(string bundleName, string assetName, UnityEngine.Object resource)
  231. {
  232. Dictionary<string, UnityEngine.Object> dict;
  233. if (!this.resourceCache.TryGetValue(bundleName.BundleNameToLower(), out dict))
  234. {
  235. dict = new Dictionary<string, UnityEngine.Object>();
  236. this.resourceCache[bundleName] = dict;
  237. }
  238. dict[assetName] = resource;
  239. }
  240. public void LoadOneBundle(string assetBundleName)
  241. {
  242. //Log.Debug($"---------------load one bundle {assetBundleName}");
  243. ABInfo abInfo;
  244. if (this.bundles.TryGetValue(assetBundleName, out abInfo))
  245. {
  246. ++abInfo.RefCount;
  247. return;
  248. }
  249. if (!Define.IsAsync)
  250. {
  251. string[] realPath = null;
  252. #if UNITY_EDITOR
  253. realPath = AssetDatabase.GetAssetPathsFromAssetBundle(assetBundleName);
  254. foreach (string s in realPath)
  255. {
  256. string assetName = Path.GetFileNameWithoutExtension(s);
  257. UnityEngine.Object resource = AssetDatabase.LoadAssetAtPath<UnityEngine.Object>(s);
  258. AddResource(assetBundleName, assetName, resource);
  259. }
  260. abInfo = EntityFactory.CreateWithParent<ABInfo, string, AssetBundle>(this, assetBundleName, null);
  261. this.bundles[assetBundleName] = abInfo;
  262. #endif
  263. return;
  264. }
  265. string p = Path.Combine(PathHelper.AppHotfixResPath, assetBundleName);
  266. AssetBundle assetBundle = null;
  267. if (File.Exists(p))
  268. {
  269. assetBundle = AssetBundle.LoadFromFile(p);
  270. }
  271. else
  272. {
  273. p = Path.Combine(PathHelper.AppResPath, assetBundleName);
  274. assetBundle = AssetBundle.LoadFromFile(p);
  275. }
  276. if (assetBundle == null)
  277. {
  278. throw new Exception($"assets bundle not found: {assetBundleName}");
  279. }
  280. if (!assetBundle.isStreamedSceneAssetBundle)
  281. {
  282. // 异步load资源到内存cache住
  283. UnityEngine.Object[] assets = assetBundle.LoadAllAssets();
  284. foreach (UnityEngine.Object asset in assets)
  285. {
  286. AddResource(assetBundleName, asset.name, asset);
  287. }
  288. }
  289. abInfo = EntityFactory.CreateWithParent<ABInfo, string, AssetBundle>(this, assetBundleName, assetBundle);
  290. this.bundles[assetBundleName] = abInfo;
  291. }
  292. /// <summary>
  293. /// 异步加载assetbundle
  294. /// </summary>
  295. /// <param name="assetBundleName"></param>
  296. /// <returns></returns>
  297. public async ETTask LoadBundleAsync(string assetBundleName)
  298. {
  299. assetBundleName = assetBundleName.ToLower();
  300. string[] dependencies = AssetBundleHelper.GetSortedDependencies(assetBundleName);
  301. // Log.Debug($"-----------dep load {assetBundleName} dep: {dependencies.ToList().ListToString()}");
  302. foreach (string dependency in dependencies)
  303. {
  304. if (string.IsNullOrEmpty(dependency))
  305. {
  306. continue;
  307. }
  308. await this.LoadOneBundleAsync(dependency);
  309. }
  310. }
  311. public async ETTask LoadOneBundleAsync(string assetBundleName)
  312. {
  313. ABInfo abInfo;
  314. if (this.bundles.TryGetValue(assetBundleName, out abInfo))
  315. {
  316. ++abInfo.RefCount;
  317. return;
  318. }
  319. //Log.Debug($"---------------load one bundle {assetBundleName}");
  320. if (!Define.IsAsync)
  321. {
  322. string[] realPath = null;
  323. #if UNITY_EDITOR
  324. realPath = AssetDatabase.GetAssetPathsFromAssetBundle(assetBundleName);
  325. foreach (string s in realPath)
  326. {
  327. string assetName = Path.GetFileNameWithoutExtension(s);
  328. UnityEngine.Object resource = AssetDatabase.LoadAssetAtPath<UnityEngine.Object>(s);
  329. AddResource(assetBundleName, assetName, resource);
  330. }
  331. abInfo = EntityFactory.CreateWithParent<ABInfo, string, AssetBundle>(this, assetBundleName, null);
  332. this.bundles[assetBundleName] = abInfo;
  333. #endif
  334. return;
  335. }
  336. string p = Path.Combine(PathHelper.AppHotfixResPath, assetBundleName);
  337. AssetBundle assetBundle = null;
  338. if (!File.Exists(p))
  339. {
  340. p = Path.Combine(PathHelper.AppResPath, assetBundleName);
  341. }
  342. using (AssetsBundleLoaderAsync assetsBundleLoaderAsync = EntityFactory.Create<AssetsBundleLoaderAsync>(this.Domain))
  343. {
  344. assetBundle = await assetsBundleLoaderAsync.LoadAsync(p);
  345. }
  346. if (assetBundle == null)
  347. {
  348. throw new Exception($"assets bundle not found: {assetBundleName}");
  349. }
  350. if (!assetBundle.isStreamedSceneAssetBundle)
  351. {
  352. // 异步load资源到内存cache住
  353. UnityEngine.Object[] assets;
  354. using (AssetsLoaderAsync assetsLoaderAsync = EntityFactory.Create<AssetsLoaderAsync, AssetBundle>(this.Domain, assetBundle))
  355. {
  356. assets = await assetsLoaderAsync.LoadAllAssetsAsync();
  357. }
  358. foreach (UnityEngine.Object asset in assets)
  359. {
  360. AddResource(assetBundleName, asset.name, asset);
  361. }
  362. }
  363. abInfo = EntityFactory.CreateWithParent<ABInfo, string, AssetBundle>(this, assetBundleName, assetBundle);
  364. this.bundles[assetBundleName] = abInfo;
  365. }
  366. public string DebugString()
  367. {
  368. StringBuilder sb = new StringBuilder();
  369. foreach (ABInfo abInfo in this.bundles.Values)
  370. {
  371. sb.Append($"{abInfo.Name}:{abInfo.RefCount}\n");
  372. }
  373. return sb.ToString();
  374. }
  375. }
  376. }