【免费】对资源加载和释放逻辑的封装

先吐槽一下

用 Cocos Creator 开发也有段时间了,说实话资源加载这块一直让我挺头疼的。

每次写加载代码都是这样,或者先加载bundle再这样:

resources.load("prefab/player", Prefab, (err, prefab) => {
    if (err) {
        console.error(err);
        return;
    }
    // 可以用了
});

加载个资源要写这些,关键是项目里到处都是这种代码。

更麻烦的是批量加载。比如进入战斗场景,我要加载:

  • config 文件夹下的配置
  • icon 文件夹下的图标
  • prefab 文件夹下的预制体
  • 还有其他 bundle 里的资源

这时候就得自己写循环、计算进度、处理异常…每个项目都要封装一遍,烦死了。

还有资源释放的问题。退出战斗场景时,怎么把刚才加载的资源都释放掉?

反正就是觉得官方的 API 不太够用,每次都要自己折腾。

干脆自己封装一个

既然每个项目都要写,不如封装成库,以后直接用。于是就有了 bit-assets。

核心思路很简单:

  1. 用配置的方式批量加载资源
  2. 自动管理资源池和引用计数
  3. 支持按批次释放资源

批量加载资源

import { AssetLoader, IAssetConfig } from "kunpocc-assets";

// 把要加载的资源配置好
let configs: IAssetConfig[] = [
    { path: "config" },
    { path: "icon", type: SpriteFrame },
    { path: "prefab", type: Prefab },
    { path: "texture/avatar", type: SpriteFrame, isFile: true },  // 单个文件
    { path: "pet", type: SpriteFrame, bundle: "battle" },     // 其他 bundle
];

// 创建加载器
let loader = new AssetLoader("battle");  // 给个名字,后面释放用得上
loader.parallel = 10;  // 并行的任务数量
loader.retry = 3;      // 失败了重试 3 次

loader.setCallbacks({
    complete: () => {
        console.log("加载完了");
    },
    fail: (code, msg) => {
        console.error("加载失败:", msg);
    },
    progress: (percent) => {
        console.log("进度:", Math.floor(percent * 100) + "%");
    }
});

loader.start(configs);

使用已加载的资源

import { AssetPool } from "kunpocc-assets";

let prefab = AssetPool.get<Prefab>("prefab/player");
let icon = AssetPool.get<SpriteFrame>("icon/gold", "bundleName");

// 也可以用 UUID 获取
let asset = AssetPool.getByUUID<SpriteFrame>("xxxxx-xxxx-xxxx");

释放资源

import { AssetPool } from "kunpocc-assets";

// 释放单个资源
AssetPool.releasePath("prefab/player");

// 释放整个文件夹下的 SpriteFrame 类型资源
AssetPool.releaseDir("icon", "resources", SpriteFrame);

// 重点:按批次释放
AssetPool.releaseBatchAssets("battle");  // 把刚才加载的战斗资源全释放掉

// 释放所有资源
AssetPool.releaseAll();

这个批次释放真的很好用。比如我做了个战斗场景和商城场景:

// 进战斗
let battleLoader = new AssetLoader("battle");
battleLoader.start(battleConfigs);

// 进商城
let shopLoader = new AssetLoader("shop");
shopLoader.start(shopConfigs);

// 退出战斗,一行代码释放所有战斗资源
AssetPool.releaseBatchAssets("battle");

// 退出商城,一行代码释放所有商城资源
AssetPool.releaseBatchAssets("shop");

场景切换的时候再也不用担心资源释放的问题了。

其他功能

用Creator加载的资源也可以加入到管理中,最后统一释放

resources.load("prefab/player", Prefab, (err, prefab) => {
    if (err) {
        console.error(err);
        return;
    }
    // 参数1: 资源
    // 参数2: 资源所在的bundle 默认: resources
    // 参数3: 资源加载批次名 可以不传 默认 ""
    AssetPool.add(prefab, resources, "batchName");
});

一些特点

  • 支持 Cocos Creator 3.7+
  • 没有其他依赖,就几个文件
  • 自动管理引用计数,不用担心内存泄漏
  • 同一个资源加载多次和一次效果一样,释放一次卸载

开源免费

代码放 GitHub 上了,ISC 协议,随便用:

也有商城版本,同样免费

有 bug 或者建议欢迎提 issue,我看到会回复。

最后

这个库主要是我自己项目在用,觉得还挺顺手的,就分享出来了。

如果你也觉得 Creator 的资源加载 API 不太好用,可以试试这个。代码不多,看源码也很快。

有问题随时交流。

一些其他插件或开源项目

收费插件

行为树蓝图编辑器
节点树和内存占用插件
ecs实体编辑器
kunpocc框架界面属性关联插件
excle转json插件
框架模块
creator资源模块封装

开源项目 方便魔改:

kunpocc 游戏框架
ecs 框架
EC框架
四叉树
网络库
事件系统
资源管理
行为树
打包工具

10赞

所以 老宫,你的这部分

// 把要加载的资源配置好
let configs: IAssetConfig[] = [
    { path: "config" },
    { path: "icon", type: SpriteFrame },
    { path: "prefab", type: Prefab },
    { path: "texture/avatar", type: SpriteFrame, isFile: true },  // 单个文件
    { path: "pet", type: SpriteFrame, bundle: "battle" },     // 其他 bundle
];

为什么不做个脚本自动生成所有bundle的资源列表呢,类似Android的R文件.这样就有了ID映射,无论是接口调用还是你后面做资源池缓存管理都不用硬编码字符串了,调用R文件的id即可.

实际要加载哪些资源需要根据使用场景来确定
并不是要加载全部呀

嗯.懂你的意思. 但是我指的是资源名字和路径ID化, 这个和Bundle和Bundle资源加载倒没有关系.

哦哦 我明白你的意思了
有点懒,觉得这样也够用了,就没想过你说的这个

哈哈. ID化有两个好处
1:没有硬编码
2:在编辑器中,每次这个R文件更新后,代码中使用资源ID的地方如果ID有误会爆红,这样就很便捷.

我的项目中并不会用到很多需要加载资源的地方
并且配置也不需要写多少,大部分都是通过配置数据然后整理成这样的格式

嗯.那没必要. ID化只针对量多的时候才便捷. 量少反而显得繁琐. 你的没问题.

目前来说
只有首场景会通过硬编码来配置一些加载项,不过也不多,大概有个10行左右

剩下的基本都是解析配置表,然后根据配置数据来组装需要加载的资源

比如进战斗时,根据当前关卡的配置数据,把用到的地图、怪物、使用的技能等信息组装成需要的格式
加载读条完成后进战斗
当退出战斗后,再一键卸载掉

1赞

嗯很清晰的流程。

我比较烦恼的是动态加载一些资源的时候,需要自己管理引用计数,加载后,用的时候要+1,节点销毁的时候要找到资源然后对其-1,计数归零了再去释放,一旦漏了-1,就会有资源卸载不掉造成资源泄漏。一旦重复-1又会错误释放资源。

可以,这个东西不错。我收藏了。下次项目我要用

工作找的怎么样了 :rofl:

所以规划时,按模块分
这样加载整合模块的内容,之后就直接用,等不用的时候,就全卸载掉
应该可以省点事

我们两个都在一个微信群里,你直接私我不就好了。 :relieved:

即时聊天和传统的论坛聊感觉是不一样滴

应该问,后续的剧情准备什么时候写 :rofl:

哈哈哈哈哈哈哈

看着应该是有问题的。如果使用到了交叉资源

我自用不会出现交叉资源

想交叉的话,改下引用计数
然后再改下所在的批次