cocos creator主程入门教程(三)—— 资源管理
五邑隐侠,本名关健昌,10年游戏生涯,现隐居五邑。本系列文章以TypeScript为介绍语言。
在初识篇,我介绍过怎样加载prefab。cocos提供了一系列的加载接口,包括cc.loader.load,cc.loader.loadRes,cc.loader.loadResArray,cc.loader.loadResDir。
static load(resources: string|string[]|{uuid?: string, url?: string, type?: string}, completeCallback?: Function): void; static loadRes(url: string, type: typeof cc.Asset, completeCallback: (error: Error, resource: any) => void): void; static loadResArray(url: string[], type: typeof cc.Asset, progressCallback: (completedCount: number, totalCount: number, item: any) => void, completeCallback: ((error: Error, resource: any[]) => void)|null): void; static loadResDir(url: string, type: typeof cc.Asset, progressCallback: (completedCount: number, totalCount: number, item: any) => void, completeCallback: ((error: Error, resource: any[], urls: string[]) => void)|null): void;
这些接口除了加载资源外,也负责资源管理。所以,在界面被销毁时,如果在cc.loader里还有该资源,资源是不会释放的。对于资源的管理,有两种方式,一种是资源加载后,cc.loader不管理资源,通过界面的引用来确定是否销毁资源。一种是cc.loader管理资源,界面使用资源,在模块退出时,通过cc.loader销毁资源。我偏向于第二种方式,这样避免依赖内存gc,资源可以得到即时释放。为了避免错误释放资源,在资源管理模块对加载的资源设置引用计数,引用计数为0时才实际销毁。
下面说说这几个接口的使用场景:
1.cc.loader.load用于加载第三方远程资源,在游戏中一般用于加载第三方平台的头像资源,如果该资源的链接没有文件后缀名,需要加参数{type:"png"}。
2.cc.loader.loadRes用于加载assets/resources目录下单个资源
3.cc.loader.loadResArray用于批量加载assets/resources目录下资源,比较适合于进度条加载界面,通过进度变化更新进度条。
4.cc.loader.loadResDir用于加载assets/resources目录下单个目录的资源,一般我会把单个spine骨骼动画放在一个目录,把一个界面的资源放在一个目录。这样就可以通过这个接口加载单个spine动画或者一个界面的资源。
在初识篇提到,我们建立assets/resources目录用于存放资源,目的是可以通过上述除了cc.loader.load外的接口加载资源,简化使用。
资源加载管理模块,可以划分为ResLoader、ViewLoader。其中ResLoader负责基础资源加载,另外提供超时、重试机制。ViewLoader负责对加载的prefab、重用界面的node进行缓存管理。这类工具性的类,我都习惯做成单例,一来游戏里只需要一个对象,另外单例有利于这些对象可以全局访问。
ResLoader,封装cc.loader上述几个接口,以及对应的释放接口。
超时实现:设置回调控制变量,settimeout回调中设置变量,并调用超时回调,在成功失败处理中判断变量是否触发成功失败的回调。由于cc.loader本身有做资源管理,所以下次调用加载时如果已经通过cc.loader正在加载和成功加载的资源不会重复加载。
重试实现:通过变量记录加载次数,在失败和超时处理中判断是否达到重试次数,未达到则重新加载。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 | /** * 加载 resources 目录下单个资源 * @param url * @param type * @param progressCallback * @param completeCallback */ public static loadRes(url: string, type: typeof cc.Asset, completeCallback: (error: Error, resource: any) => void): void { let count = ResLoader.retryCount + 1; let hasCb = false ; // timeout let timer = setTimeout(() => { hasCb = true ; completeCallback && completeCallback({ name: "timeout" , message: "timeout" , }, null ); }, ResLoader.timeout); // real load let realLoad = function () { count--; // load cc.loader.loadRes(url, type, (err, result) => { if (!err || count <= 0) { clearTimeout(timer); !hasCb && completeCallback && completeCallback(err, result); return ; } realLoad(); }); }; realLoad(); } |
ViewLoader,负责prefab和重用界面node的缓存,所以每个prefab都设置一个对应的tag,加载的prefab存放在Dictionary<number,cc.Prefab>类型的prefabDict属性中(Dictionary可以通过两个数组存放key-value封装出来),重用界面的node存放在Dictionary<number,cc.Node>类型的nodeDict属性中。
1 2 3 | private static tag2prefabPathMap: Dictionary<number, string> = new Dictionary<number, string>(); private static tag2prefabMap: Dictionary<number, cc.Prefab> = new Dictionary<number, cc.Prefab>(); private static tag2nodeMap: Dictionary<number, cc.Node> = new Dictionary<number, cc.Node>(); |
通过ResLoader加载cc.Prefab。cc.instantiate实例出node节点
1 2 3 4 5 6 7 8 | let instantiatePrefab = function (prefab: cc.Prefab) { let node = cc.instantiate(prefab); if (reuse && !ViewLoader.tag2nodeMap.hasKey(tag)) { ViewLoader.tag2nodeMap.set(tag, node); } cb && cb(node); } |
资源加载管理先聊到这里,下一篇我们将介绍下怎样做网络通信。
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 深入理解 Mybatis 分库分表执行原理
· 如何打造一个高并发系统?
· .NET Core GC压缩(compact_phase)底层原理浅谈
· 现代计算机视觉入门之:什么是图片特征编码
· .NET 9 new features-C#13新的锁类型和语义
· Sdcb Chats 技术博客:数据库 ID 选型的曲折之路 - 从 Guid 到自增 ID,再到
· 语音处理 开源项目 EchoSharp
· 《HelloGitHub》第 106 期
· Spring AI + Ollama 实现 deepseek-r1 的API服务和调用
· 使用 Dify + LLM 构建精确任务处理应用