Framework.ResMgr资源管理
This commit is contained in:
356
client/assets/scripts/Framework/ResMgr/README.md
Normal file
356
client/assets/scripts/Framework/ResMgr/README.md
Normal file
@@ -0,0 +1,356 @@
|
||||
# 资源管理模块 (Framework/ResMgr)
|
||||
|
||||
## 📋 模块概述
|
||||
统一的资源加载管理器,封装 Cocos Creator 的资源加载 API,支持从 bundle 中按路径加载资源,提供资源缓存和释放机制。
|
||||
|
||||
## 🎯 核心特性
|
||||
- ✅ 单例模式,全局统一管理
|
||||
- ✅ 资源缓存机制
|
||||
- ✅ Bundle 管理
|
||||
- ✅ 支持单个资源加载
|
||||
- ✅ 支持资源预加载
|
||||
- ✅ 支持目录批量加载
|
||||
- ✅ 资源释放管理
|
||||
- ✅ 完整的日志输出
|
||||
|
||||
## 🗂️ 文件结构
|
||||
|
||||
```
|
||||
Framework/ResMgr/
|
||||
├── ResMgr.ts # 资源管理器核心
|
||||
├── ResConfig.ts # 资源配置
|
||||
└── ResMgrExample.ts # 使用示例
|
||||
```
|
||||
|
||||
## 📘 核心类详解
|
||||
|
||||
### ResMgr - 资源管理器
|
||||
|
||||
**职责**: 管理资源加载、缓存和释放
|
||||
|
||||
```typescript
|
||||
class ResMgr {
|
||||
// 获取单例
|
||||
static getInstance(): ResMgr;
|
||||
|
||||
// 加载单个资源
|
||||
load<T extends Asset>(
|
||||
bundleName: string,
|
||||
path: string,
|
||||
type: typeof Asset
|
||||
): Promise<T>;
|
||||
|
||||
// 预加载资源(不实例化)
|
||||
preload<T extends Asset>(
|
||||
bundleName: string,
|
||||
path: string,
|
||||
type: typeof Asset,
|
||||
onProgress?: (finished: number, total: number) => void
|
||||
): Promise<void>;
|
||||
|
||||
// 加载目录
|
||||
loadDir<T extends Asset>(
|
||||
bundleName: string,
|
||||
dir: string,
|
||||
type: typeof Asset
|
||||
): Promise<T[]>;
|
||||
|
||||
// 释放单个资源
|
||||
release(bundleName: string, path: string): void;
|
||||
|
||||
// 释放目录资源
|
||||
releaseDir(bundleName: string, dir: string): void;
|
||||
|
||||
// 释放所有资源
|
||||
releaseAll(): void;
|
||||
|
||||
// 获取缓存大小
|
||||
getCacheSize(): number;
|
||||
}
|
||||
```
|
||||
|
||||
### ResConfig - 资源配置
|
||||
|
||||
**配置接口**:
|
||||
|
||||
```typescript
|
||||
// 资源加载配置
|
||||
interface ResLoadConfig {
|
||||
showProgress?: boolean; // 是否显示加载进度
|
||||
timeout?: number; // 加载超时时间(ms)
|
||||
retryCount?: number; // 失败重试次数
|
||||
}
|
||||
|
||||
// 预加载配置
|
||||
interface PreloadConfig extends ResLoadConfig {
|
||||
onProgress?: (finished: number, total: number) => void;
|
||||
}
|
||||
|
||||
// 常用资源路径
|
||||
class ResPath {
|
||||
static readonly PREFAB = 'prefabs';
|
||||
static readonly TEXTURE = 'textures';
|
||||
static readonly AUDIO = 'audio';
|
||||
static readonly SCENE = 'scenes';
|
||||
}
|
||||
|
||||
// 资源类型枚举
|
||||
enum ResType {
|
||||
Prefab = 'Prefab',
|
||||
Texture = 'Texture',
|
||||
Audio = 'Audio',
|
||||
Scene = 'Scene'
|
||||
}
|
||||
```
|
||||
|
||||
## 📝 使用指南
|
||||
|
||||
### 1. 加载单个资源
|
||||
|
||||
```typescript
|
||||
import { ResMgr } from './Framework/ResMgr/ResMgr';
|
||||
import { Prefab, SpriteFrame, AudioClip } from 'cc';
|
||||
|
||||
// 加载预制体
|
||||
const prefab = await ResMgr.getInstance().load<Prefab>(
|
||||
'resources',
|
||||
'prefabs/Player',
|
||||
Prefab
|
||||
);
|
||||
|
||||
// 加载图片
|
||||
const spriteFrame = await ResMgr.getInstance().load<SpriteFrame>(
|
||||
'resources',
|
||||
'textures/icon',
|
||||
SpriteFrame
|
||||
);
|
||||
|
||||
// 加载音频
|
||||
const audioClip = await ResMgr.getInstance().load<AudioClip>(
|
||||
'resources',
|
||||
'audio/bgm',
|
||||
AudioClip
|
||||
);
|
||||
```
|
||||
|
||||
### 2. 预加载资源
|
||||
|
||||
```typescript
|
||||
import { ResMgr } from './Framework/ResMgr/ResMgr';
|
||||
import { Prefab } from 'cc';
|
||||
|
||||
// 预加载资源(带进度回调)
|
||||
await ResMgr.getInstance().preload<Prefab>(
|
||||
'resources',
|
||||
'prefabs/Enemy',
|
||||
Prefab,
|
||||
(finished, total) => {
|
||||
const progress = (finished / total * 100).toFixed(0);
|
||||
console.log(`预加载进度: ${progress}%`);
|
||||
}
|
||||
);
|
||||
|
||||
// 预加载完成后再实际加载
|
||||
const prefab = await ResMgr.getInstance().load<Prefab>(
|
||||
'resources',
|
||||
'prefabs/Enemy',
|
||||
Prefab
|
||||
);
|
||||
```
|
||||
|
||||
### 3. 加载目录
|
||||
|
||||
```typescript
|
||||
import { ResMgr } from './Framework/ResMgr/ResMgr';
|
||||
import { SpriteFrame } from 'cc';
|
||||
|
||||
// 加载整个目录的资源
|
||||
const sprites = await ResMgr.getInstance().loadDir<SpriteFrame>(
|
||||
'resources',
|
||||
'textures/ui',
|
||||
SpriteFrame
|
||||
);
|
||||
|
||||
console.log(`加载了 ${sprites.length} 个图片资源`);
|
||||
|
||||
// 使用加载的资源
|
||||
for (const sprite of sprites) {
|
||||
console.log(`资源名称: ${sprite.name}`);
|
||||
}
|
||||
```
|
||||
|
||||
### 4. 释放资源
|
||||
|
||||
```typescript
|
||||
import { ResMgr } from './Framework/ResMgr/ResMgr';
|
||||
|
||||
// 释放单个资源
|
||||
ResMgr.getInstance().release('resources', 'prefabs/Player');
|
||||
|
||||
// 释放目录资源
|
||||
ResMgr.getInstance().releaseDir('resources', 'textures/ui');
|
||||
|
||||
// 释放所有资源(场景切换时)
|
||||
ResMgr.getInstance().releaseAll();
|
||||
```
|
||||
|
||||
### 5. 游戏场景资源管理
|
||||
|
||||
```typescript
|
||||
// 场景资源管理器
|
||||
class SceneResMgr {
|
||||
private _loadedRes: string[] = [];
|
||||
|
||||
// 加载场景所需资源
|
||||
async loadSceneRes(): Promise<void> {
|
||||
const resMgr = ResMgr.getInstance();
|
||||
|
||||
// 加载角色
|
||||
await resMgr.load('resources', 'prefabs/Player', Prefab);
|
||||
this._loadedRes.push('prefabs/Player');
|
||||
|
||||
// 加载敌人
|
||||
await resMgr.load('resources', 'prefabs/Enemy', Prefab);
|
||||
this._loadedRes.push('prefabs/Enemy');
|
||||
|
||||
// 加载UI
|
||||
const uiSprites = await resMgr.loadDir(
|
||||
'resources',
|
||||
'textures/ui',
|
||||
SpriteFrame
|
||||
);
|
||||
this._loadedRes.push('textures/ui');
|
||||
|
||||
console.log(`场景资源加载完成,共 ${this._loadedRes.length} 项`);
|
||||
}
|
||||
|
||||
// 释放场景资源
|
||||
releaseSceneRes(): void {
|
||||
const resMgr = ResMgr.getInstance();
|
||||
|
||||
for (const resPath of this._loadedRes) {
|
||||
if (resPath.endsWith('/')) {
|
||||
resMgr.releaseDir('resources', resPath);
|
||||
} else {
|
||||
resMgr.release('resources', resPath);
|
||||
}
|
||||
}
|
||||
|
||||
this._loadedRes = [];
|
||||
console.log('场景资源已释放');
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 🔄 资源加载流程
|
||||
|
||||
```
|
||||
ResMgr.load(bundleName, path, type)
|
||||
↓
|
||||
1. 检查缓存
|
||||
↓
|
||||
2. 如果已缓存,直接返回
|
||||
↓
|
||||
3. 通过 assetManager 加载 bundle
|
||||
↓
|
||||
4. 从 bundle 加载资源
|
||||
↓
|
||||
5. 存入缓存
|
||||
↓
|
||||
6. 返回资源实例
|
||||
```
|
||||
|
||||
## 📊 资源生命周期
|
||||
|
||||
```
|
||||
[请求加载资源]
|
||||
↓
|
||||
load() / preload() / loadDir()
|
||||
↓
|
||||
资源加载到内存
|
||||
↓
|
||||
缓存资源引用
|
||||
↓
|
||||
[使用资源]
|
||||
↓
|
||||
release() / releaseDir() / releaseAll()
|
||||
↓
|
||||
资源从内存释放
|
||||
```
|
||||
|
||||
## ⚠️ 注意事项
|
||||
|
||||
1. **Bundle 必须存在**: 确保 bundleName 对应的 bundle 已在 Cocos 中配置
|
||||
2. **路径不含扩展名**: 资源路径不需要包含文件扩展名
|
||||
3. **类型要匹配**: 加载时的 type 参数必须与实际资源类型匹配
|
||||
4. **及时释放资源**: 不再使用的资源应及时释放,避免内存泄漏
|
||||
5. **目录加载限制**: loadDir 只加载指定目录下的资源,不递归子目录
|
||||
6. **缓存机制**: 相同路径的资源会被缓存,重复加载返回缓存实例
|
||||
|
||||
## 🔍 调试技巧
|
||||
|
||||
### 查看缓存大小
|
||||
|
||||
```typescript
|
||||
const cacheSize = ResMgr.getInstance().getCacheSize();
|
||||
console.log(`当前缓存资源数量: ${cacheSize}`);
|
||||
```
|
||||
|
||||
### 日志输出
|
||||
|
||||
```typescript
|
||||
// ResMgr 内部包含详细日志:
|
||||
// [ResMgr] 加载资源: bundleName/path
|
||||
// [ResMgr] 从缓存加载: bundleName/path
|
||||
// [ResMgr] 释放资源: bundleName/path
|
||||
// [ResMgr] 释放目录: bundleName/dir
|
||||
// [ResMgr] 释放所有资源
|
||||
```
|
||||
|
||||
### 常见问题
|
||||
|
||||
**问题1**: 资源加载失败
|
||||
```typescript
|
||||
// 检查: bundle 是否存在、路径是否正确、类型是否匹配
|
||||
try {
|
||||
const prefab = await ResMgr.getInstance().load('resources', 'prefabs/Player', Prefab);
|
||||
} catch (error) {
|
||||
console.error('资源加载失败:', error);
|
||||
}
|
||||
```
|
||||
|
||||
**问题2**: 内存占用过高
|
||||
```typescript
|
||||
// 解决: 及时释放不用的资源
|
||||
ResMgr.getInstance().releaseAll();
|
||||
```
|
||||
|
||||
## 💡 最佳实践
|
||||
|
||||
1. **场景切换时释放**: 切换场景时调用 `releaseAll()` 清理资源
|
||||
2. **按需加载**: 不要一次性加载所有资源,按需加载
|
||||
3. **预加载优化**: 对大资源使用 `preload()` 在空闲时预加载
|
||||
4. **目录管理**: 合理组织资源目录结构,方便批量加载和释放
|
||||
5. **资源复用**: 相同资源重复使用时,利用缓存机制避免重复加载
|
||||
6. **错误处理**: 资源加载使用 try-catch 处理异常情况
|
||||
|
||||
## 📚 常用资源类型
|
||||
|
||||
| 资源类型 | Cocos 类型 | 用途 |
|
||||
|---------|----------|------|
|
||||
| 预制体 | Prefab | 场景节点、UI 等 |
|
||||
| 图片 | SpriteFrame | 精灵图、UI图标 |
|
||||
| 音频 | AudioClip | 音效、背景音乐 |
|
||||
| 场景 | SceneAsset | 游戏场景 |
|
||||
| 字体 | Font | 文本渲染 |
|
||||
| 图集 | SpriteAtlas | 批量图片资源 |
|
||||
| 动画 | AnimationClip | 动画数据 |
|
||||
|
||||
## 🎯 应用场景
|
||||
|
||||
- ✅ 场景资源加载(角色、道具、环境等)
|
||||
- ✅ UI 资源加载(界面预制体、图标等)
|
||||
- ✅ 音频资源加载(背景音乐、音效等)
|
||||
- ✅ 关卡资源动态加载
|
||||
- ✅ 资源热更新
|
||||
- ✅ 分包加载
|
||||
97
client/assets/scripts/Framework/ResMgr/ResConfig.ts
Normal file
97
client/assets/scripts/Framework/ResMgr/ResConfig.ts
Normal file
@@ -0,0 +1,97 @@
|
||||
/**
|
||||
* 资源加载配置
|
||||
*/
|
||||
export interface ResLoadConfig {
|
||||
/** 是否显示加载进度 */
|
||||
showProgress?: boolean;
|
||||
|
||||
/** 加载超时时间(ms) */
|
||||
timeout?: number;
|
||||
|
||||
/** 失败重试次数 */
|
||||
retryCount?: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* 预加载配置
|
||||
*/
|
||||
export interface PreloadConfig extends ResLoadConfig {
|
||||
/** 进度回调 */
|
||||
onProgress?: (finished: number, total: number) => void;
|
||||
}
|
||||
|
||||
/**
|
||||
* 默认资源加载配置
|
||||
*/
|
||||
export const DefaultResConfig: ResLoadConfig = {
|
||||
showProgress: false,
|
||||
timeout: 30000, // 30秒超时
|
||||
retryCount: 3 // 重试3次
|
||||
};
|
||||
|
||||
/**
|
||||
* Bundle配置
|
||||
*/
|
||||
export interface BundleConfig {
|
||||
/** Bundle名称 */
|
||||
name: string;
|
||||
|
||||
/** Bundle路径(可选,默认从远程服务器加载) */
|
||||
url?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* 资源路径配置
|
||||
* 统一管理常用资源路径
|
||||
*/
|
||||
export class ResPath {
|
||||
/** UI预制体路径 */
|
||||
static readonly UI_PREFAB = 'prefabs/ui';
|
||||
|
||||
/** 角色预制体路径 */
|
||||
static readonly ACTOR_PREFAB = 'prefabs/actor';
|
||||
|
||||
/** 特效预制体路径 */
|
||||
static readonly EFFECT_PREFAB = 'prefabs/effect';
|
||||
|
||||
/** UI纹理路径 */
|
||||
static readonly UI_TEXTURE = 'textures/ui';
|
||||
|
||||
/** 角色纹理路径 */
|
||||
static readonly ACTOR_TEXTURE = 'textures/actor';
|
||||
|
||||
/** 音效路径 */
|
||||
static readonly AUDIO_EFFECT = 'audio/effect';
|
||||
|
||||
/** 背景音乐路径 */
|
||||
static readonly AUDIO_MUSIC = 'audio/music';
|
||||
}
|
||||
|
||||
/**
|
||||
* 资源类型枚举
|
||||
*/
|
||||
export enum ResType {
|
||||
/** 预制体 */
|
||||
PREFAB = 'Prefab',
|
||||
|
||||
/** 纹理 */
|
||||
TEXTURE = 'Texture2D',
|
||||
|
||||
/** 精灵帧 */
|
||||
SPRITE_FRAME = 'SpriteFrame',
|
||||
|
||||
/** 音频片段 */
|
||||
AUDIO_CLIP = 'AudioClip',
|
||||
|
||||
/** JSON */
|
||||
JSON = 'JsonAsset',
|
||||
|
||||
/** 文本 */
|
||||
TEXT = 'TextAsset',
|
||||
|
||||
/** 材质 */
|
||||
MATERIAL = 'Material',
|
||||
|
||||
/** 动画片段 */
|
||||
ANIMATION_CLIP = 'AnimationClip'
|
||||
}
|
||||
9
client/assets/scripts/Framework/ResMgr/ResConfig.ts.meta
Normal file
9
client/assets/scripts/Framework/ResMgr/ResConfig.ts.meta
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"ver": "4.0.24",
|
||||
"importer": "typescript",
|
||||
"imported": true,
|
||||
"uuid": "8f4e19f5-9ef9-4b66-8fbb-257f4af92170",
|
||||
"files": [],
|
||||
"subMetas": {},
|
||||
"userData": {}
|
||||
}
|
||||
293
client/assets/scripts/Framework/ResMgr/ResMgr.ts
Normal file
293
client/assets/scripts/Framework/ResMgr/ResMgr.ts
Normal file
@@ -0,0 +1,293 @@
|
||||
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<string, Asset>;
|
||||
|
||||
/**
|
||||
* Bundle缓存
|
||||
* key: bundleName
|
||||
* value: AssetManager.Bundle
|
||||
*/
|
||||
private _bundles: Map<string, AssetManager.Bundle>;
|
||||
|
||||
/**
|
||||
* 构造函数(私有)
|
||||
*/
|
||||
private constructor() {
|
||||
this._cache = new Map<string, Asset>();
|
||||
this._bundles = new Map<string, AssetManager.Bundle>();
|
||||
|
||||
// 默认添加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<T extends Asset>(
|
||||
bundleName: string,
|
||||
path: string,
|
||||
type: new (...args: any[]) => T
|
||||
): Promise<T> {
|
||||
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<void>
|
||||
*/
|
||||
preload<T extends Asset>(
|
||||
bundleName: string,
|
||||
path: string,
|
||||
type: new (...args: any[]) => T,
|
||||
onProgress?: (finished: number, total: number) => void
|
||||
): Promise<void> {
|
||||
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<T extends Asset>(
|
||||
bundleName: string,
|
||||
dir: string,
|
||||
type: new (...args: any[]) => T
|
||||
): Promise<T[]> {
|
||||
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();
|
||||
}
|
||||
}
|
||||
9
client/assets/scripts/Framework/ResMgr/ResMgr.ts.meta
Normal file
9
client/assets/scripts/Framework/ResMgr/ResMgr.ts.meta
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"ver": "4.0.24",
|
||||
"importer": "typescript",
|
||||
"imported": true,
|
||||
"uuid": "30402092-c485-4900-8888-8e2f71682bd3",
|
||||
"files": [],
|
||||
"subMetas": {},
|
||||
"userData": {}
|
||||
}
|
||||
291
client/assets/scripts/Framework/ResMgr/ResMgrExample.ts
Normal file
291
client/assets/scripts/Framework/ResMgr/ResMgrExample.ts
Normal file
@@ -0,0 +1,291 @@
|
||||
import { ResMgr } from "./ResMgr";
|
||||
import { ResPath } from "./ResConfig";
|
||||
import { Prefab, SpriteFrame, AudioClip, instantiate, Node } from 'cc';
|
||||
|
||||
/**
|
||||
* ResMgr 资源管理器使用示例
|
||||
*/
|
||||
|
||||
// ========== 示例1: 加载单个资源 ==========
|
||||
|
||||
/**
|
||||
* 加载UI预制体
|
||||
*/
|
||||
export async function loadUIPrefab(): Promise<void> {
|
||||
try {
|
||||
console.log("========== 加载UI预制体 ==========");
|
||||
|
||||
// 加载预制体
|
||||
const prefab = await ResMgr.getInstance().load(
|
||||
'resources',
|
||||
'prefabs/ui/LoginUI',
|
||||
Prefab
|
||||
);
|
||||
|
||||
console.log("预制体加载成功:", prefab.name);
|
||||
|
||||
// 实例化预制体
|
||||
const node = instantiate(prefab);
|
||||
console.log("预制体实例化成功");
|
||||
|
||||
} catch (error) {
|
||||
console.error("加载UI预制体失败:", error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 加载图片资源
|
||||
*/
|
||||
export async function loadSprite(): Promise<void> {
|
||||
try {
|
||||
console.log("========== 加载图片资源 ==========");
|
||||
|
||||
// 加载精灵帧
|
||||
const spriteFrame = await ResMgr.getInstance().load(
|
||||
'resources',
|
||||
'textures/ui/icon_player',
|
||||
SpriteFrame
|
||||
);
|
||||
|
||||
console.log("图片加载成功:", spriteFrame.name);
|
||||
|
||||
} catch (error) {
|
||||
console.error("加载图片失败:", error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 加载音频资源
|
||||
*/
|
||||
export async function loadAudio(): Promise<void> {
|
||||
try {
|
||||
console.log("========== 加载音频资源 ==========");
|
||||
|
||||
// 加载音频片段
|
||||
const audioClip = await ResMgr.getInstance().load(
|
||||
'resources',
|
||||
'audio/music/bg_music',
|
||||
AudioClip
|
||||
);
|
||||
|
||||
console.log("音频加载成功:", audioClip.name);
|
||||
|
||||
} catch (error) {
|
||||
console.error("加载音频失败:", error);
|
||||
}
|
||||
}
|
||||
|
||||
// ========== 示例2: 预加载资源 ==========
|
||||
|
||||
/**
|
||||
* 预加载关卡资源
|
||||
*/
|
||||
export async function preloadLevel(): Promise<void> {
|
||||
try {
|
||||
console.log("========== 预加载关卡资源 ==========");
|
||||
|
||||
// 预加载多个资源
|
||||
const resources = [
|
||||
'prefabs/enemy/Enemy1',
|
||||
'prefabs/enemy/Enemy2',
|
||||
'prefabs/effect/Explosion'
|
||||
];
|
||||
|
||||
for (const path of resources) {
|
||||
await ResMgr.getInstance().preload(
|
||||
'resources',
|
||||
path,
|
||||
Prefab,
|
||||
(finished, total) => {
|
||||
const progress = (finished / total * 100).toFixed(0);
|
||||
console.log(`预加载 ${path}: ${progress}%`);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
console.log("关卡资源预加载完成");
|
||||
|
||||
} catch (error) {
|
||||
console.error("预加载失败:", error);
|
||||
}
|
||||
}
|
||||
|
||||
// ========== 示例3: 加载目录 ==========
|
||||
|
||||
/**
|
||||
* 加载所有UI图标
|
||||
*/
|
||||
export async function loadAllUIIcons(): Promise<void> {
|
||||
try {
|
||||
console.log("========== 加载UI图标目录 ==========");
|
||||
|
||||
// 加载整个目录
|
||||
const sprites = await ResMgr.getInstance().loadDir(
|
||||
'resources',
|
||||
ResPath.UI_TEXTURE + '/icons',
|
||||
SpriteFrame
|
||||
);
|
||||
|
||||
console.log(`加载了 ${sprites.length} 个UI图标`);
|
||||
|
||||
// 遍历所有图标
|
||||
sprites.forEach((sprite, index) => {
|
||||
console.log(`图标 ${index + 1}: ${sprite.name}`);
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
console.error("加载目录失败:", error);
|
||||
}
|
||||
}
|
||||
|
||||
// ========== 示例4: 资源释放 ==========
|
||||
|
||||
/**
|
||||
* 释放不需要的资源
|
||||
*/
|
||||
export function releaseUnusedResources(): void {
|
||||
console.log("========== 释放资源 ==========");
|
||||
|
||||
// 释放单个资源
|
||||
ResMgr.getInstance().release('resources', 'prefabs/ui/LoginUI');
|
||||
|
||||
// 释放目录资源
|
||||
ResMgr.getInstance().releaseDir('resources', 'textures/ui/icons');
|
||||
|
||||
console.log("资源释放完成");
|
||||
}
|
||||
|
||||
// ========== 示例5: 完整的资源管理流程 ==========
|
||||
|
||||
/**
|
||||
* 游戏场景资源管理示例
|
||||
*/
|
||||
export class GameSceneResourceManager {
|
||||
private loadedPrefabs: Map<string, Prefab> = new Map();
|
||||
|
||||
/**
|
||||
* 进入场景 - 加载场景资源
|
||||
*/
|
||||
async onEnterScene(): Promise<void> {
|
||||
console.log("========== 进入游戏场景 ==========");
|
||||
|
||||
try {
|
||||
// 1. 预加载场景资源
|
||||
await this.preloadSceneResources();
|
||||
|
||||
// 2. 加载玩家预制体
|
||||
const playerPrefab = await ResMgr.getInstance().load(
|
||||
'resources',
|
||||
'prefabs/actor/Player',
|
||||
Prefab
|
||||
);
|
||||
this.loadedPrefabs.set('Player', playerPrefab);
|
||||
|
||||
// 3. 加载敌人预制体
|
||||
const enemies = await ResMgr.getInstance().loadDir(
|
||||
'resources',
|
||||
'prefabs/enemy',
|
||||
Prefab
|
||||
);
|
||||
enemies.forEach(prefab => {
|
||||
this.loadedPrefabs.set(prefab.name, prefab);
|
||||
});
|
||||
|
||||
console.log("场景资源加载完成");
|
||||
|
||||
} catch (error) {
|
||||
console.error("场景资源加载失败:", error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 预加载场景资源
|
||||
*/
|
||||
private async preloadSceneResources(): Promise<void> {
|
||||
// 预加载特效预制体
|
||||
const effectResources = [
|
||||
'prefabs/effect/Hit',
|
||||
'prefabs/effect/Explosion'
|
||||
];
|
||||
|
||||
for (const path of effectResources) {
|
||||
await ResMgr.getInstance().preload(
|
||||
'resources',
|
||||
path,
|
||||
Prefab
|
||||
);
|
||||
}
|
||||
|
||||
// 预加载音频
|
||||
const audioResources = [
|
||||
'audio/effect/hit',
|
||||
'audio/effect/explosion'
|
||||
];
|
||||
|
||||
for (const path of audioResources) {
|
||||
await ResMgr.getInstance().preload(
|
||||
'resources',
|
||||
path,
|
||||
AudioClip
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取已加载的预制体
|
||||
*/
|
||||
getPrefab(name: string): Prefab | undefined {
|
||||
return this.loadedPrefabs.get(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* 离开场景 - 释放场景资源
|
||||
*/
|
||||
onLeaveScene(): void {
|
||||
console.log("========== 离开游戏场景 ==========");
|
||||
|
||||
// 清空预制体缓存
|
||||
this.loadedPrefabs.clear();
|
||||
|
||||
// 释放场景相关资源
|
||||
ResMgr.getInstance().releaseDir('resources', 'prefabs/actor');
|
||||
ResMgr.getInstance().releaseDir('resources', 'prefabs/enemy');
|
||||
ResMgr.getInstance().releaseDir('resources', 'prefabs/effect');
|
||||
|
||||
console.log("场景资源释放完成");
|
||||
console.log(`缓存大小: ${ResMgr.getInstance().getCacheSize()}`);
|
||||
}
|
||||
}
|
||||
|
||||
// ========== 测试函数 ==========
|
||||
|
||||
/**
|
||||
* 测试资源管理器
|
||||
*/
|
||||
export async function testResMgr(): Promise<void> {
|
||||
console.log("========================================");
|
||||
console.log(" 开始测试 ResMgr 资源管理器");
|
||||
console.log("========================================");
|
||||
|
||||
// 测试1: 加载单个资源
|
||||
await loadUIPrefab();
|
||||
await loadSprite();
|
||||
|
||||
// 测试2: 预加载资源
|
||||
await preloadLevel();
|
||||
|
||||
// 测试3: 加载目录
|
||||
await loadAllUIIcons();
|
||||
|
||||
// 查看缓存大小
|
||||
console.log(`当前缓存资源数量: ${ResMgr.getInstance().getCacheSize()}`);
|
||||
|
||||
// 测试4: 释放资源
|
||||
releaseUnusedResources();
|
||||
|
||||
console.log(`释放后缓存资源数量: ${ResMgr.getInstance().getCacheSize()}`);
|
||||
|
||||
console.log("========================================");
|
||||
console.log(" ResMgr 测试完成");
|
||||
console.log("========================================");
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"ver": "4.0.24",
|
||||
"importer": "typescript",
|
||||
"imported": true,
|
||||
"uuid": "b20f37a0-5ba0-4a08-83d5-00f2090cd5a4",
|
||||
"files": [],
|
||||
"subMetas": {},
|
||||
"userData": {}
|
||||
}
|
||||
Reference in New Issue
Block a user