ResourceManager.ts 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171
  1. import { BUNDLE_ID } from '@scripts/define/ResDefine';
  2. import { Asset, assetManager, AssetManager, ImageAsset, Prefab, Sprite, SpriteFrame, Texture2D } from 'cc';
  3. export enum ResourceScope {
  4. App = 'app',
  5. Scene = 'scene',
  6. View = 'view',
  7. }
  8. interface RemoteRes<T> {
  9. url: string,
  10. asset?: T,
  11. task?: Promise<Asset>;
  12. }
  13. interface ResLoader<T> {
  14. asset: T,
  15. }
  16. export class ResourceManager {
  17. private readonly bundles = new Map<string, AssetManager.Bundle>();
  18. private readonly remotes = new Map<string, RemoteRes<Asset>>();
  19. private readonly loaders = new Map<string, Promise<Asset>>();
  20. private getLoaderKey(bundleName: string, path: string, typeName: string) {
  21. return `Bundle:${bundleName}-path:${path}-type:${typeName}`;
  22. }
  23. private async getResLoading<T extends Asset>(key: string): Promise<T> {
  24. if (this.loaders.has(key)) {
  25. const res = await this.loaders.get(key);
  26. return res as T;
  27. }
  28. }
  29. async loadBundle(name: string): Promise<AssetManager.Bundle> {
  30. const cached = this.bundles.get(name) ?? assetManager.getBundle(name);
  31. if (cached) {
  32. this.bundles.set(name, cached);
  33. return cached;
  34. }
  35. return new Promise((resolve, reject) => {
  36. assetManager.loadBundle(name, (error, bundle) => {
  37. if (error || !bundle) {
  38. reject(error ?? new Error(`Bundle not found: ${name}`));
  39. return;
  40. }
  41. this.bundles.set(name, bundle);
  42. resolve(bundle);
  43. });
  44. });
  45. }
  46. private async onLoad<T extends Asset>(key: string, loader: Promise<T>) {
  47. this.loaders.set(key, loader);
  48. try {
  49. const res = await loader;
  50. this.loaders.delete(key);
  51. return res as T;
  52. } catch {
  53. this.loaders.delete(key);
  54. return null;
  55. }
  56. }
  57. async load<T extends Asset>(bundleName: string, path: string, type: new (...args: never[]) => T): Promise<T> {
  58. const key = this.getLoaderKey(bundleName, path, type.name);
  59. if (this.loaders.has(key)) {
  60. const res = await this.loaders.get(key);
  61. return res as T;
  62. }
  63. const bundle = await this.loadBundle(bundleName);
  64. const loader = new Promise<T>((resolve, reject) => {
  65. bundle.load(path, type, (error, asset) => {
  66. if (error || !asset) {
  67. reject(error ?? new Error(`Asset not found: ${bundleName}/${path}`));
  68. return;
  69. }
  70. resolve(asset as T);
  71. });
  72. });
  73. return await this.onLoad<T>(key, loader);
  74. }
  75. loadPrefab(bundleName: string, path: string): Promise<Prefab> {
  76. return this.load(bundleName, path, Prefab);
  77. }
  78. releaseBundle(name: string): void {
  79. const bundle = this.bundles.get(name);
  80. if (!bundle) return;
  81. bundle.releaseAll();
  82. assetManager.removeBundle(bundle);
  83. this.bundles.delete(name);
  84. }
  85. async loadRemoteSp(url: string): Promise<SpriteFrame | null> {
  86. const key = this.getLoaderKey("RemoteAsset", url, "SpriteFrame");
  87. if (this.remotes.has(key)) {
  88. const res = this.remotes.get(key);
  89. if (res.task) {
  90. await res.task;
  91. }
  92. return res.asset as SpriteFrame;
  93. }
  94. const cache: RemoteRes<SpriteFrame> = { url };
  95. cache.task = this.loadRemoteRes<ImageAsset>(url, "ImageAsset");
  96. this.remotes.set(key, cache);
  97. const img = await cache.task;
  98. if (!img) {
  99. this.remotes.delete(key);
  100. return null;
  101. }
  102. const sp = SpriteFrame.createWithImage(img as ImageAsset);
  103. cache.asset = sp;
  104. delete cache.task;
  105. sp["_cacheKey"] = key;
  106. (sp.texture as Texture2D).image
  107. return sp;
  108. }
  109. async loadRemoteRes<T extends Asset>(url: string, type: string): Promise<T | null> {
  110. const key = this.getLoaderKey("RemoteAsset", url, type);
  111. if (this.loaders.has(key)) {
  112. const res = await this.loaders.get(key);
  113. return res as T;
  114. }
  115. const loader = new Promise<T>((resolve, reject) => {
  116. assetManager.loadRemote<T>(url, (err, res) => {
  117. if (err) {
  118. console.error(err, url);
  119. resolve(null);
  120. return;
  121. }
  122. resolve(res);
  123. });
  124. })
  125. return await this.onLoad<T>(key, loader);
  126. }
  127. async releaseRes(res: Asset) {
  128. res.decRef();
  129. const cacheKey: string = res["_cacheKey"];
  130. if (cacheKey && this.remotes.has(cacheKey)) {
  131. const cache = this.remotes.get(cacheKey);
  132. const refCount = cache.asset.refCount;
  133. if (refCount === 0) {
  134. // if (cache.asset) {
  135. // if (cache.asset instanceof SpriteFrame) {
  136. // const texture = cache.asset.texture as Texture2D;
  137. // const image = texture.image;
  138. // image.destroy();
  139. // texture.destroy()
  140. // }
  141. // cache.asset.destroy();
  142. // }
  143. this.remotes.delete(cacheKey);
  144. }
  145. }
  146. }
  147. }