ResourcesComponent.cs 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444
  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 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. if (this.AssetBundle != null)
  45. {
  46. this.AssetBundle.Unload(true);
  47. }
  48. }
  49. }
  50. // 用于字符串转换,减少GC
  51. public static class AssetBundleHelper
  52. {
  53. public static readonly Dictionary<int, string> IntToStringDict = new Dictionary<int, string>();
  54. public static readonly Dictionary<string, string> StringToABDict = new Dictionary<string, string>();
  55. public static readonly Dictionary<string, string> BundleNameToLowerDict = new Dictionary<string, string>()
  56. {
  57. { "StreamingAssets", "StreamingAssets" }
  58. };
  59. // 缓存包依赖,不用每次计算
  60. public static Dictionary<string, string[]> DependenciesCache = new Dictionary<string, string[]>();
  61. public static string IntToString(this int value)
  62. {
  63. string result;
  64. if (IntToStringDict.TryGetValue(value, out result))
  65. {
  66. return result;
  67. }
  68. result = value.ToString();
  69. IntToStringDict[value] = result;
  70. return result;
  71. }
  72. public static string StringToAB(this string value)
  73. {
  74. string result;
  75. if (StringToABDict.TryGetValue(value, out result))
  76. {
  77. return result;
  78. }
  79. result = value + ".unity3d";
  80. StringToABDict[value] = result;
  81. return result;
  82. }
  83. public static string IntToAB(this int value)
  84. {
  85. return value.IntToString().StringToAB();
  86. }
  87. public static string BundleNameToLower(this string value)
  88. {
  89. string result;
  90. if (BundleNameToLowerDict.TryGetValue(value, out result))
  91. {
  92. return result;
  93. }
  94. result = value.ToLower();
  95. BundleNameToLowerDict[value] = result;
  96. return result;
  97. }
  98. public static string[] GetDependencies(string assetBundleName)
  99. {
  100. string[] dependencies = new string[0];
  101. if (DependenciesCache.TryGetValue(assetBundleName,out dependencies))
  102. {
  103. return dependencies;
  104. }
  105. if (!Define.IsAsync)
  106. {
  107. #if UNITY_EDITOR
  108. dependencies = AssetDatabase.GetAssetBundleDependencies(assetBundleName, true);
  109. #endif
  110. }
  111. else
  112. {
  113. dependencies = ResourcesComponent.AssetBundleManifestObject.GetAllDependencies(assetBundleName);
  114. }
  115. DependenciesCache.Add(assetBundleName, dependencies);
  116. return dependencies;
  117. }
  118. public static string[] GetSortedDependencies(string assetBundleName)
  119. {
  120. Dictionary<string, int> info = new Dictionary<string, int>();
  121. List<string> parents = new List<string>();
  122. CollectDependencies(parents, assetBundleName, info);
  123. string[] ss = info.OrderBy(x => x.Value).Select(x => x.Key).ToArray();
  124. return ss;
  125. }
  126. public static void CollectDependencies(List<string> parents, string assetBundleName, Dictionary<string, int> info)
  127. {
  128. parents.Add(assetBundleName);
  129. string[] deps = GetDependencies(assetBundleName);
  130. foreach (string parent in parents)
  131. {
  132. if (!info.ContainsKey(parent))
  133. {
  134. info[parent] = 0;
  135. }
  136. info[parent] += deps.Length;
  137. }
  138. foreach (string dep in deps)
  139. {
  140. if (parents.Contains(dep))
  141. {
  142. throw new Exception($"包有循环依赖,请重新标记: {assetBundleName} {dep}");
  143. }
  144. CollectDependencies(parents, dep, info);
  145. }
  146. parents.RemoveAt(parents.Count - 1);
  147. }
  148. }
  149. public class ResourcesComponent : Component
  150. {
  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?.AssetBundle?.Unload(true);
  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.ToLower();
  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.ToLower();
  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. abInfo.Dispose();
  208. //Log.Debug($"cache count: {this.cacheDictionary.Count}");
  209. }
  210. /// <summary>
  211. /// 同步加载assetbundle
  212. /// </summary>
  213. /// <param name="assetBundleName"></param>
  214. /// <returns></returns>
  215. public void LoadBundle(string assetBundleName)
  216. {
  217. assetBundleName = assetBundleName.ToLower();
  218. string[] dependencies = AssetBundleHelper.GetSortedDependencies(assetBundleName);
  219. //Log.Debug($"-----------dep load {assetBundleName} dep: {dependencies.ToList().ListToString()}");
  220. foreach (string dependency in dependencies)
  221. {
  222. if (string.IsNullOrEmpty(dependency))
  223. {
  224. continue;
  225. }
  226. this.LoadOneBundle(dependency);
  227. }
  228. }
  229. public void AddResource(string bundleName, string assetName, UnityEngine.Object resource)
  230. {
  231. Dictionary<string, UnityEngine.Object> dict;
  232. if (!this.resourceCache.TryGetValue(bundleName.BundleNameToLower(), out dict))
  233. {
  234. dict = new Dictionary<string, UnityEngine.Object>();
  235. this.resourceCache[bundleName] = dict;
  236. }
  237. dict[assetName] = resource;
  238. }
  239. public void LoadOneBundle(string assetBundleName)
  240. {
  241. //Log.Debug($"---------------load one bundle {assetBundleName}");
  242. ABInfo abInfo;
  243. if (this.bundles.TryGetValue(assetBundleName, out abInfo))
  244. {
  245. ++abInfo.RefCount;
  246. return;
  247. }
  248. if (!Define.IsAsync)
  249. {
  250. string[] realPath = null;
  251. #if UNITY_EDITOR
  252. realPath = AssetDatabase.GetAssetPathsFromAssetBundle(assetBundleName);
  253. foreach (string s in realPath)
  254. {
  255. string assetName = Path.GetFileNameWithoutExtension(s);
  256. UnityEngine.Object resource = AssetDatabase.LoadAssetAtPath<UnityEngine.Object>(s);
  257. AddResource(assetBundleName, assetName, resource);
  258. }
  259. abInfo = new ABInfo(assetBundleName, null);
  260. abInfo.Parent = this;
  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 = new ABInfo(assetBundleName, assetBundle);
  290. abInfo.Parent = this;
  291. this.bundles[assetBundleName] = abInfo;
  292. }
  293. /// <summary>
  294. /// 异步加载assetbundle
  295. /// </summary>
  296. /// <param name="assetBundleName"></param>
  297. /// <returns></returns>
  298. public async ETTask LoadBundleAsync(string assetBundleName)
  299. {
  300. assetBundleName = assetBundleName.ToLower();
  301. string[] dependencies = AssetBundleHelper.GetSortedDependencies(assetBundleName);
  302. // Log.Debug($"-----------dep load {assetBundleName} dep: {dependencies.ToList().ListToString()}");
  303. foreach (string dependency in dependencies)
  304. {
  305. if (string.IsNullOrEmpty(dependency))
  306. {
  307. continue;
  308. }
  309. await this.LoadOneBundleAsync(dependency);
  310. }
  311. }
  312. public async ETTask LoadOneBundleAsync(string assetBundleName)
  313. {
  314. ABInfo abInfo;
  315. if (this.bundles.TryGetValue(assetBundleName, out abInfo))
  316. {
  317. ++abInfo.RefCount;
  318. return;
  319. }
  320. //Log.Debug($"---------------load one bundle {assetBundleName}");
  321. if (!Define.IsAsync)
  322. {
  323. string[] realPath = null;
  324. #if UNITY_EDITOR
  325. realPath = AssetDatabase.GetAssetPathsFromAssetBundle(assetBundleName);
  326. foreach (string s in realPath)
  327. {
  328. string assetName = Path.GetFileNameWithoutExtension(s);
  329. UnityEngine.Object resource = AssetDatabase.LoadAssetAtPath<UnityEngine.Object>(s);
  330. AddResource(assetBundleName, assetName, resource);
  331. }
  332. abInfo = new ABInfo(assetBundleName, null);
  333. abInfo.Parent = this;
  334. this.bundles[assetBundleName] = abInfo;
  335. #endif
  336. return;
  337. }
  338. string p = Path.Combine(PathHelper.AppHotfixResPath, assetBundleName);
  339. AssetBundle assetBundle = null;
  340. if (!File.Exists(p))
  341. {
  342. p = Path.Combine(PathHelper.AppResPath, assetBundleName);
  343. }
  344. using (AssetsBundleLoaderAsync assetsBundleLoaderAsync = ComponentFactory.Create<AssetsBundleLoaderAsync>())
  345. {
  346. assetBundle = await assetsBundleLoaderAsync.LoadAsync(p);
  347. }
  348. if (assetBundle == null)
  349. {
  350. throw new Exception($"assets bundle not found: {assetBundleName}");
  351. }
  352. if (!assetBundle.isStreamedSceneAssetBundle)
  353. {
  354. // 异步load资源到内存cache住
  355. UnityEngine.Object[] assets;
  356. using (AssetsLoaderAsync assetsLoaderAsync = ComponentFactory.Create<AssetsLoaderAsync, AssetBundle>(assetBundle))
  357. {
  358. assets = await assetsLoaderAsync.LoadAllAssetsAsync();
  359. }
  360. foreach (UnityEngine.Object asset in assets)
  361. {
  362. AddResource(assetBundleName, asset.name, asset);
  363. }
  364. }
  365. abInfo = new ABInfo(assetBundleName, assetBundle);
  366. abInfo.Parent = this;
  367. this.bundles[assetBundleName] = abInfo;
  368. }
  369. public string DebugString()
  370. {
  371. StringBuilder sb = new StringBuilder();
  372. foreach (ABInfo abInfo in this.bundles.Values)
  373. {
  374. sb.Append($"{abInfo.Name}:{abInfo.RefCount}\n");
  375. }
  376. return sb.ToString();
  377. }
  378. }
  379. }