ResKeeper.ts 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213
  1. import AssetsBundleMgr from "../../utils/AssetsBundleMgr";
  2. import { EResType } from "./ResBaseAsset";
  3. import { ResCollector } from "./ResCollector";
  4. const { ccclass } = cc._decorator;
  5. declare global {
  6. /**
  7. * !API
  8. */
  9. interface IResKeeper {
  10. // 组件销毁时自动释放某个资源
  11. AutoReleaseAsset<T extends cc.Asset>(asset: T, url: string): void;
  12. // 加载资源
  13. LoadResCollector(resCollector: ResCollector, promise: IPromiseFunc): Promise<void>;
  14. }
  15. }
  16. /**
  17. * 销毁时自动释放资源组件
  18. */
  19. // @ccclass("ResKeeper")
  20. @ccclass
  21. export class ResKeeper extends cc.Component implements IResKeeper {
  22. /** 是否调试状态 */
  23. static DEBUG: boolean = CC_PREVIEW ? true : false;
  24. private _i_LoadedAssets: Array<AssetKeepItem> = undefined;
  25. /** 已加载资源列表 */
  26. private get m_LoadedAssets(): Array<AssetKeepItem> {
  27. if (!this._i_LoadedAssets) {
  28. this._i_LoadedAssets = [];
  29. }
  30. return this._i_LoadedAssets;
  31. }
  32. /**
  33. * 从目标节点或其父节点递归查找一个资源挂载(销毁时自动释放)组件
  34. * @param attachNode 目标节点
  35. * @param autoCreate 当目标节点找不到 ResKeeper 时是否自动创建一个
  36. */
  37. public static Get(attachNode: cc.Node, autoCreate: boolean = false): IResKeeper {
  38. if (attachNode && cc.isValid(attachNode)) {
  39. let ret = attachNode.getComponent(ResKeeper);
  40. if (!ret) {
  41. if (autoCreate) {
  42. return attachNode.addComponent(ResKeeper);
  43. } else {
  44. if (!attachNode.parent) {
  45. console.error(`ResKeeper >> attachNode 的父节点不存在 >> attachNode.name = ${attachNode.name}`);
  46. return null;
  47. }
  48. return ResKeeper.Get(attachNode.parent, autoCreate);
  49. }
  50. }
  51. return ret;
  52. }
  53. console.error(`ResKeeper >> attachNode 为空节点`);
  54. return null;
  55. }
  56. /**
  57. * 加载自动释放的资源
  58. * @param resCollector
  59. */
  60. async LoadResCollector(resCollector: ResCollector, promise: IPromiseFunc): Promise<void> {
  61. const that = this;
  62. let __bindAutoReleaseAssetToComp = <T extends cc.Asset>(url: string, res: T) => {
  63. // 性能调优: 调试状态才打印加载的 url
  64. // ResKeeper.DEBUG && console.log(`url: ${url} 加载成功`)
  65. if (cc.isValid(that)) {
  66. that.AutoReleaseAsset(res, url);
  67. } else {
  68. console.warn("资源加载完毕时,节点已不可用")
  69. }
  70. }
  71. let _onResLoaded = (url: string, err, res, resolve) => {
  72. if (err) {
  73. console.error(err);
  74. /** 释放加载项 */
  75. resolve();
  76. return;
  77. }
  78. __bindAutoReleaseAssetToComp(url, res);
  79. resolve();
  80. }
  81. let proArr: Promise<cc.Asset>[] = [];
  82. resCollector.GetResList().forEach(loadAssetItem => {
  83. proArr.push(new Promise((resolve, reject) => {
  84. switch (loadAssetItem.typ) {
  85. case EResType.Prefab: {
  86. const bundleUrl = loadAssetItem.data;
  87. AssetsBundleMgr.loadBundle(bundleUrl.bundle, (err, bundle) => {
  88. if (err) {
  89. console.log("SpineNode:bundle load failed:", err)
  90. return
  91. }
  92. // let cacheAsset = bundle.get(bundleUrl.url, cc.Prefab);
  93. // if (cacheAsset) {
  94. // _onResLoaded(bundleUrl.url, err, cacheAsset, resolve);
  95. // } else {
  96. bundle.load(bundleUrl.url, cc.Prefab, (err: Error, asset: cc.Prefab) => {
  97. _onResLoaded(bundleUrl.url, err, asset, resolve);
  98. })
  99. // }
  100. })
  101. } break;
  102. case EResType.Spine: {
  103. const bundleUrl = loadAssetItem.data;
  104. AssetsBundleMgr.loadBundle(bundleUrl.bundle, (err, bundle) => {
  105. if (err) {
  106. console.log("SpineNode:bundle load failed:", err)
  107. return
  108. }
  109. // let cacheAsset = bundle.get(bundleUrl.url, cc.Prefab);
  110. // if (cacheAsset) {
  111. // _onResLoaded(bundleUrl.url, err, cacheAsset, resolve);
  112. // } else {
  113. bundle.load(bundleUrl.url, sp.SkeletonData, (err: Error, asset: sp.SkeletonData) => {
  114. _onResLoaded(bundleUrl.url, err, asset, resolve);
  115. })
  116. // }
  117. })
  118. } break;
  119. case EResType.SpriteFrame: {
  120. const bundleUrl = loadAssetItem.data;
  121. AssetsBundleMgr.loadBundle(bundleUrl.bundle, (err, bundle) => {
  122. if (err) {
  123. console.log("SpriteFrame:bundle load failed:", err)
  124. return
  125. }
  126. bundle.load(bundleUrl.url, cc.SpriteFrame, (err: Error, asset: cc.SpriteFrame) => {
  127. _onResLoaded(bundleUrl.url, err, asset, resolve);
  128. })
  129. })
  130. } break;
  131. case EResType.Json: {
  132. const bundleUrl = loadAssetItem.data;
  133. AssetsBundleMgr.loadBundle(bundleUrl.bundle, (err, bundle) => {
  134. if (err) {
  135. console.log("SpriteFrame:bundle load failed:", err)
  136. return
  137. }
  138. bundle.load(bundleUrl.url, cc.JsonAsset, (err: Error, asset: cc.JsonAsset) => {
  139. _onResLoaded(bundleUrl.url, err, asset, resolve);
  140. })
  141. })
  142. } break;
  143. default: {
  144. console.error(`【ResKeeper】 >> 未处理的资源加载类型 ${loadAssetItem.typ}`)
  145. } break;
  146. }
  147. }))
  148. });
  149. return promise(async (resolve, reject) => {
  150. await Promise.all(proArr).then((values) => {
  151. resolve();
  152. });
  153. });
  154. }
  155. /**
  156. * 添加自动释放引用
  157. * @param keepItem
  158. */
  159. AutoReleaseAsset<T extends cc.Asset>(asset: T, url: string): void {
  160. /** Borrow 时自动添加了引用计数,销毁时,解引用即可 */
  161. this.m_LoadedAssets.push(new AssetKeepItem(asset, url));
  162. }
  163. /**
  164. * 释放所有引用的资源
  165. */
  166. onDestroy() {
  167. if (this._i_LoadedAssets) {
  168. this._i_LoadedAssets.forEach(it => {
  169. if (it && it.DecRef) {
  170. it.DecRef()
  171. }
  172. });
  173. this._i_LoadedAssets = undefined;
  174. }
  175. }
  176. }
  177. class AssetKeepItem {
  178. // --------------------------------
  179. mAsset: cc.Asset;
  180. mUrl: string;
  181. constructor(asset: cc.Asset, url: string) {
  182. this.mAsset = asset;
  183. this.mUrl = url;
  184. this.AddRef();
  185. }
  186. public AddRef(): void {
  187. this.mAsset.addRef();
  188. }
  189. public DecRef(): void {
  190. this.mAsset.decRef();
  191. }
  192. }