import { BUNDLE_ID } from '@scripts/define/ResDefine'; import { Asset, assetManager, AssetManager, ImageAsset, path, Prefab, Sprite, SpriteFrame, Texture2D } from 'cc'; export enum ResourceScope { App = 'app', Scene = 'scene', View = 'view', } interface RemoteRes { url: string, asset?: T, task?: Promise; } interface ResLoader { asset: T, } export class ResourceManager { private readonly bundles = new Map(); private readonly remotes = new Map>(); private readonly loaders = new Map>(); private getLoaderKey(bundleName: string, path: string, typeName: string) { return `Bundle:${bundleName}-path:${path}-type:${typeName}`; } private async getResLoading(key: string): Promise { if (this.loaders.has(key)) { const res = await this.loaders.get(key); return res as T; } } async loadBundle(name: string): Promise { const cached = this.bundles.get(name) ?? assetManager.getBundle(name); if (cached) { this.bundles.set(name, cached); return cached; } return new Promise((resolve, reject) => { assetManager.loadBundle(name, (error, bundle) => { if (error || !bundle) { reject(error ?? new Error(`Bundle not found: ${name}`)); return; } this.bundles.set(name, bundle); resolve(bundle); }); }); } private async onLoad(key: string, loader: Promise) { this.loaders.set(key, loader); try { const res = await loader; this.loaders.delete(key); return res as T; } catch { this.loaders.delete(key); return null; } } async load(bundleName: string, path: string, type: new (...args: never[]) => T): Promise { const key = this.getLoaderKey(bundleName, path, type.name); if (this.loaders.has(key)) { const res = await this.loaders.get(key); return res as T; } const bundle = await this.loadBundle(bundleName); const loader = new Promise((resolve, reject) => { bundle.load(path, type, (error, asset) => { if (error || !asset) { console.log(`asset load error!! file:${bundleName}/${path} error:${error}`); resolve(null); return; } resolve(asset); }); }); return await this.onLoad(key, loader); } loadPrefab(bundleName: string, path: string): Promise { return this.load(bundleName, path, Prefab); } releaseBundle(name: string): void { const bundle = this.bundles.get(name); if (!bundle) return; bundle.releaseAll(); assetManager.removeBundle(bundle); this.bundles.delete(name); } async loadRemoteSp(url: string): Promise { const key = this.getLoaderKey("RemoteAsset", url, "SpriteFrame"); if (this.remotes.has(key)) { const res = this.remotes.get(key); if (res.task) { await res.task; } return res.asset as SpriteFrame; } const cache: RemoteRes = { url }; cache.task = this.loadRemoteRes(url, "ImageAsset"); this.remotes.set(key, cache); const img = await cache.task; if (!img) { this.remotes.delete(key); return null; } const sp = SpriteFrame.createWithImage(img as ImageAsset); sp.packable = false; cache.asset = sp; delete cache.task; sp["_cacheKey"] = key; return sp; } async loadRemoteRes(url: string, type: string): Promise { const key = this.getLoaderKey("RemoteAsset", url, type); if (this.loaders.has(key)) { const res = await this.loaders.get(key); return res as T; } const loader = new Promise((resolve, reject) => { assetManager.loadRemote(url, (err, res) => { if (err) { console.error(err, url); resolve(null); return; } resolve(res); }); }) return await this.onLoad(key, loader); } async releaseRes(res: Asset) { res.decRef(); const cacheKey: string = res["_cacheKey"]; if (cacheKey && this.remotes.has(cacheKey)) { const cache = this.remotes.get(cacheKey); const refCount = cache.asset.refCount; if (refCount === 0) { // if (cache.asset) { // if (cache.asset instanceof SpriteFrame) { // const texture = cache.asset.texture as Texture2D; // const image = texture.image; // image.destroy(); // texture.destroy() // } // cache.asset.destroy(); // } this.remotes.delete(cacheKey); } } } }