From 0a30c2831661fba28802de5bb0275a31c72db6fc Mon Sep 17 00:00:00 2001 From: janing Date: Sun, 14 Dec 2025 22:40:00 +0800 Subject: [PATCH] =?UTF-8?q?Framework.ResMgr=E8=B5=84=E6=BA=90=E7=AE=A1?= =?UTF-8?q?=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../assets/scripts/Framework/ResMgr/README.md | 356 ++++++++++++++++++ .../scripts/Framework/ResMgr/ResConfig.ts | 97 +++++ .../Framework/ResMgr/ResConfig.ts.meta | 9 + .../assets/scripts/Framework/ResMgr/ResMgr.ts | 293 ++++++++++++++ .../scripts/Framework/ResMgr/ResMgr.ts.meta | 9 + .../scripts/Framework/ResMgr/ResMgrExample.ts | 291 ++++++++++++++ .../Framework/ResMgr/ResMgrExample.ts.meta | 9 + 7 files changed, 1064 insertions(+) create mode 100644 client/assets/scripts/Framework/ResMgr/README.md create mode 100644 client/assets/scripts/Framework/ResMgr/ResConfig.ts create mode 100644 client/assets/scripts/Framework/ResMgr/ResConfig.ts.meta create mode 100644 client/assets/scripts/Framework/ResMgr/ResMgr.ts create mode 100644 client/assets/scripts/Framework/ResMgr/ResMgr.ts.meta create mode 100644 client/assets/scripts/Framework/ResMgr/ResMgrExample.ts create mode 100644 client/assets/scripts/Framework/ResMgr/ResMgrExample.ts.meta diff --git a/client/assets/scripts/Framework/ResMgr/README.md b/client/assets/scripts/Framework/ResMgr/README.md new file mode 100644 index 0000000..62a72e9 --- /dev/null +++ b/client/assets/scripts/Framework/ResMgr/README.md @@ -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( + bundleName: string, + path: string, + type: typeof Asset + ): Promise; + + // 预加载资源(不实例化) + preload( + bundleName: string, + path: string, + type: typeof Asset, + onProgress?: (finished: number, total: number) => void + ): Promise; + + // 加载目录 + loadDir( + bundleName: string, + dir: string, + type: typeof Asset + ): Promise; + + // 释放单个资源 + 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( + 'resources', + 'prefabs/Player', + Prefab +); + +// 加载图片 +const spriteFrame = await ResMgr.getInstance().load( + 'resources', + 'textures/icon', + SpriteFrame +); + +// 加载音频 +const audioClip = await ResMgr.getInstance().load( + 'resources', + 'audio/bgm', + AudioClip +); +``` + +### 2. 预加载资源 + +```typescript +import { ResMgr } from './Framework/ResMgr/ResMgr'; +import { Prefab } from 'cc'; + +// 预加载资源(带进度回调) +await ResMgr.getInstance().preload( + 'resources', + 'prefabs/Enemy', + Prefab, + (finished, total) => { + const progress = (finished / total * 100).toFixed(0); + console.log(`预加载进度: ${progress}%`); + } +); + +// 预加载完成后再实际加载 +const prefab = await ResMgr.getInstance().load( + 'resources', + 'prefabs/Enemy', + Prefab +); +``` + +### 3. 加载目录 + +```typescript +import { ResMgr } from './Framework/ResMgr/ResMgr'; +import { SpriteFrame } from 'cc'; + +// 加载整个目录的资源 +const sprites = await ResMgr.getInstance().loadDir( + '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 { + 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 资源加载(界面预制体、图标等) +- ✅ 音频资源加载(背景音乐、音效等) +- ✅ 关卡资源动态加载 +- ✅ 资源热更新 +- ✅ 分包加载 diff --git a/client/assets/scripts/Framework/ResMgr/ResConfig.ts b/client/assets/scripts/Framework/ResMgr/ResConfig.ts new file mode 100644 index 0000000..751ec29 --- /dev/null +++ b/client/assets/scripts/Framework/ResMgr/ResConfig.ts @@ -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' +} diff --git a/client/assets/scripts/Framework/ResMgr/ResConfig.ts.meta b/client/assets/scripts/Framework/ResMgr/ResConfig.ts.meta new file mode 100644 index 0000000..128ff3f --- /dev/null +++ b/client/assets/scripts/Framework/ResMgr/ResConfig.ts.meta @@ -0,0 +1,9 @@ +{ + "ver": "4.0.24", + "importer": "typescript", + "imported": true, + "uuid": "8f4e19f5-9ef9-4b66-8fbb-257f4af92170", + "files": [], + "subMetas": {}, + "userData": {} +} diff --git a/client/assets/scripts/Framework/ResMgr/ResMgr.ts b/client/assets/scripts/Framework/ResMgr/ResMgr.ts new file mode 100644 index 0000000..e6580c0 --- /dev/null +++ b/client/assets/scripts/Framework/ResMgr/ResMgr.ts @@ -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; + + /** + * 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(); + } +} diff --git a/client/assets/scripts/Framework/ResMgr/ResMgr.ts.meta b/client/assets/scripts/Framework/ResMgr/ResMgr.ts.meta new file mode 100644 index 0000000..71cc8e3 --- /dev/null +++ b/client/assets/scripts/Framework/ResMgr/ResMgr.ts.meta @@ -0,0 +1,9 @@ +{ + "ver": "4.0.24", + "importer": "typescript", + "imported": true, + "uuid": "30402092-c485-4900-8888-8e2f71682bd3", + "files": [], + "subMetas": {}, + "userData": {} +} diff --git a/client/assets/scripts/Framework/ResMgr/ResMgrExample.ts b/client/assets/scripts/Framework/ResMgr/ResMgrExample.ts new file mode 100644 index 0000000..3cb6c4b --- /dev/null +++ b/client/assets/scripts/Framework/ResMgr/ResMgrExample.ts @@ -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 { + 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 { + 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 { + 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 { + 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 { + 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 = new Map(); + + /** + * 进入场景 - 加载场景资源 + */ + async onEnterScene(): Promise { + 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 { + // 预加载特效预制体 + 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 { + 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("========================================"); +} diff --git a/client/assets/scripts/Framework/ResMgr/ResMgrExample.ts.meta b/client/assets/scripts/Framework/ResMgr/ResMgrExample.ts.meta new file mode 100644 index 0000000..ad33cda --- /dev/null +++ b/client/assets/scripts/Framework/ResMgr/ResMgrExample.ts.meta @@ -0,0 +1,9 @@ +{ + "ver": "4.0.24", + "importer": "typescript", + "imported": true, + "uuid": "b20f37a0-5ba0-4a08-83d5-00f2090cd5a4", + "files": [], + "subMetas": {}, + "userData": {} +}