转载自:https://www.jianshu.com/p/fe54ca980384

一、前言

根据上一篇(Cocos Creator热更新),可以看出以下几点:

  • build-default目录下的main.js,为cocos creator项目的入口;
  • 热更新一文中,放置在服务器上的,仅有资源,脚本,配置等,没有入口程序,因此本文中,我们需要创造一个入口程序。

还是解释一下什么叫大厅+子游戏模式:

  1. 将大厅单独作为一个完整的项目,不同的子游戏,则为不同的项目
  2. 然后要实现不同项目之间的互调,即大厅调子游戏,或者子游戏调大厅
  3. 资源共享,共用的资源放在大厅项目中,并且子游戏中可以调用

这样做的好处:

  1. 减小上架包的体积
  2. 提高热更新的效率(打开指定子游戏,才会更新子游戏)
  3. 降低项目的耦合性(如果不共享资源,子游戏完全可以随时抽取出来作为一个单独的包使用)

二、修改子游戏

1. 添加version_generato.js

2. 构建项目

3. 在原生src下,添加 main.js 入口文件

  3.1 每次构建完项目,拷贝main.js到原生目录的src中

  main.js的内容如下:

(function () {'use strict';if (window.jsb) {/// 1.初始化资源Lib路径Root.var subgameSearchPath = (jsb.fileUtils ? jsb.fileUtils.getWritablePath() : '/')+'ALLGame/subgame/';/// 2.subgame资源未映射,则初始化资源映射表,否则略过映射.if(!cc.HallAndSubGameGlobal.subgameGlobal){cc.HallAndSubGameGlobal.subgameGlobal = {};/// 加载settings.jsrequire(subgameSearchPath + 'src/settings.js');var settings = window._CCSettings;window._CCSettings = undefined;if ( !settings.debug ) {var uuids = settings.uuids;var rawAssets = settings.rawAssets;var assetTypes = settings.assetTypes;var realRawAssets = settings.rawAssets = {};for (var mount in rawAssets) {var entries = rawAssets[mount];var realEntries = realRawAssets[mount] = {};for (var id in entries) {var entry = entries[id];var type = entry[1];// retrieve minified raw assetif (typeof type === 'number') {entry[1] = assetTypes[type];}// retrieve uuidrealEntries[uuids[id] || id] = entry;}}var scenes = settings.scenes;for (var i = 0; i < scenes.length; ++i) {var scene = scenes[i];if (typeof scene.uuid === 'number') {scene.uuid = uuids[scene.uuid];}}var packedAssets = settings.packedAssets;for (var packId in packedAssets) {var packedIds = packedAssets[packId];for (var j = 0; j < packedIds.length; ++j) {if (typeof packedIds[j] === 'number') {packedIds[j] = uuids[packedIds[j]];}}}}/// 加载project.jsvar projectDir = 'src/project.js';if ( settings.debug ) {projectDir = 'src/project.dev.js';}require(subgameSearchPath + projectDir);/// 如果当前搜索路径没有subgame,则添加进去搜索路径。var currentSearchPaths = jsb.fileUtils.getSearchPaths();if(currentSearchPaths && currentSearchPaths.indexOf(subgameSearchPath) === -1){jsb.fileUtils.addSearchPath(subgameSearchPath, true);console.log('subgame main.js 之前未添加,添加下subgameSearchPath' + currentSearchPaths);}cc.AssetLibrary.init({libraryPath: 'res/import',rawAssetsBase: 'res/raw-',rawAssets: settings.rawAssets,packedAssets: settings.packedAssets,md5AssetsMap: settings.md5AssetsMap});cc.HallAndSubGameGlobal.subgameGlobal.launchScene = settings.launchScene;/// 将subgame的场景添加到cc.game中,使得cc.director.loadScene可以从cc.game._sceneInfos查找到相关场景for(var i = 0; i < settings.scenes.length; ++i){cc.game._sceneInfos.push(settings.scenes[i]);}}/// 3.加载初始场景var launchScene = cc.HallAndSubGameGlobal.subgameGlobal.launchScene;cc.director.loadScene(launchScene, null,function () {console.log('subgame main.js 成功加载初始场景' + launchScene);});}
})();

ps: 不用管src外部的main.js文件

  3.2 或者 添加build-templates目录,自动在每次构建项目后生成main.js文件

这里的main.js内容和上面的内容一致

4. 执行version_generator.js文件

  生成version.manifest 和 project.mainfest。这个在上一篇中已经讲过,就不细说了。

三、拷贝res,src,version.manifest 和 project.mainfest到服务器目录下

  很明显,现在我们只是把子游戏生成了资源包,但是没有做任何热更新的操作。
接下来,就需要在大厅项目中,添加下载,更新的逻辑了。

四、在大厅项目中,添加相应逻辑

  负责下载,检测更新,更新子游戏的工具库文件内容如下:

const SubgameManager = {_storagePath: [],_getfiles: function(name, type, downloadCallback, finishCallback) {this._storagePath[name] = ((jsb.fileUtils ? jsb.fileUtils.getWritablePath() : '/') + 'ALLGame/' + name);this._downloadCallback = downloadCallback;this._finishCallback = finishCallback;this._fileName = name;/// 替换该地址var UIRLFILE = "http://192.168.200.117:8000/" + name + "/remote-assets";var filees = this._storagePath[name] + '/project.manifest';var customManifestStr = JSON.stringify({'packageUrl': UIRLFILE,'remoteManifestUrl': UIRLFILE + '/project.manifest','remoteVersionUrl': UIRLFILE + '/version.manifest','version': '0.0.1','assets': {},'searchPaths': []});var versionCompareHandle = function(versionA, versionB) {var vA = versionA.split('.');var vB = versionB.split('.');for (var i = 0; i < vA.length; ++i) {var a = parseInt(vA[i]);var b = parseInt(vB[i] || 0);if (a === b) {continue;} else {return a - b;}}if (vB.length > vA.length) {return -1;} else {return 0;}};this._am = new jsb.AssetsManager('', this._storagePath[name], versionCompareHandle);if (!cc.sys.ENABLE_GC_FOR_NATIVE_OBJECTS) {this._am.retain();}this._am.setVerifyCallback(function(path, asset) {var compressed = asset.compressed;if (compressed) {return true;} else {return true;}});if (cc.sys.os === cc.sys.OS_ANDROID) {this._am.setMaxConcurrentTask(2);}if (type === 1) {this._updateListener = new jsb.EventListenerAssetsManager(this._am, this._updateCb.bind(this));} else if (type == 2) {this._updateListener = new jsb.EventListenerAssetsManager(this._am, this._checkCb.bind(this));} else {this._updateListener = new jsb.EventListenerAssetsManager(this._am, this._needUpdate.bind(this));}cc.eventManager.addListener(this._updateListener, 1);if (this._am.getState() === jsb.AssetsManager.State.UNINITED) {var manifest = new jsb.Manifest(customManifestStr, this._storagePath[name]);this._am.loadLocalManifest(manifest, this._storagePath[name]);}if (type === 1) {this._am.update();this._failCount = 0;} else {this._am.checkUpdate();}this._updating = true;cc.log('更新文件:' + filees);},// type = 1_updateCb: function(event) {var failed = false;let self = this;switch (event.getEventCode()) {case jsb.EventAssetsManager.ERROR_NO_LOCAL_MANIFEST:/*0 本地没有配置文件*/cc.log('updateCb本地没有配置文件');failed = true;break;case jsb.EventAssetsManager.ERROR_DOWNLOAD_MANIFEST:/*1下载配置文件错误*/cc.log('updateCb下载配置文件错误');failed = true;break;case jsb.EventAssetsManager.ERROR_PARSE_MANIFEST:/*2 解析文件错误*/cc.log('updateCb解析文件错误');failed = true;break;case jsb.EventAssetsManager.NEW_VERSION_FOUND:/*3发现新的更新*/cc.log('updateCb发现新的更新');break;case jsb.EventAssetsManager.ALREADY_UP_TO_DATE:/*4 已经是最新的*/cc.log('updateCb已经是最新的');failed = true;break;case jsb.EventAssetsManager.UPDATE_PROGRESSION:/*5 最新进展 */self._downloadCallback && self._downloadCallback(event.getPercentByFile());break;case jsb.EventAssetsManager.ASSET_UPDATED:/*6需要更新*/break;case jsb.EventAssetsManager.ERROR_UPDATING:/*7更新错误*/cc.log('updateCb更新错误');break;case jsb.EventAssetsManager.UPDATE_FINISHED:/*8更新完成*/self._finishCallback && self._finishCallback(true);break;case jsb.EventAssetsManager.UPDATE_FAILED:/*9更新失败*/self._failCount++;if (self._failCount <= 3) {self._am.downloadFailedAssets();cc.log(('updateCb更新失败' + this._failCount + ' 次'));} else {cc.log(('updateCb失败次数过多'));self._failCount = 0;failed = true;self._updating = false;}break;case jsb.EventAssetsManager.ERROR_DECOMPRESS:/*10解压失败*/cc.log('updateCb解压失败');break;}if (failed) {cc.eventManager.removeListener(self._updateListener);self._updateListener = null;self._updating = false;self._finishCallback && self._finishCallback(false);}},// type = 2_checkCb: function(event) {var failed = false;let self = this;switch (event.getEventCode()) {case jsb.EventAssetsManager.ERROR_NO_LOCAL_MANIFEST:/*0 本地没有配置文件*/cc.log('checkCb本地没有配置文件');break;case jsb.EventAssetsManager.ERROR_DOWNLOAD_MANIFEST:/*1下载配置文件错误*/cc.log('checkCb下载配置文件错误');failed = true;break;case jsb.EventAssetsManager.ERROR_PARSE_MANIFEST:/*2 解析文件错误*/cc.log('checkCb解析文件错误');failed = true;break;case jsb.EventAssetsManager.NEW_VERSION_FOUND:/*3发现新的更新*/self._getfiles(self._fileName, 1, self._downloadCallback, self._finishCallback);break;case jsb.EventAssetsManager.ALREADY_UP_TO_DATE:/*4 已经是最新的*/cc.log('checkCb已经是最新的');self._finishCallback && self._finishCallback(true);break;case jsb.EventAssetsManager.UPDATE_PROGRESSION:/*5 最新进展 */break;case jsb.EventAssetsManager.ASSET_UPDATED:/*6需要更新*/break;case jsb.EventAssetsManager.ERROR_UPDATING:/*7更新错误*/cc.log('checkCb更新错误');failed = true;break;case jsb.EventAssetsManager.UPDATE_FINISHED:/*8更新完成*/cc.log('checkCb更新完成');break;case jsb.EventAssetsManager.UPDATE_FAILED:/*9更新失败*/cc.log('checkCb更新失败');failed = true;break;case jsb.EventAssetsManager.ERROR_DECOMPRESS:/*10解压失败*/cc.log('checkCb解压失败');break;}this._updating = false;if (failed) {self._finishCallback && self._finishCallback(false);}},// type = 3_needUpdate: function(event) {let self = this;switch (event.getEventCode()) {case jsb.EventAssetsManager.ALREADY_UP_TO_DATE:cc.log('子游戏已经是最新的,不需要更新');self._finishCallback && self._finishCallback(false);break;case jsb.EventAssetsManager.NEW_VERSION_FOUND:cc.log('子游戏需要更新');self._finishCallback && self._finishCallback(true);break;// 检查是否更新出错case jsb.EventAssetsManager.ERROR_DOWNLOAD_MANIFEST:case jsb.EventAssetsManager.ERROR_PARSE_MANIFEST:case jsb.EventAssetsManager.ERROR_UPDATING:case jsb.EventAssetsManager.UPDATE_FAILED:self._downloadCallback();break;}},/*** 下载子游戏* @param {string} name - 游戏名* @param progress - 下载进度回调* @param finish - 完成回调* @note finish 返回true表示下载成功,false表示下载失败*/downloadSubgame: function(name, progress, finish) {this._getfiles(name, 2, progress, finish);},/*** 进入子游戏* @param {string} name - 游戏名*/enterSubgame: function(name) {if (!this._storagePath[name]) {this.downloadSubgame(name);return;}require(this._storagePath[name] + '/src/main.js');},/*** 判断子游戏是否已经下载* @param {string} name - 游戏名*/isSubgameDownLoad: function (name) {let file = (jsb.fileUtils ? jsb.fileUtils.getWritablePath() : '/') + 'ALLGame/' + name + '/project.manifest';if (jsb.fileUtils.isFileExist(file)) {return true;} else {return false;}},/*** 判断子游戏是否需要更新* @param {string} name - 游戏名* @param isUpdateCallback - 是否需要更新回调* @param failCallback - 错误回调* @note isUpdateCallback 返回true表示需要更新,false表示不需要更新*/needUpdateSubgame: function (name, isUpdateCallback, failCallback) {this._getfiles(name, 3, failCallback, isUpdateCallback);},
};module.exports = SubgameManager;

  调用的过程如下:
    1. 判断子游戏是否已下载
    2. 已下载,判断是否需要更新
    3.1 下载游戏
    3.2 更新游戏
    4. 进入子游戏

const SubgameManager = require('SubgameManager');cc.Class({extends: cc.Component,properties: {downloadBtn: {default: null,type: cc.Node},downloadLabel: {default: null,type: cc.Label}},onLoad: function () {const name = 'subgame';    //判断子游戏有没有下载if (SubgameManager.isSubgameDownLoad(name)) {//已下载,判断是否需要更新SubgameManager.needUpdateSubgame(name, (success) => {if (success) {this.downloadLabel.string = "子游戏需要更新";} else {this.downloadLabel.string = "子游戏不需要更新";}}, () => {cc.log('出错了');});} else {this.downloadLabel.string = "子游戏未下载";}this.downloadBtn.on('click', () => {//下载子游戏/更新子游戏SubgameManager.downloadSubgame(name, (progress) => {if (isNaN(progress)) {progress = 0;}this.downloadLabel.string = "资源下载中   " + parseInt(progress * 100) + "%";}, function(success) {if (success) {SubgameManager.enterSubgame('subgame');} else {cc.log('下载失败');}});}, this);},
});

说到这呢,就得提一下,
如果界面设计时,从大厅点击子游戏,中间有loading的界面的话,
loading界面就应该放在大厅的工程中了。

五、测试

  打开服务------>编译大厅目录------>安装运行
注意:
    一定要生成原生apk,在真机(也可以是类似于夜神的模拟器啦)上运行测试。
结果:
    1. 第一次,本地没有子游戏,提示“游戏未下载”,下载后,无需重启,可直接进入子游戏;
    2. 修改version_generator.js中的版本号,将步骤二,再走一遍,能检测到更新,同样无需重启;
    3. 在大厅中,使用cc.sys.localStorage存储的值,在子游戏中可以获取到;

本人的一点小思考:

在研究之前,想着一定要研究一下资源共享的问题;
现在想来,既然要将子游戏独立出一个项目,自然也期望以后子游戏可以作为一个单独的apk来运行,如果共用大厅的资源,以后想抽取出来,又是一项艰巨的任务。但是这样必然会造成一定的重复资源。具体取舍,等到项目后期再协调。

Cocos Creator大厅+子游戏模式相关推荐

  1. cocos creator 大厅+子游戏模式探讨(creator版本1.8.2)

    之前一直从事android开发,接触cocos creator不久.近期公司安排我研究大厅子游戏模式的热更新,前后花了近一周时间,在论坛上查资料,请教大神,期间得到了一些帮助,在此很感谢cocos中文 ...

  2. cocos creator 大厅 ➕ 子游戏(子游戏作为单独的项目更新)

    先标记上文章的出处,嘿休嘿,感谢这位:https://www.jianshu.com/p/fe54ca980384 cocos creator版本: 1.9.1 首先建立大厅的项目 添加文件  Sub ...

  3. CocosCreator 2.4.3热更新实现方案(AssetBundle),大厅+子游戏模式快速实现

    _ 实现功能 项目环境 关于 Asset Bundle 实现过程 工程结构 快速使用代码 构建发布 Creator 构建 制作热更新资源 制作随包发布模块 测试功能 模块完整下载 windows下测试 ...

  4. Cocos Creator子游戏动态下载实现(大厅+子游戏模式)

    热更新 在App开发过程中,当一个App发布之后,突然发现了一个严重bug需要进行紧急修复,这时候公司各方就会忙得焦头烂额:重新打包App.测试.向各个应用市场和渠道换包.提示用户升级.用户下载.覆盖 ...

  5. CocosCreator大厅+子游戏+热更新方案

    转载自:https://www.jianshu.com/p/efee9f5937a3 前言 随着游戏的玩法越来越多,也就意味着包体越来越大,对于玩家来说,首次下载的包体就会越来越大,从而也会增加首次启 ...

  6. cocos creator大厅、子游戏实现方案

    参考cocos论坛:http://forum.cocos.com/t/1-5-2-demo/48200 demo: https://github.com/zhangjiangyi/HallAndChi ...

  7. Cocos Creator AssetBundle 游戏分包方案评估

      Cocos Creator 2.0 之后推出了AssetBundle系统,类似于Unity3D的AssetBundle.先简单讲一下AssetBundle的原理作用,不涉及具体引擎的具体细节,各个 ...

  8. [Cocos Creator] 本地文件加载系列六:本地龙骨动画dragonbones加载(web模式)

    [Cocos Creator] 本地文件加载系列一:本地文件读取(web模式) [Cocos Creator] 本地文件加载系列二:本地音频文件播放(web模式) [Cocos Creator] 本地 ...

  9. Cocos Creator:挑战全平台引擎极限

    「深圳站」Cocos 开发者沙龙,引擎核心开发者 Jare 为我们带来了精彩的分享.其中包括 Creator 游戏开发的最佳实践模式.团队不同角色如何高效科学的分工合作来提高团队整体效率.引擎功能进化 ...

  10. Cocos Creator 解决热更新资源md5比较引发卡顿问题

    大家在使用Cococ Creator提供的热更新 assetsManagers ,做md5校验的时候,一定会遇到卡顿的问题. 备注:文末有完整实现源码 原因是 Cococ Creator 官方提供的热 ...

最新文章

  1. 原创:去繁存简,回归本源:微信小程序公开课信息分析《一》
  2. cnn池化层输入通道数_(pytorch-深度学习系列)CNN中的池化层-学习笔记
  3. 十问十答 BSD 许可证
  4. docker 提交自定义镜像
  5. 邻接矩阵有向图的介绍
  6. JavaScript 删除某个数组中指定的对象
  7. ViewState使用兼谈序列化
  8. 虚拟化平台cloudstack(7)——新版本的调试
  9. win10使用import pcap 建议放弃,还是使用linux系统吧
  10. Oclint 安装指导
  11. 绿盟扫漏出现的Web常规漏洞
  12. scratch编程 飞翔的小鸟 开发笔记 0604
  13. matlab 地形图案例,matlab-画地形图
  14. 漫谈程序员系列:别说“我已经很努力了”
  15. 理解C语言——从小菜到大神的晋级之路(15)——完结篇:C编程风格
  16. 【渝粤教育】国家开放大学2019年春季 0553-22T色彩 参考试题
  17. 一个好用的软件定时器模块MultiTimer
  18. 这是我见过最好的Python教程:十分钟带你认识Python
  19. oracle 删除后恢复,Oracle快速恢复误删数据的方法
  20. MIT Cheetah Learning (一):State Estimate

热门文章

  1. 手机网站制作html5,【怎么样制作手机网站】如何使用dreamweavercs6建立手机网站?织梦手机WAP浏览模块如何制作手机网站?如何制作html5手机页面?...
  2. 多媒体计算机中的扬声器指的是音箱和,怎样算合格? 音箱主要指标参数全解析...
  3. linux系统装psp,psp上装Linux
  4. win8.1能安装matlab,解决win8.1安装matlab出现无法访问您试图使用的功能...vcredist.msc...等等...
  5. 艾默生Ev3100变频器源码,汇编语言的。电梯变频器
  6. 关于Java中TCP/IPMonitor监听器无响应的心得
  7. 用Python实现基于Tkinter和sqlite3的加密日记本程序
  8. 【零基础】PostgreSQL从入门到精通
  9. [C语言学习]C语言程序设计基础(一)
  10. HTML,CSS,font-family:中文字体的英文名称 (宋体 微软雅黑)