import AssetsBundleMgr from "../../utils/AssetsBundleMgr"; import { EResType } from "./ResBaseAsset"; import { ResCollector } from "./ResCollector"; const { ccclass } = cc._decorator; declare global { /** * !API */ interface IResKeeper { // 组件销毁时自动释放某个资源 AutoReleaseAsset(asset: T, url: string): void; // 加载资源 LoadResCollector(resCollector: ResCollector, promise: IPromiseFunc): Promise; } } /** * 销毁时自动释放资源组件 */ // @ccclass("ResKeeper") @ccclass export class ResKeeper extends cc.Component implements IResKeeper { /** 是否调试状态 */ static DEBUG: boolean = CC_PREVIEW ? true : false; private _i_LoadedAssets: Array = undefined; /** 已加载资源列表 */ private get m_LoadedAssets(): Array { if (!this._i_LoadedAssets) { this._i_LoadedAssets = []; } return this._i_LoadedAssets; } /** * 从目标节点或其父节点递归查找一个资源挂载(销毁时自动释放)组件 * @param attachNode 目标节点 * @param autoCreate 当目标节点找不到 ResKeeper 时是否自动创建一个 */ public static Get(attachNode: cc.Node, autoCreate: boolean = false): IResKeeper { if (attachNode && cc.isValid(attachNode)) { let ret = attachNode.getComponent(ResKeeper); if (!ret) { if (autoCreate) { return attachNode.addComponent(ResKeeper); } else { if (!attachNode.parent) { console.error(`ResKeeper >> attachNode 的父节点不存在 >> attachNode.name = ${attachNode.name}`); return null; } return ResKeeper.Get(attachNode.parent, autoCreate); } } return ret; } console.error(`ResKeeper >> attachNode 为空节点`); return null; } /** * 加载自动释放的资源 * @param resCollector */ async LoadResCollector(resCollector: ResCollector, promise: IPromiseFunc): Promise { const that = this; let __bindAutoReleaseAssetToComp = (url: string, res: T) => { // 性能调优: 调试状态才打印加载的 url // ResKeeper.DEBUG && console.log(`url: ${url} 加载成功`) if (cc.isValid(that)) { that.AutoReleaseAsset(res, url); } else { console.warn("资源加载完毕时,节点已不可用") } } let _onResLoaded = (url: string, err, res, resolve) => { if (err) { console.error(err); /** 释放加载项 */ resolve(); return; } __bindAutoReleaseAssetToComp(url, res); resolve(); } let proArr: Promise[] = []; resCollector.GetResList().forEach(loadAssetItem => { proArr.push(new Promise((resolve, reject) => { switch (loadAssetItem.typ) { case EResType.Prefab: { const bundleUrl = loadAssetItem.data; AssetsBundleMgr.loadBundle(bundleUrl.bundle, (err, bundle) => { if (err) { console.log("SpineNode:bundle load failed:", err) return } // let cacheAsset = bundle.get(bundleUrl.url, cc.Prefab); // if (cacheAsset) { // _onResLoaded(bundleUrl.url, err, cacheAsset, resolve); // } else { bundle.load(bundleUrl.url, cc.Prefab, (err: Error, asset: cc.Prefab) => { _onResLoaded(bundleUrl.url, err, asset, resolve); }) // } }) } break; case EResType.Spine: { const bundleUrl = loadAssetItem.data; AssetsBundleMgr.loadBundle(bundleUrl.bundle, (err, bundle) => { if (err) { console.log("SpineNode:bundle load failed:", err) return } // let cacheAsset = bundle.get(bundleUrl.url, cc.Prefab); // if (cacheAsset) { // _onResLoaded(bundleUrl.url, err, cacheAsset, resolve); // } else { bundle.load(bundleUrl.url, sp.SkeletonData, (err: Error, asset: sp.SkeletonData) => { _onResLoaded(bundleUrl.url, err, asset, resolve); }) // } }) } break; case EResType.SpriteFrame: { const bundleUrl = loadAssetItem.data; AssetsBundleMgr.loadBundle(bundleUrl.bundle, (err, bundle) => { if (err) { console.log("SpriteFrame:bundle load failed:", err) return } bundle.load(bundleUrl.url, cc.SpriteFrame, (err: Error, asset: cc.SpriteFrame) => { _onResLoaded(bundleUrl.url, err, asset, resolve); }) }) } break; case EResType.Json: { const bundleUrl = loadAssetItem.data; AssetsBundleMgr.loadBundle(bundleUrl.bundle, (err, bundle) => { if (err) { console.log("SpriteFrame:bundle load failed:", err) return } bundle.load(bundleUrl.url, cc.JsonAsset, (err: Error, asset: cc.JsonAsset) => { _onResLoaded(bundleUrl.url, err, asset, resolve); }) }) } break; default: { console.error(`【ResKeeper】 >> 未处理的资源加载类型 ${loadAssetItem.typ}`) } break; } })) }); return promise(async (resolve, reject) => { await Promise.all(proArr).then((values) => { resolve(); }); }); } /** * 添加自动释放引用 * @param keepItem */ AutoReleaseAsset(asset: T, url: string): void { /** Borrow 时自动添加了引用计数,销毁时,解引用即可 */ this.m_LoadedAssets.push(new AssetKeepItem(asset, url)); } /** * 释放所有引用的资源 */ onDestroy() { if (this._i_LoadedAssets) { this._i_LoadedAssets.forEach(it => { if (it && it.DecRef) { it.DecRef() } }); this._i_LoadedAssets = undefined; } } } class AssetKeepItem { // -------------------------------- mAsset: cc.Asset; mUrl: string; constructor(asset: cc.Asset, url: string) { this.mAsset = asset; this.mUrl = url; this.AddRef(); } public AddRef(): void { this.mAsset.addRef(); } public DecRef(): void { this.mAsset.decRef(); } }