import { assetManager, JsonAsset, Asset, SpriteFrame, AnimationClip } from 'cc'; import { sp } from 'cc'; /** * Bundle资源加载器 * 统一管理Asset Bundle的加载和资源获取 */ export class BundleLoader { private static instance: BundleLoader = null; private loadedBundles: Map = new Map(); private loadingPromises: Map> = new Map(); public static getInstance(): BundleLoader { if (!BundleLoader.instance) { BundleLoader.instance = new BundleLoader(); } return BundleLoader.instance; } /** * 加载Bundle * @param bundleName Bundle名称 * @returns Promise */ public async loadBundle(bundleName: string): Promise { // 如果已经加载过,直接返回 if (this.loadedBundles.has(bundleName)) { return this.loadedBundles.get(bundleName); } // 如果正在加载,返回加载Promise if (this.loadingPromises.has(bundleName)) { return this.loadingPromises.get(bundleName); } // 开始加载Bundle const loadingPromise = new Promise((resolve, reject) => { console.log(`[BundleLoader] 开始加载Bundle: ${bundleName}`); assetManager.loadBundle(bundleName, (err, bundle) => { if (err) { console.error(`[BundleLoader] 加载Bundle失败: ${bundleName}`, err); this.loadingPromises.delete(bundleName); reject(err); return; } console.log(`[BundleLoader] Bundle加载成功: ${bundleName}`); this.loadedBundles.set(bundleName, bundle); this.loadingPromises.delete(bundleName); resolve(bundle); }); }); this.loadingPromises.set(bundleName, loadingPromise); return loadingPromise; } /** * 从Bundle中加载资源 * @param bundleName Bundle名称 * @param resourcePath 资源路径(相对于Bundle根目录) * @param assetType 资源类型 * @returns Promise */ public async loadAssetFromBundle(bundleName: string, resourcePath: string, assetType?: typeof Asset): Promise { try { // 先确保Bundle已加载 const bundle = await this.loadBundle(bundleName); return new Promise((resolve, reject) => { console.log(`[BundleLoader] 从Bundle ${bundleName} 加载资源: ${resourcePath}`); if (assetType) { bundle.load(resourcePath, assetType, (err: Error, asset: T) => { if (err) { console.error(`[BundleLoader] 从Bundle ${bundleName} 加载资源失败: ${resourcePath}`, err); reject(err); return; } console.log(`[BundleLoader] 资源加载成功: ${bundleName}/${resourcePath}`); resolve(asset); }); } else { bundle.load(resourcePath, (err: Error, asset: T) => { if (err) { console.error(`[BundleLoader] 从Bundle ${bundleName} 加载资源失败: ${resourcePath}`, err); reject(err); return; } console.log(`[BundleLoader] 资源加载成功: ${bundleName}/${resourcePath}`); resolve(asset); }); } }); } catch (error) { console.error(`[BundleLoader] 加载资源时发生错误: ${bundleName}/${resourcePath}`, error); throw error; } } /** * 从data Bundle加载JSON资源的便捷方法 * @param resourcePath 资源路径(相对于data Bundle根目录) * @returns Promise */ public async loadDataJson(resourcePath: string): Promise { return this.loadAssetFromBundle('data', resourcePath, JsonAsset); } /** * 从images Bundle加载图片资源的便捷方法 * @param resourcePath 资源路径(相对于images Bundle根目录) * @returns Promise */ public async loadImage(resourcePath: string): Promise { return this.loadAssetFromBundle('images', resourcePath); } /** * 从images Bundle加载SpriteFrame资源的便捷方法 * @param resourcePath 资源路径(相对于images Bundle根目录) * @returns Promise */ public async loadSpriteFrame(resourcePath: string): Promise { return this.loadAssetFromBundle('images', resourcePath, SpriteFrame); } /** * 从Animation Bundle加载动画资源的便捷方法 * @param resourcePath 资源路径(相对于Animation Bundle根目录) * @returns Promise */ public async loadAnimation(resourcePath: string): Promise { return this.loadAssetFromBundle('Animation', resourcePath, AnimationClip); } /** * 从Animation Bundle加载骨骼动画数据 * @param resourcePath 资源路径(相对于Bundle根目录) * @returns Promise */ public static async loadSkeletonData(resourcePath: string): Promise { const instance = BundleLoader.getInstance(); // 根据资源类型确定正确的加载路径 let finalPath: string; // 如果是敌人动画路径(如 EnemyAni/007/007),直接使用 if (resourcePath.includes('EnemyAni/') || resourcePath.includes('WeaponTx/')) { finalPath = resourcePath; } // 如果是武器燃烧动画路径(如 WeaponBurnAni/shengji),需要添加/skeleton else if (resourcePath.includes('WeaponBurnAni/')) { finalPath = resourcePath.endsWith('/skeleton') || resourcePath.endsWith('skeleton') ? resourcePath : resourcePath + '/skeleton'; } // 其他情况,先尝试直接路径 else { finalPath = resourcePath; } console.log(`[BundleLoader] 加载骨骼动画资源: ${resourcePath} -> ${finalPath}`); try { return await instance.loadAssetFromBundle('Animation', finalPath, sp.SkeletonData); } catch (error) { console.error(`[BundleLoader] 骨骼动画加载失败: ${finalPath}`, error); throw error; } } /** * 检查Bundle是否已加载 * @param bundleName Bundle名称 * @returns boolean */ public isBundleLoaded(bundleName: string): boolean { return this.loadedBundles.has(bundleName); } /** * 获取已加载的Bundle * @param bundleName Bundle名称 * @returns Bundle | null */ public getLoadedBundle(bundleName: string): any { return this.loadedBundles.get(bundleName) || null; } /** * 释放Bundle资源 * @param bundleName Bundle名称 */ public releaseBundle(bundleName: string): void { const bundle = this.loadedBundles.get(bundleName); if (bundle) { bundle.releaseAll(); this.loadedBundles.delete(bundleName); console.log(`[BundleLoader] Bundle已释放: ${bundleName}`); } } /** * 释放所有Bundle资源 */ public releaseAllBundles(): void { for (const [bundleName, bundle] of this.loadedBundles) { bundle.releaseAll(); } this.loadedBundles.clear(); console.log('[BundleLoader] 所有Bundle已释放'); } }