import { Asset, AssetManager, assetManager, resources } from 'cc'; /** * 资源管理器 * 职责: * - 统一管理资源加载 * - 提供从bundle中按路径加载资源的接口 * - 支持资源预加载 * - 管理资源缓存和释放 */ export class ResMgr { private static _instance: ResMgr | null = null; /** * 资源缓存 * key: bundleName:path * value: Asset */ private _cache: Map; /** * Bundle缓存 * key: bundleName * value: AssetManager.Bundle */ private _bundles: Map; /** * 构造函数(私有) */ private constructor() { this._cache = new Map(); this._bundles = new Map(); // 默认添加resources bundle this._bundles.set('resources', resources); } /** * 获取单例 */ static getInstance(): ResMgr { if (!this._instance) { this._instance = new ResMgr(); } return this._instance; } /** * 获取Bundle * @param bundleName bundle名称 */ private getBundle(bundleName: string): AssetManager.Bundle | null { // 从缓存中获取 if (this._bundles.has(bundleName)) { return this._bundles.get(bundleName)!; } // 使用 assetManager.getBundle() 方法获取已加载的 Bundle const bundle = assetManager.getBundle(bundleName); if (bundle) { this._bundles.set(bundleName, bundle); return bundle; } console.error(`[ResMgr] Bundle "${bundleName}" 不存在`); return null; } /** * 生成缓存key */ private getCacheKey(bundleName: string, path: string): string { return `${bundleName}:${path}`; } /** * 加载单个资源 * @param bundleName bundle名称 * @param path 资源路径 * @param type 资源类型 * @returns Promise<资源> */ load( bundleName: string, path: string, type: new (...args: any[]) => T ): Promise { return new Promise((resolve, reject) => { const cacheKey = this.getCacheKey(bundleName, path); // 检查缓存 if (this._cache.has(cacheKey)) { const cached = this._cache.get(cacheKey) as T; console.log(`[ResMgr] 从缓存加载资源: ${path}`); resolve(cached); return; } // 获取bundle const bundle = this.getBundle(bundleName); if (!bundle) { reject(new Error(`Bundle "${bundleName}" 不存在`)); return; } // 加载资源 console.log(`[ResMgr] 开始加载资源: ${bundleName}/${path}`); bundle.load(path, type, (err, asset) => { if (err) { console.error(`[ResMgr] 加载资源失败: ${bundleName}/${path}`, err); reject(err); return; } // 缓存资源 this._cache.set(cacheKey, asset as Asset); console.log(`[ResMgr] 资源加载成功: ${bundleName}/${path}`); resolve(asset as T); }); }); } /** * 预加载资源 * @param bundleName bundle名称 * @param path 资源路径 * @param type 资源类型 * @param onProgress 进度回调 * @returns Promise */ preload( bundleName: string, path: string, type: new (...args: any[]) => T, onProgress?: (finished: number, total: number) => void ): Promise { return new Promise((resolve, reject) => { // 获取bundle const bundle = this.getBundle(bundleName); if (!bundle) { reject(new Error(`Bundle "${bundleName}" 不存在`)); return; } console.log(`[ResMgr] 开始预加载资源: ${bundleName}/${path}`); // 预加载资源 bundle.preload( path, type, (finished, total) => { if (onProgress) { onProgress(finished, total); } }, (err) => { if (err) { console.error(`[ResMgr] 预加载资源失败: ${bundleName}/${path}`, err); reject(err); return; } console.log(`[ResMgr] 资源预加载成功: ${bundleName}/${path}`); resolve(); } ); }); } /** * 加载目录 * @param bundleName bundle名称 * @param dir 目录路径 * @param type 资源类型 * @returns Promise<资源数组> */ loadDir( bundleName: string, dir: string, type: new (...args: any[]) => T ): Promise { return new Promise((resolve, reject) => { // 获取bundle const bundle = this.getBundle(bundleName); if (!bundle) { reject(new Error(`Bundle "${bundleName}" 不存在`)); return; } console.log(`[ResMgr] 开始加载目录: ${bundleName}/${dir}`); // 加载目录 bundle.loadDir(dir, type, (err, assets) => { if (err) { console.error(`[ResMgr] 加载目录失败: ${bundleName}/${dir}`, err); reject(err); return; } // 缓存所有资源 assets.forEach((asset) => { const path = `${dir}/${asset.name}`; const cacheKey = this.getCacheKey(bundleName, path); this._cache.set(cacheKey, asset); }); console.log(`[ResMgr] 目录加载成功: ${bundleName}/${dir}, 共 ${assets.length} 个资源`); resolve(assets as T[]); }); }); } /** * 释放资源 * @param bundleName bundle名称 * @param path 资源路径 */ release(bundleName: string, path: string): void { const cacheKey = this.getCacheKey(bundleName, path); // 从缓存中移除 if (this._cache.has(cacheKey)) { const asset = this._cache.get(cacheKey)!; this._cache.delete(cacheKey); // 释放资源 const bundle = this.getBundle(bundleName); if (bundle) { bundle.release(path); console.log(`[ResMgr] 释放资源: ${bundleName}/${path}`); } } } /** * 释放目录资源 * @param bundleName bundle名称 * @param dir 目录路径 */ releaseDir(bundleName: string, dir: string): void { // 释放所有以dir开头的缓存资源 const prefix = `${bundleName}:${dir}`; const keysToDelete: string[] = []; this._cache.forEach((_, key) => { if (key.startsWith(prefix)) { keysToDelete.push(key); } }); keysToDelete.forEach((key) => { this._cache.delete(key); }); // 释放目录 const bundle = this.getBundle(bundleName); if (bundle) { bundle.releaseAll(); console.log(`[ResMgr] 释放目录资源: ${bundleName}/${dir}, 共 ${keysToDelete.length} 个资源`); } } /** * 释放所有资源 */ releaseAll(): void { console.log(`[ResMgr] 释放所有资源, 共 ${this._cache.size} 个`); this._cache.clear(); // 释放所有bundle的资源 this._bundles.forEach((bundle, name) => { if (name !== 'resources') { bundle.releaseAll(); } }); } /** * 获取缓存大小 */ getCacheSize(): number { return this._cache.size; } /** * 清空缓存(不释放资源) */ clearCache(): void { console.log(`[ResMgr] 清空缓存, 共 ${this._cache.size} 个`); this._cache.clear(); } }