项目有个需求,需要缓存dundle内容,但是其实loadBundle下来的是一个配置文件,并不是实际上用到的资源,所以在想,是否可以直接吧资源下下来找一下原理;就研究了一下cocos的源码;

参考‘宝爷’的这个帖子:https://forum.cocos.org/t/cocos-creator/100107

看源码前我们先了解一下cocos的管线和任务:https://docs.cocos.com/creator/manual/zh/asset-manager/pipeline-task.html?h=pipe

    AssetManager.js中的3条管线:/*** !#en * Normal loading pipeline* * !#zh* 正常加载管线* * @property pipeline* @type {Pipeline}*/this.pipeline = pipeline.append(preprocess).append(load);/*** !#en * Fetching pipeline* * !#zh* 下载管线* * @property fetchPipeline* @type {Pipeline}*/this.fetchPipeline = fetchPipeline.append(preprocess).append(fetch);/*** !#en * Url transformer* * !#zh* Url 转换器* * @property transformPipeline* @type {Pipeline}*/this.transformPipeline = transformPipeline.append(parse).append(combine);

开始吧,当我们cc.assetManager.loadBundle的时候,引擎在做些什么:

 AssetManager.jsloadBundle(nameOrUrl, options, onComplete) {//当options为undefined时,将回调函数由options变成onComplete;var { options, onComplete } = parseParameters(options, undefined, onComplete);//获取bundle的名字;let bundleName = cc.path.basename(nameOrUrl);//本地检测;if (this.bundles.has(bundleName)) {return asyncify(onComplete)(null, this.getBundle(bundleName));}//是否有自定义预设,没有就用bundle;options.preset = options.preset || 'bundle';options.ext = 'bundle';this.loadRemote(nameOrUrl, options, onComplete);},loadRemote(url, options, onComplete) {var { options, onComplete } = parseParameters(options, undefined, onComplete);//本地检测;if (this.assets.has(url)) {return asyncify(onComplete)(null, this.assets.get(url));}options.__isNative__ = true;options.preset = options.preset || 'remote';//下载;this.loadAny({ url }, options, null, function (err, data) {if (err) {cc.error(err.message, err.stack);onComplete && onComplete(err, null);}else {factory.create(url, data, options.ext || cc.path.extname(url), options, function (err, out) {onComplete && onComplete(err, out);});}});},

接下来管线和任务登场了

   loadAny(requests, options, onProgress, onComplete) {var { options, onProgress, onComplete } = parseParameters(options, onProgress, onComplete);options.preset = options.preset || 'default';requests = Array.isArray(requests) ? requests.concat() : requests;//新建一个任务;let task = new Task({ input: requests, onProgress, onComplete: asyncify(onComplete), options });//加载管线去处理任务,注意这里的是3条管线中的加载管线;pipeline.async(task);},async(task) {var pipes = this.pipes;if (!(task instanceof Task) || pipes.length === 0) return;//设置任务输入;if (task.output != null) {task.input = task.output;task.output = null;}task._isFinish = false;this._flow(0, task);},_flow(index, task) {var self = this;/*回到上面的这里this.pipeline = pipeline.append(preprocess).append(load);加载管线有preprocess和load两部分;preprocess实际上只创建了一个子任务,然后交由transformPipeline执行,得到一个RequestItem对象并赋值给task.output;*/var pipe = this.pipes[index];pipe(task, function (result) {if (result) {task._isFinish = true;task.onComplete && task.onComplete(result);}else {index++;if (index < self.pipes.length) {// move output to inputtask.input = task.output;task.output = null;self._flow(index, task);}else {task._isFinish = true;task.onComplete && task.onComplete(result, task.output);}}});}

正常加载管线pipeline中的preprocess任务,通过转换器管线transformPipeline获取一个RequestItem对象并赋值给task.output;

    function preprocess(task, done) {var options = task.options, subOptions = Object.create(null), leftOptions = Object.create(null);for (var op in options) {switch (op) {// can't set these attributes in optionscase RequestType.PATH:case RequestType.UUID:case RequestType.DIR:case RequestType.SCENE:case RequestType.URL: break;// only need these attributes to transform urlcase '__requestType__':case '__isNative__':case 'ext':case 'type':case '__nativeName__':case 'audioLoadMode':case 'bundle':subOptions[op] = options[op];break;// other settings, left to next pipecase '__exclude__':case '__outputAsArray__':leftOptions[op] = options[op];break;default:subOptions[op] = options[op];leftOptions[op] = options[op];break;}}task.options = leftOptions;// 创建一个新的任务;let subTask = Task.create({ input: task.input, options: subOptions });var err = null;try {/*3条管线中的转换管线this.transformPipeline = transformPipeline.append(parse).append(combine);包含parse和combine2部分:parse的职责是为每个要加载的资源生成RequestItem对象并初始化其资源信息(AssetInfo、uuid、config等;combine方法会为每个RequestItem构建出真正的加载路径,这个加载路径最终会转换到item.url中*/task.output = task.source = transformPipeline.sync(subTask);}catch (e) {err = e;for (var i = 0, l = subTask.output.length; i < l; i++) {subTask.output[i].recycle();}}subTask.recycle();done(err);}

正常加载管线pipeline中的load任务:

function load (task, done) {let firstTask = false;if (!task.progress) {task.progress = { finish: 0, total: task.input.length, canInvoke: true };firstTask = true;}var options = task.options, progress = task.progress;options.__exclude__ = options.__exclude__ || Object.create(null);task.output = [];forEach(task.input, function (item, cb) {let subTask = Task.create({ input: item, onProgress: task.onProgress, options, progress, onComplete: function (err, item) {if (err && !task.isFinish) {if (!cc.assetManager.force || firstTask) {if (!CC_EDITOR) {cc.error(err.message, err.stack);}progress.canInvoke = false;done(err);}else {progress.canInvoke && task.dispatch('progress', ++progress.finish, progress.total, item);}}task.output.push(item);subTask.recycle();cb();}});/*load管线,由fetch和parse两部分组成:fetch方法用于下载资源文件,packManager.load去下载文件,并将文件放入item.file中。*/        loadOneAssetPipeline.async(subTask);}, function () {options.__exclude__ = null;if (task.isFinish) {clear(task, true);return task.dispatch('error');}gatherAsset(task);clear(task, true);done();});
}

loadOneAsset中的两个任务:

    //fetch方法用于下载资源文件,由packManager负责下载的实现,fetch会将下载完的文件数据放到item.file中function fetch (task, done) {var item = task.output = task.input;var { options, isNative, uuid, file } = item;var { reload } = options;if (file || (!reload && !isNative && assets.has(uuid))) return done();/*下载文件;packManager.load->downloader.download->downloadBundle(下载bundle的方法里面会去下载目录下的config.json和index.js,downloadJson downloadScript)*/packManager.load(item, task.options, function (err, data) {item.file = data;done(err);});},//parse方法用于将加载完的资源文件转换成我们可用的资源对象function parse (task, done) {var item = task.output = task.input, progress = task.progress, exclude = task.options.__exclude__;var { id, file, options } = item;if (item.isNative) {parser.parse(id, file, item.ext, options, function (err, asset) {if (err) return done(err);item.content = asset;progress.canInvoke && task.dispatch('progress', ++progress.finish, progress.total, item);files.remove(id);parsed.remove(id);done();});}else {var { uuid } = item;if (uuid in exclude) {var { finish, content, err, callbacks } = exclude[uuid];progress.canInvoke && task.dispatch('progress', ++progress.finish, progress.total, item);if (finish || checkCircleReference(uuid, uuid, exclude) ) {content && content.addRef && content.addRef();item.content = content;done(err);}else {callbacks.push({ done, item });}}else {if (!options.reload && assets.has(uuid)) {var asset = assets.get(uuid);if (options.__asyncLoadAssets__ || !asset.__asyncLoadAssets__) {item.content = asset.addRef();progress.canInvoke && task.dispatch('progress', ++progress.finish, progress.total, item);done();}else {loadDepends(task, asset, done, false);}}else {parser.parse(id, file, 'import', options, function (err, asset) {if (err) return done(err);asset._uuid = uuid;loadDepends(task, asset, done, true);});}}}}

ok了,需要的文件已经下载下来了,

  loadRemote(url, options, onComplete) {var { options, onComplete } = parseParameters(options, undefined, onComplete);if (this.assets.has(url)) {return asyncify(onComplete)(null, this.assets.get(url));}options.__isNative__ = true;options.preset = options.preset || 'remote';this.loadAny({ url }, options, null, function (err, data) {if (err) {cc.error(err.message, err.stack);onComplete && onComplete(err, null);}else {//config文件已经下载完毕;factory.create(url, data, options.ext || cc.path.extname(url), options, function (err, out) {onComplete && onComplete(err, out);});}});},create(id, data, type, options, onComplete) {//func = createBundle;var func = producers[type] || producers['default'];let asset, creating;if (asset = assets.get(id)) {onComplete(null, asset);}else if (creating = _creating.get(id)) {creating.push(onComplete);}else {_creating.add(id, [onComplete]);//初始化createBundle,并返回;func(id, data, options, function (err, data) {if (!err && data instanceof cc.Asset) {data._uuid = id;assets.add(id, data);}let callbacks = _creating.remove(id);for (let i = 0, l = callbacks.length; i < l; i++) {callbacks[i](err, data);}});}}

整个流程就是这样子的。这就是我理解的loadBundle整个流程了。有不足和错误的话,请各位大佬指出~。

Cocos Creator(v.2.4.5) Bundle的加载与解析相关推荐

  1. cocos creator 3.x 精灵不显示、加载动态图片、物理碰撞、人物跟随鼠标移动、碰撞后节点销毁

    温馨提醒:即刻转去Unity3d 精灵不显示: 不要在空节点下直接添加组件 正确的做法是:在空节点(Node)上右键创建一个精灵才会给看到 或者直接拖曳一个图片放到场景编辑器中也可 cocos cre ...

  2. Cocos Creator 的 web/原生多平台 Spine 换装方案解析,附 Demo 源码

    引言:Spine 换装是游戏开发中的一种常见实现方案,本次,羽毛先生将介绍自己对整体换装和局部换装实现方案的探索与选择. 运行环境 Cocos Creator 3.5.2 web/native 需求 ...

  3. 麒麟子Cocos Creator 3D研究笔记一:图片资源类型和加载

    图片资源在Cocos Creator 3D里面,有两种状态,一种用于3D纹理,另一种用于2D界面.如下图所示,sprite-frame用于2D,其余都用于3D. 如果你试图把一个标记为texture的 ...

  4. Creator 2.0.x 微信小游戏加载优化(一):定制wx-downloader

    转载请保留原文链接:https://blog.csdn.net/zzx023/article/details/89842503 微信小游戏的环境与正常web浏览器的环境相似,但很多细节上会有所不同,同 ...

  5. 用《捕鱼达人3》讲解Cocos引擎3D技术(一):加载鱼的模型和播放动画

    大家好,相信许多朋友们都听过或玩过<捕鱼达人>这款游戏.今年7月中旬,<捕鱼达人3>正式发布,24小时就突破实现1千万下载,欢迎大家一起来下载试玩! 本次<捕鱼达人3&g ...

  6. Cocos Creator资源管理AssetManager细说一二

    关于AssetManager Asset Manager 是 Creator 在 v2.4 新推出的资源管理器,用于替代之前的 cc.loader.新的 Asset Manager 资源管理模块具备加 ...

  7. 拖动小游戏html,Cocos Creator 入门篇-拖拽小游戏(一)

    前言 Cocos Creator的官方文档还是非常友好的,有中英文两个版本. [强烈建议] 初学者先把官方文档看一遍.里面还包含了很多demo. 今天主要先带大家简单熟悉一下Cocos Creator ...

  8. Cocos Creator 资源管理AssetManager

    版本:2.4.0 cocos的资源管理初用真的很难.靠依赖关系去加载,释放,往往不灵活,得不到想要的结果. Egret资源管理做得很好,有可视化管理工具,资源分组加载,动态加载之类都比较灵活.解决了1 ...

  9. Cocos Creator v1.5发布:物理集成、2D摄像机、TypeScript

    经过1个多月的Beta版发布和社区测试,直至跳票两周之后,我们终于能够发布 Cocos Creator v1.5 正式版了.这个版本又给大家带来了很多实用的新功能. Cocos Creator 作为第 ...

最新文章

  1. .NET项目开发的几个非常重要的项目设置
  2. 在PowerDesigner中设置字段唯一约束 --相当于unique
  3. Python时间库—datetime的详解及使用
  4. 数学--数论--快速乘法+快速幂
  5. mysql sql语句 入门_mysql(3)mysql的sql语句入门
  6. php的添加语句怎么写,php修改语句怎么写
  7. 将更新面板的显示内容清除的代码实现;_免费开源:墨涩必应壁纸网盘系统1.0(自动壁纸更新免维护)
  8. maven 指定jdk版本打包
  9. 实体与电商,有啥区别?
  10. 手机上有没有学python的软件-【Learn Python】用手机学Python
  11. 云编程和软件环境 、物联网的关键技术
  12. 上门洗车APP --- Androidclient开发 之 网络框架封装介绍(二)
  13. Pong’s Birds(概率 模拟)
  14. 狗狗最近不爱吃饭了,还好有办法应对
  15. 第13章 原始套接字
  16. UltraEdit 多词搜索
  17. 一切都显得那么苍白无力
  18. emacs 使用集锦
  19. 使用Java建立一个公交管理系统,监督管理公交日常运营情况。
  20. 【NetWorkX实例(4)】Football数据集

热门文章

  1. 太可了,刷透这份“架构师养成手册”成就自己的架构之路
  2. 三天玩玩UIPATH的RPA的机器人自动化的玩意儿
  3. Java:annotation注解的简单理解和总结
  4. 【Git】下载安装学习记录
  5. 全国数据治理认证DAMA-CDGA/CDGP(线上班)招生简章
  6. 基于OneNet平台智慧农业大棚(esp32)
  7. 机器人softmove_ABB机器人的各种选项
  8. 配置oracle方言类,Dialect 修改oracle方言失败
  9. 读《许式伟的架构课》有感
  10. javascript中的offsetWidth、clientWidth、innerWidth及相关属性方法