ResourceManager.ts 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172
  1. import { BUNDLE_ID } from '@scripts/define/ResDefine';
  2. import { Asset, assetManager, AssetManager, ImageAsset, path, 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<T>(path, type, (error, asset) => {
  66. if (error || !asset) {
  67. console.log(`asset load error!! file:${bundleName}/${path} error:${error}`);
  68. resolve(null);
  69. return;
  70. }
  71. resolve(asset);
  72. });
  73. });
  74. return await this.onLoad<T>(key, loader);
  75. }
  76. loadPrefab(bundleName: string, path: string): Promise<Prefab> {
  77. return this.load(bundleName, path, Prefab);
  78. }
  79. releaseBundle(name: string): void {
  80. const bundle = this.bundles.get(name);
  81. if (!bundle) return;
  82. bundle.releaseAll();
  83. assetManager.removeBundle(bundle);
  84. this.bundles.delete(name);
  85. }
  86. async loadRemoteSp(url: string): Promise<SpriteFrame | null> {
  87. const key = this.getLoaderKey("RemoteAsset", url, "SpriteFrame");
  88. if (this.remotes.has(key)) {
  89. const res = this.remotes.get(key);
  90. if (res.task) {
  91. await res.task;
  92. }
  93. return res.asset as SpriteFrame;
  94. }
  95. const cache: RemoteRes<SpriteFrame> = { url };
  96. cache.task = this.loadRemoteRes<ImageAsset>(url, "ImageAsset");
  97. this.remotes.set(key, cache);
  98. const img = await cache.task;
  99. if (!img) {
  100. this.remotes.delete(key);
  101. return null;
  102. }
  103. const sp = SpriteFrame.createWithImage(img as ImageAsset);
  104. sp.packable = false;
  105. cache.asset = sp;
  106. delete cache.task;
  107. sp["_cacheKey"] = key;
  108. return sp;
  109. }
  110. async loadRemoteRes<T extends Asset>(url: string, type: string): Promise<T | null> {
  111. const key = this.getLoaderKey("RemoteAsset", url, type);
  112. if (this.loaders.has(key)) {
  113. const res = await this.loaders.get(key);
  114. return res as T;
  115. }
  116. const loader = new Promise<T>((resolve, reject) => {
  117. assetManager.loadRemote<T>(url, (err, res) => {
  118. if (err) {
  119. console.error(err, url);
  120. resolve(null);
  121. return;
  122. }
  123. resolve(res);
  124. });
  125. })
  126. return await this.onLoad<T>(key, loader);
  127. }
  128. async releaseRes(res: Asset) {
  129. res.decRef();
  130. const cacheKey: string = res["_cacheKey"];
  131. if (cacheKey && this.remotes.has(cacheKey)) {
  132. const cache = this.remotes.get(cacheKey);
  133. const refCount = cache.asset.refCount;
  134. if (refCount === 0) {
  135. // if (cache.asset) {
  136. // if (cache.asset instanceof SpriteFrame) {
  137. // const texture = cache.asset.texture as Texture2D;
  138. // const image = texture.image;
  139. // image.destroy();
  140. // texture.destroy()
  141. // }
  142. // cache.asset.destroy();
  143. // }
  144. this.remotes.delete(cacheKey);
  145. }
  146. }
  147. }
  148. }