接上篇:
小白也能写框架之【四、带加密的数据管理器】
一、直接上菜
1、第一个文件:
assets\Core\Scripts\Managers\ResMgr.ts
import { Asset, AssetManager, assetManager } from "cc";
import { logMgr } from "./LogMgr";
import { bundleMgr } from "./BundleMgr";
import { Parser } from "../Utils/Parser";
/**
* 资源管理器
* 提供资源加载、释放功能。
*/
class ResMgr {
/** 私有构造函数,确保外部无法直接通过new创建实例 */
private constructor() {}
/** 单例实例 */
public static readonly instance: ResMgr = new ResMgr();
/**
* 加载资源
* @param resPath 资源路径
* @param progressFun 进度回调函数
* @param completeFun 完成回调函数
* @returns Promise<T> 加载完成后的Promise
*/
public async loadRes<T extends Asset>(
resPath: string,
progressFun?: (completedCount: number, totalCount: number, item: any) => void,
completeFun?: (err: Error | null, asset: T) => void
): Promise<T> {
try {
const { bundleName, path } = Parser.path(resPath);
const bundle = await bundleMgr.getBundle(bundleName);
return await this.loadAsset<T>(bundle, path, progressFun, completeFun);
} catch (error) {
logMgr.err(`加载资源失败: ${resPath}`, error.message);
throw error;
}
}
/**
* 加载目录下的所有资源
* @param resPath 资源路径
* @param progressFun 进度回调函数
* @param completeFun 完成回调函数
* @returns Promise<Array<Asset>> 加载完成后的Promise
*/
public async loadResDir(
resPath: string,
progressFun?: (completedCount: number, totalCount: number, item: any) => void,
completeFun?: (err: Error | null, assets: Array<Asset>) => void
): Promise<Array<Asset>> {
try {
const { bundleName, path } = Parser.path(resPath);
const bundle = await bundleMgr.getBundle(bundleName);
return await this.loadAssetDir(bundle, path, progressFun, completeFun);
} catch (error) {
logMgr.err(`加载目录失败: ${resPath}`, error.message);
throw error;
}
}
/**
* 释放指定分包单个资源
* @param resPath 资源路径
*/
public releaseRes(resPath: string): void {
const { bundleName, path } = Parser.path(resPath);
const bundle = assetManager.getBundle(bundleName);
if (bundle) {
bundle.release(path);
} else {
logMgr.err(`分包 ${bundleName} 未找到,无法释放资源 ${path}。`);
}
}
/**
* 释放指定分包全部资源
* @param bundleName 分包名称
*/
public releaseBundle(bundleName: string): void {
const bundle = assetManager.getBundle(bundleName);
if (bundle) {
bundle.releaseAll();
assetManager.removeBundle(bundle);
} else {
logMgr.err(`分包 ${bundleName} 未找到,无法移除。`);
}
}
/** 移除所有分包 */
public releaseAll(): void {
assetManager.releaseAll();
}
/**
* 加载单个资源的辅助方法
* @param bundle 资源所在的分包
* @param path 资源路径
* @param progressFun 进度回调函数
* @param completeFun 完成回调函数
* @returns Promise<T> 加载完成后的Promise
*/
private loadAsset<T extends Asset>(
bundle: AssetManager.Bundle,
path: string,
progressFun?: (completedCount: number, totalCount: number, item: any) => void,
completeFun?: (err: Error | null, asset: T) => void
): Promise<T> {
return new Promise<T>((resolve, reject) => {
bundle.load(
path,
(completedCount, totalCount, item) => progressFun?.(completedCount, totalCount, item),
(err, asset) => {
completeFun?.(err, asset as T);
if (err) {
logMgr.err(`从分包加载资源 ${path} 失败`, err.message);
reject(err);
} else {
resolve(asset as T);
}
}
);
});
}
/**
* 加载目录下所有资源的辅助方法
* @param bundle 资源所在的分包
* @param path 目录路径
* @param progressFun 进度回调函数
* @param completeFun 完成回调函数
* @returns Promise<Array<Asset>> 加载完成后的Promise
*/
private loadAssetDir(
bundle: AssetManager.Bundle,
path: string,
progressFun?: (completedCount: number, totalCount: number, item: any) => void,
completeFun?: (err: Error | null, assets: Array<Asset>) => void
): Promise<Array<Asset>> {
return new Promise<Array<Asset>>((resolve, reject) => {
bundle.loadDir(
path,
(completedCount, totalCount, item) => progressFun?.(completedCount, totalCount, item),
(err, assets) => {
completeFun?.(err, assets);
if (err) {
logMgr.err(`从分包加载目录 ${path} 失败`, err.message);
reject(err);
} else {
resolve(assets);
}
}
);
});
}
}
/** 资源管理器实例 */
export const resMgr = ResMgr.instance;
2、第二个文件
assets\Core\Scripts\Utils\Parser.ts
/**
* 解析器
* 提供路径解析功能。
*/
export class Parser {
/**
* 解析资源路径并将其分解为包名和路径
*
* @param resPath - 要解析的资源路径
* @returns 返回包含 bundleName 和 path 的对象
*/
public static path(resPath: string): { bundleName: string; path: string } {
const [bundleName, ...pathParts] = resPath.split('/');
return {
bundleName,
path: pathParts.join('/')
};
}
}
二、全局映射
assets\Core\Scripts\Core.ts
三、使用示例
四、测试效果
五、注意事项
记得资源路径使用正斜杠 /
从cocosIDE复制出来的是反斜杠 \
后面实际开发会开源一个一键获取资源路径的插件,并且自动在对应分包生成资源索引,这样就不用自己手动复制资源路径了