Cocos Creator大厅+子游戏模式
转载自: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大厅+子游戏模式相关推荐
- cocos creator 大厅+子游戏模式探讨(creator版本1.8.2)
之前一直从事android开发,接触cocos creator不久.近期公司安排我研究大厅子游戏模式的热更新,前后花了近一周时间,在论坛上查资料,请教大神,期间得到了一些帮助,在此很感谢cocos中文 ...
- cocos creator 大厅 ➕ 子游戏(子游戏作为单独的项目更新)
先标记上文章的出处,嘿休嘿,感谢这位:https://www.jianshu.com/p/fe54ca980384 cocos creator版本: 1.9.1 首先建立大厅的项目 添加文件 Sub ...
- CocosCreator 2.4.3热更新实现方案(AssetBundle),大厅+子游戏模式快速实现
_ 实现功能 项目环境 关于 Asset Bundle 实现过程 工程结构 快速使用代码 构建发布 Creator 构建 制作热更新资源 制作随包发布模块 测试功能 模块完整下载 windows下测试 ...
- Cocos Creator子游戏动态下载实现(大厅+子游戏模式)
热更新 在App开发过程中,当一个App发布之后,突然发现了一个严重bug需要进行紧急修复,这时候公司各方就会忙得焦头烂额:重新打包App.测试.向各个应用市场和渠道换包.提示用户升级.用户下载.覆盖 ...
- CocosCreator大厅+子游戏+热更新方案
转载自:https://www.jianshu.com/p/efee9f5937a3 前言 随着游戏的玩法越来越多,也就意味着包体越来越大,对于玩家来说,首次下载的包体就会越来越大,从而也会增加首次启 ...
- cocos creator大厅、子游戏实现方案
参考cocos论坛:http://forum.cocos.com/t/1-5-2-demo/48200 demo: https://github.com/zhangjiangyi/HallAndChi ...
- Cocos Creator AssetBundle 游戏分包方案评估
Cocos Creator 2.0 之后推出了AssetBundle系统,类似于Unity3D的AssetBundle.先简单讲一下AssetBundle的原理作用,不涉及具体引擎的具体细节,各个 ...
- [Cocos Creator] 本地文件加载系列六:本地龙骨动画dragonbones加载(web模式)
[Cocos Creator] 本地文件加载系列一:本地文件读取(web模式) [Cocos Creator] 本地文件加载系列二:本地音频文件播放(web模式) [Cocos Creator] 本地 ...
- Cocos Creator:挑战全平台引擎极限
「深圳站」Cocos 开发者沙龙,引擎核心开发者 Jare 为我们带来了精彩的分享.其中包括 Creator 游戏开发的最佳实践模式.团队不同角色如何高效科学的分工合作来提高团队整体效率.引擎功能进化 ...
- Cocos Creator 解决热更新资源md5比较引发卡顿问题
大家在使用Cococ Creator提供的热更新 assetsManagers ,做md5校验的时候,一定会遇到卡顿的问题. 备注:文末有完整实现源码 原因是 Cococ Creator 官方提供的热 ...
最新文章
- 原创:去繁存简,回归本源:微信小程序公开课信息分析《一》
- cnn池化层输入通道数_(pytorch-深度学习系列)CNN中的池化层-学习笔记
- 十问十答 BSD 许可证
- docker 提交自定义镜像
- 邻接矩阵有向图的介绍
- JavaScript 删除某个数组中指定的对象
- ViewState使用兼谈序列化
- 虚拟化平台cloudstack(7)——新版本的调试
- win10使用import pcap 建议放弃,还是使用linux系统吧
- Oclint 安装指导
- 绿盟扫漏出现的Web常规漏洞
- scratch编程 飞翔的小鸟 开发笔记 0604
- matlab 地形图案例,matlab-画地形图
- 漫谈程序员系列:别说“我已经很努力了”
- 理解C语言——从小菜到大神的晋级之路(15)——完结篇:C编程风格
- 【渝粤教育】国家开放大学2019年春季 0553-22T色彩 参考试题
- 一个好用的软件定时器模块MultiTimer
- 这是我见过最好的Python教程:十分钟带你认识Python
- oracle 删除后恢复,Oracle快速恢复误删数据的方法
- MIT Cheetah Learning (一):State Estimate
热门文章
- 手机网站制作html5,【怎么样制作手机网站】如何使用dreamweavercs6建立手机网站?织梦手机WAP浏览模块如何制作手机网站?如何制作html5手机页面?...
- 多媒体计算机中的扬声器指的是音箱和,怎样算合格? 音箱主要指标参数全解析...
- linux系统装psp,psp上装Linux
- win8.1能安装matlab,解决win8.1安装matlab出现无法访问您试图使用的功能...vcredist.msc...等等...
- 艾默生Ev3100变频器源码,汇编语言的。电梯变频器
- 关于Java中TCP/IPMonitor监听器无响应的心得
- 用Python实现基于Tkinter和sqlite3的加密日记本程序
- 【零基础】PostgreSQL从入门到精通
- [C语言学习]C语言程序设计基础(一)
- HTML,CSS,font-family:中文字体的英文名称 (宋体 微软雅黑)