Cocos Creator(v.2.4.5) Bundle的加载与解析
项目有个需求,需要缓存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的加载与解析相关推荐
- cocos creator 3.x 精灵不显示、加载动态图片、物理碰撞、人物跟随鼠标移动、碰撞后节点销毁
温馨提醒:即刻转去Unity3d 精灵不显示: 不要在空节点下直接添加组件 正确的做法是:在空节点(Node)上右键创建一个精灵才会给看到 或者直接拖曳一个图片放到场景编辑器中也可 cocos cre ...
- Cocos Creator 的 web/原生多平台 Spine 换装方案解析,附 Demo 源码
引言:Spine 换装是游戏开发中的一种常见实现方案,本次,羽毛先生将介绍自己对整体换装和局部换装实现方案的探索与选择. 运行环境 Cocos Creator 3.5.2 web/native 需求 ...
- 麒麟子Cocos Creator 3D研究笔记一:图片资源类型和加载
图片资源在Cocos Creator 3D里面,有两种状态,一种用于3D纹理,另一种用于2D界面.如下图所示,sprite-frame用于2D,其余都用于3D. 如果你试图把一个标记为texture的 ...
- Creator 2.0.x 微信小游戏加载优化(一):定制wx-downloader
转载请保留原文链接:https://blog.csdn.net/zzx023/article/details/89842503 微信小游戏的环境与正常web浏览器的环境相似,但很多细节上会有所不同,同 ...
- 用《捕鱼达人3》讲解Cocos引擎3D技术(一):加载鱼的模型和播放动画
大家好,相信许多朋友们都听过或玩过<捕鱼达人>这款游戏.今年7月中旬,<捕鱼达人3>正式发布,24小时就突破实现1千万下载,欢迎大家一起来下载试玩! 本次<捕鱼达人3&g ...
- Cocos Creator资源管理AssetManager细说一二
关于AssetManager Asset Manager 是 Creator 在 v2.4 新推出的资源管理器,用于替代之前的 cc.loader.新的 Asset Manager 资源管理模块具备加 ...
- 拖动小游戏html,Cocos Creator 入门篇-拖拽小游戏(一)
前言 Cocos Creator的官方文档还是非常友好的,有中英文两个版本. [强烈建议] 初学者先把官方文档看一遍.里面还包含了很多demo. 今天主要先带大家简单熟悉一下Cocos Creator ...
- Cocos Creator 资源管理AssetManager
版本:2.4.0 cocos的资源管理初用真的很难.靠依赖关系去加载,释放,往往不灵活,得不到想要的结果. Egret资源管理做得很好,有可视化管理工具,资源分组加载,动态加载之类都比较灵活.解决了1 ...
- Cocos Creator v1.5发布:物理集成、2D摄像机、TypeScript
经过1个多月的Beta版发布和社区测试,直至跳票两周之后,我们终于能够发布 Cocos Creator v1.5 正式版了.这个版本又给大家带来了很多实用的新功能. Cocos Creator 作为第 ...
最新文章
- .NET项目开发的几个非常重要的项目设置
- 在PowerDesigner中设置字段唯一约束 --相当于unique
- Python时间库—datetime的详解及使用
- 数学--数论--快速乘法+快速幂
- mysql sql语句 入门_mysql(3)mysql的sql语句入门
- php的添加语句怎么写,php修改语句怎么写
- 将更新面板的显示内容清除的代码实现;_免费开源:墨涩必应壁纸网盘系统1.0(自动壁纸更新免维护)
- maven 指定jdk版本打包
- 实体与电商,有啥区别?
- 手机上有没有学python的软件-【Learn Python】用手机学Python
- 云编程和软件环境 、物联网的关键技术
- 上门洗车APP --- Androidclient开发 之 网络框架封装介绍(二)
- Pong’s Birds(概率 模拟)
- 狗狗最近不爱吃饭了,还好有办法应对
- 第13章 原始套接字
- UltraEdit 多词搜索
- 一切都显得那么苍白无力
- emacs 使用集锦
- 使用Java建立一个公交管理系统,监督管理公交日常运营情况。
- 【NetWorkX实例(4)】Football数据集
热门文章
- 太可了,刷透这份“架构师养成手册”成就自己的架构之路
- 三天玩玩UIPATH的RPA的机器人自动化的玩意儿
- Java:annotation注解的简单理解和总结
- 【Git】下载安装学习记录
- 全国数据治理认证DAMA-CDGA/CDGP(线上班)招生简章
- 基于OneNet平台智慧农业大棚(esp32)
- 机器人softmove_ABB机器人的各种选项
- 配置oracle方言类,Dialect 修改oracle方言失败
- 读《许式伟的架构课》有感
- javascript中的offsetWidth、clientWidth、innerWidth及相关属性方法