BundleLoader.ts 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214
  1. import { assetManager, JsonAsset, Asset, SpriteFrame, AnimationClip } from 'cc';
  2. import { sp } from 'cc';
  3. /**
  4. * Bundle资源加载器
  5. * 统一管理Asset Bundle的加载和资源获取
  6. */
  7. export class BundleLoader {
  8. private static instance: BundleLoader = null;
  9. private loadedBundles: Map<string, any> = new Map();
  10. private loadingPromises: Map<string, Promise<any>> = new Map();
  11. public static getInstance(): BundleLoader {
  12. if (!BundleLoader.instance) {
  13. BundleLoader.instance = new BundleLoader();
  14. }
  15. return BundleLoader.instance;
  16. }
  17. /**
  18. * 加载Bundle
  19. * @param bundleName Bundle名称
  20. * @returns Promise<Bundle>
  21. */
  22. public async loadBundle(bundleName: string): Promise<any> {
  23. // 如果已经加载过,直接返回
  24. if (this.loadedBundles.has(bundleName)) {
  25. return this.loadedBundles.get(bundleName);
  26. }
  27. // 如果正在加载,返回加载Promise
  28. if (this.loadingPromises.has(bundleName)) {
  29. return this.loadingPromises.get(bundleName);
  30. }
  31. // 开始加载Bundle
  32. const loadingPromise = new Promise<any>((resolve, reject) => {
  33. console.log(`[BundleLoader] 开始加载Bundle: ${bundleName}`);
  34. assetManager.loadBundle(bundleName, (err, bundle) => {
  35. if (err) {
  36. console.error(`[BundleLoader] 加载Bundle失败: ${bundleName}`, err);
  37. this.loadingPromises.delete(bundleName);
  38. reject(err);
  39. return;
  40. }
  41. console.log(`[BundleLoader] Bundle加载成功: ${bundleName}`);
  42. this.loadedBundles.set(bundleName, bundle);
  43. this.loadingPromises.delete(bundleName);
  44. resolve(bundle);
  45. });
  46. });
  47. this.loadingPromises.set(bundleName, loadingPromise);
  48. return loadingPromise;
  49. }
  50. /**
  51. * 从Bundle中加载资源
  52. * @param bundleName Bundle名称
  53. * @param resourcePath 资源路径(相对于Bundle根目录)
  54. * @param assetType 资源类型
  55. * @returns Promise<Asset>
  56. */
  57. public async loadAssetFromBundle<T extends Asset>(bundleName: string, resourcePath: string, assetType?: typeof Asset): Promise<T> {
  58. try {
  59. // 先确保Bundle已加载
  60. const bundle = await this.loadBundle(bundleName);
  61. return new Promise<T>((resolve, reject) => {
  62. console.log(`[BundleLoader] 从Bundle ${bundleName} 加载资源: ${resourcePath}`);
  63. if (assetType) {
  64. bundle.load(resourcePath, assetType, (err: Error, asset: T) => {
  65. if (err) {
  66. console.error(`[BundleLoader] 从Bundle ${bundleName} 加载资源失败: ${resourcePath}`, err);
  67. reject(err);
  68. return;
  69. }
  70. console.log(`[BundleLoader] 资源加载成功: ${bundleName}/${resourcePath}`);
  71. resolve(asset);
  72. });
  73. } else {
  74. bundle.load(resourcePath, (err: Error, asset: T) => {
  75. if (err) {
  76. console.error(`[BundleLoader] 从Bundle ${bundleName} 加载资源失败: ${resourcePath}`, err);
  77. reject(err);
  78. return;
  79. }
  80. console.log(`[BundleLoader] 资源加载成功: ${bundleName}/${resourcePath}`);
  81. resolve(asset);
  82. });
  83. }
  84. });
  85. } catch (error) {
  86. console.error(`[BundleLoader] 加载资源时发生错误: ${bundleName}/${resourcePath}`, error);
  87. throw error;
  88. }
  89. }
  90. /**
  91. * 从data Bundle加载JSON资源的便捷方法
  92. * @param resourcePath 资源路径(相对于data Bundle根目录)
  93. * @returns Promise<JsonAsset>
  94. */
  95. public async loadDataJson(resourcePath: string): Promise<JsonAsset> {
  96. return this.loadAssetFromBundle<JsonAsset>('data', resourcePath, JsonAsset);
  97. }
  98. /**
  99. * 从images Bundle加载图片资源的便捷方法
  100. * @param resourcePath 资源路径(相对于images Bundle根目录)
  101. * @returns Promise<Asset>
  102. */
  103. public async loadImage(resourcePath: string): Promise<Asset> {
  104. return this.loadAssetFromBundle('images', resourcePath);
  105. }
  106. /**
  107. * 从images Bundle加载SpriteFrame资源的便捷方法
  108. * @param resourcePath 资源路径(相对于images Bundle根目录)
  109. * @returns Promise<SpriteFrame>
  110. */
  111. public async loadSpriteFrame(resourcePath: string): Promise<SpriteFrame> {
  112. return this.loadAssetFromBundle<SpriteFrame>('images', resourcePath, SpriteFrame);
  113. }
  114. /**
  115. * 从Animation Bundle加载动画资源的便捷方法
  116. * @param resourcePath 资源路径(相对于Animation Bundle根目录)
  117. * @returns Promise<AnimationClip>
  118. */
  119. public async loadAnimation(resourcePath: string): Promise<AnimationClip> {
  120. return this.loadAssetFromBundle<AnimationClip>('Animation', resourcePath, AnimationClip);
  121. }
  122. /**
  123. * 从Animation Bundle加载骨骼动画数据
  124. * @param resourcePath 资源路径(相对于Bundle根目录)
  125. * @returns Promise<sp.SkeletonData>
  126. */
  127. public static async loadSkeletonData(resourcePath: string): Promise<sp.SkeletonData> {
  128. const instance = BundleLoader.getInstance();
  129. // 根据资源类型确定正确的加载路径
  130. let finalPath: string;
  131. // 如果是敌人动画路径(如 EnemyAni/007/007),直接使用
  132. if (resourcePath.includes('EnemyAni/') || resourcePath.includes('WeaponTx/')) {
  133. finalPath = resourcePath;
  134. }
  135. // 如果是武器燃烧动画路径(如 WeaponBurnAni/shengji),需要添加/skeleton
  136. else if (resourcePath.includes('WeaponBurnAni/')) {
  137. finalPath = resourcePath.endsWith('/skeleton') || resourcePath.endsWith('skeleton')
  138. ? resourcePath
  139. : resourcePath + '/skeleton';
  140. }
  141. // 其他情况,先尝试直接路径
  142. else {
  143. finalPath = resourcePath;
  144. }
  145. console.log(`[BundleLoader] 加载骨骼动画资源: ${resourcePath} -> ${finalPath}`);
  146. try {
  147. return await instance.loadAssetFromBundle('Animation', finalPath, sp.SkeletonData);
  148. } catch (error) {
  149. console.error(`[BundleLoader] 骨骼动画加载失败: ${finalPath}`, error);
  150. throw error;
  151. }
  152. }
  153. /**
  154. * 检查Bundle是否已加载
  155. * @param bundleName Bundle名称
  156. * @returns boolean
  157. */
  158. public isBundleLoaded(bundleName: string): boolean {
  159. return this.loadedBundles.has(bundleName);
  160. }
  161. /**
  162. * 获取已加载的Bundle
  163. * @param bundleName Bundle名称
  164. * @returns Bundle | null
  165. */
  166. public getLoadedBundle(bundleName: string): any {
  167. return this.loadedBundles.get(bundleName) || null;
  168. }
  169. /**
  170. * 释放Bundle资源
  171. * @param bundleName Bundle名称
  172. */
  173. public releaseBundle(bundleName: string): void {
  174. const bundle = this.loadedBundles.get(bundleName);
  175. if (bundle) {
  176. bundle.releaseAll();
  177. this.loadedBundles.delete(bundleName);
  178. console.log(`[BundleLoader] Bundle已释放: ${bundleName}`);
  179. }
  180. }
  181. /**
  182. * 释放所有Bundle资源
  183. */
  184. public releaseAllBundles(): void {
  185. for (const [bundleName, bundle] of this.loadedBundles) {
  186. bundle.releaseAll();
  187. }
  188. this.loadedBundles.clear();
  189. console.log('[BundleLoader] 所有Bundle已释放');
  190. }
  191. }