ResourcesComponent.cs 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347
  1. using System;
  2. using System.Collections.Generic;
  3. using System.IO;
  4. using System.Text;
  5. using System.Threading.Tasks;
  6. using UnityEngine;
  7. #if UNITY_EDITOR
  8. using UnityEditor;
  9. #endif
  10. namespace ETModel
  11. {
  12. public class ABInfo : Component
  13. {
  14. private int refCount;
  15. public string Name { get; }
  16. public int RefCount
  17. {
  18. get
  19. {
  20. return this.refCount;
  21. }
  22. set
  23. {
  24. //Log.Debug($"{this.Name} refcount: {value}");
  25. this.refCount = value;
  26. }
  27. }
  28. public AssetBundle AssetBundle { get; }
  29. public ABInfo(string name, AssetBundle ab)
  30. {
  31. this.Name = name;
  32. this.AssetBundle = ab;
  33. this.RefCount = 1;
  34. //Log.Debug($"load assetbundle: {this.Name}");
  35. }
  36. public override void Dispose()
  37. {
  38. if (this.IsDisposed)
  39. {
  40. return;
  41. }
  42. base.Dispose();
  43. //Log.Debug($"desdroy assetbundle: {this.Name}");
  44. this.AssetBundle?.Unload(true);
  45. }
  46. }
  47. public class ResourcesComponent : Component
  48. {
  49. public static AssetBundleManifest AssetBundleManifestObject { get; set; }
  50. private readonly Dictionary<string, UnityEngine.Object> resourceCache = new Dictionary<string, UnityEngine.Object>();
  51. private readonly Dictionary<string, ABInfo> bundles = new Dictionary<string, ABInfo>();
  52. // lru缓存队列
  53. private readonly QueueDictionary<string, ABInfo> cacheDictionary = new QueueDictionary<string, ABInfo>();
  54. public override void Dispose()
  55. {
  56. if (this.IsDisposed)
  57. {
  58. return;
  59. }
  60. base.Dispose();
  61. foreach (var abInfo in this.bundles)
  62. {
  63. abInfo.Value?.AssetBundle?.Unload(true);
  64. }
  65. while (cacheDictionary.Count > 0)
  66. {
  67. ABInfo abInfo = this.cacheDictionary.FirstValue;
  68. this.cacheDictionary.Dequeue();
  69. abInfo.AssetBundle?.Unload(true);
  70. }
  71. this.bundles.Clear();
  72. this.cacheDictionary.Clear();
  73. this.resourceCache.Clear();
  74. }
  75. public UnityEngine.Object GetAsset(string bundleName, string prefab)
  76. {
  77. string path = $"{bundleName}/{prefab}".ToLower();
  78. UnityEngine.Object resource = null;
  79. if (!this.resourceCache.TryGetValue(path, out resource))
  80. {
  81. throw new Exception($"not found asset: {path}");
  82. }
  83. return resource;
  84. }
  85. public void UnloadBundle(string assetBundleName)
  86. {
  87. assetBundleName = assetBundleName.ToLower();
  88. string[] dependencies = ResourcesHelper.GetSortedDependencies(assetBundleName);
  89. //Log.Debug($"-----------dep unload {assetBundleName} dep: {dependencies.ToList().ListToString()}");
  90. foreach (string dependency in dependencies)
  91. {
  92. this.UnloadOneBundle(dependency);
  93. }
  94. }
  95. private void UnloadOneBundle(string assetBundleName)
  96. {
  97. assetBundleName = assetBundleName.ToLower();
  98. ABInfo abInfo;
  99. if (!this.bundles.TryGetValue(assetBundleName, out abInfo))
  100. {
  101. throw new Exception($"not found assetBundle: {assetBundleName}");
  102. }
  103. //Log.Debug($"---------- unload one bundle {assetBundleName} refcount: {abInfo.RefCount}");
  104. --abInfo.RefCount;
  105. if (abInfo.RefCount > 0)
  106. {
  107. return;
  108. }
  109. this.bundles.Remove(assetBundleName);
  110. // 缓存10个包
  111. this.cacheDictionary.Enqueue(assetBundleName, abInfo);
  112. if (this.cacheDictionary.Count > 10)
  113. {
  114. abInfo = this.cacheDictionary[this.cacheDictionary.FirstKey];
  115. this.cacheDictionary.Dequeue();
  116. abInfo.Dispose();
  117. }
  118. //Log.Debug($"cache count: {this.cacheDictionary.Count}");
  119. }
  120. /// <summary>
  121. /// 同步加载assetbundle
  122. /// </summary>
  123. /// <param name="assetBundleName"></param>
  124. /// <returns></returns>
  125. public void LoadBundle(string assetBundleName)
  126. {
  127. assetBundleName = assetBundleName.ToLower();
  128. string[] dependencies = ResourcesHelper.GetSortedDependencies(assetBundleName);
  129. //Log.Debug($"-----------dep load {assetBundleName} dep: {dependencies.ToList().ListToString()}");
  130. foreach (string dependency in dependencies)
  131. {
  132. if (string.IsNullOrEmpty(dependency))
  133. {
  134. continue;
  135. }
  136. this.LoadOneBundle(dependency);
  137. }
  138. }
  139. public void LoadOneBundle(string assetBundleName)
  140. {
  141. //Log.Debug($"---------------load one bundle {assetBundleName}");
  142. ABInfo abInfo;
  143. if (this.bundles.TryGetValue(assetBundleName, out abInfo))
  144. {
  145. ++abInfo.RefCount;
  146. return;
  147. }
  148. if (this.cacheDictionary.ContainsKey(assetBundleName))
  149. {
  150. abInfo = this.cacheDictionary[assetBundleName];
  151. ++abInfo.RefCount;
  152. this.bundles[assetBundleName] = abInfo;
  153. this.cacheDictionary.Remove(assetBundleName);
  154. return;
  155. }
  156. if (!Define.IsAsync)
  157. {
  158. string[] realPath = null;
  159. #if UNITY_EDITOR
  160. realPath = AssetDatabase.GetAssetPathsFromAssetBundle(assetBundleName);
  161. foreach (string s in realPath)
  162. {
  163. string assetName = Path.GetFileNameWithoutExtension(s);
  164. string path = $"{assetBundleName}/{assetName}".ToLower();
  165. UnityEngine.Object resource = AssetDatabase.LoadAssetAtPath<UnityEngine.Object>(s);
  166. this.resourceCache[path] = resource;
  167. }
  168. this.bundles[assetBundleName] = new ABInfo(assetBundleName, null);
  169. #endif
  170. return;
  171. }
  172. string p = Path.Combine(PathHelper.AppHotfixResPath, assetBundleName);
  173. AssetBundle assetBundle = null;
  174. if (File.Exists(p))
  175. {
  176. assetBundle = AssetBundle.LoadFromFile(p);
  177. }
  178. else
  179. {
  180. p = Path.Combine(PathHelper.AppResPath, assetBundleName);
  181. assetBundle = AssetBundle.LoadFromFile(p);
  182. }
  183. if (assetBundle == null)
  184. {
  185. throw new Exception($"assets bundle not found: {assetBundleName}");
  186. }
  187. if (!assetBundle.isStreamedSceneAssetBundle)
  188. {
  189. // 异步load资源到内存cache住
  190. UnityEngine.Object[] assets = assetBundle.LoadAllAssets();
  191. foreach (UnityEngine.Object asset in assets)
  192. {
  193. string path = $"{assetBundleName}/{asset.name}".ToLower();
  194. this.resourceCache[path] = asset;
  195. }
  196. }
  197. this.bundles[assetBundleName] = new ABInfo(assetBundleName, assetBundle);
  198. }
  199. /// <summary>
  200. /// 异步加载assetbundle
  201. /// </summary>
  202. /// <param name="assetBundleName"></param>
  203. /// <returns></returns>
  204. public async Task LoadBundleAsync(string assetBundleName)
  205. {
  206. assetBundleName = assetBundleName.ToLower();
  207. string[] dependencies = ResourcesHelper.GetSortedDependencies(assetBundleName);
  208. // Log.Debug($"-----------dep load {assetBundleName} dep: {dependencies.ToList().ListToString()}");
  209. foreach (string dependency in dependencies)
  210. {
  211. if (string.IsNullOrEmpty(dependency))
  212. {
  213. continue;
  214. }
  215. await this.LoadOneBundleAsync(dependency);
  216. }
  217. }
  218. public async Task LoadOneBundleAsync(string assetBundleName)
  219. {
  220. //Log.Debug($"---------------load one bundle {assetBundleName}");
  221. ABInfo abInfo;
  222. if (this.bundles.TryGetValue(assetBundleName, out abInfo))
  223. {
  224. ++abInfo.RefCount;
  225. return;
  226. }
  227. if (this.cacheDictionary.ContainsKey(assetBundleName))
  228. {
  229. abInfo = this.cacheDictionary[assetBundleName];
  230. ++abInfo.RefCount;
  231. this.bundles[assetBundleName] = abInfo;
  232. this.cacheDictionary.Remove(assetBundleName);
  233. return;
  234. }
  235. if (!Define.IsAsync)
  236. {
  237. string[] realPath = null;
  238. #if UNITY_EDITOR
  239. realPath = AssetDatabase.GetAssetPathsFromAssetBundle(assetBundleName);
  240. foreach (string s in realPath)
  241. {
  242. string assetName = Path.GetFileNameWithoutExtension(s);
  243. string path = $"{assetBundleName}/{assetName}".ToLower();
  244. UnityEngine.Object resource = AssetDatabase.LoadAssetAtPath<UnityEngine.Object>(s);
  245. this.resourceCache[path] = resource;
  246. }
  247. this.bundles[assetBundleName] = new ABInfo(assetBundleName, null);
  248. #endif
  249. return;
  250. }
  251. string p = Path.Combine(PathHelper.AppHotfixResPath, assetBundleName);
  252. AssetBundle assetBundle = null;
  253. if (!File.Exists(p))
  254. {
  255. p = Path.Combine(PathHelper.AppResPath, assetBundleName);
  256. }
  257. using (AssetsBundleLoaderAsync assetsBundleLoaderAsync = ComponentFactory.Create<AssetsBundleLoaderAsync>())
  258. {
  259. assetBundle = await assetsBundleLoaderAsync.LoadAsync(p);
  260. }
  261. if (assetBundle == null)
  262. {
  263. throw new Exception($"assets bundle not found: {assetBundleName}");
  264. }
  265. if (!assetBundle.isStreamedSceneAssetBundle)
  266. {
  267. // 异步load资源到内存cache住
  268. UnityEngine.Object[] assets;
  269. using (AssetsLoaderAsync assetsLoaderAsync = ComponentFactory.Create<AssetsLoaderAsync, AssetBundle>(assetBundle))
  270. {
  271. assets = await assetsLoaderAsync.LoadAllAssetsAsync();
  272. }
  273. foreach (UnityEngine.Object asset in assets)
  274. {
  275. string path = $"{assetBundleName}/{asset.name}".ToLower();
  276. this.resourceCache[path] = asset;
  277. }
  278. }
  279. this.bundles[assetBundleName] = new ABInfo(assetBundleName, assetBundle);
  280. }
  281. public string DebugString()
  282. {
  283. StringBuilder sb = new StringBuilder();
  284. foreach (ABInfo abInfo in this.bundles.Values)
  285. {
  286. sb.Append($"{abInfo.Name}:{abInfo.RefCount}\n");
  287. }
  288. return sb.ToString();
  289. }
  290. }
  291. }