热更新主要用到cocos2d的热更新模块RawAsset    AssetsManager

先按教程写个demo

创建大厅空项目 ,添加场景helloworld ,如下图在场景中加热更需要的控件 check和update按钮 label显示状态,图片guanggao_1 区别新旧版本。

创建脚本文件 HotUpdata.js 关联到场景hotUpdata内容:


/*** 负责热更新逻辑的组件*/
cc.Class({extends: cc.Component,properties: {//manifestUrl: cc.RawAsset,  //本地project.manifest资源清单文件manifestUrl:{type:cc.Asset,default:null},_updating: false,_canRetry: false,_storagePath: ''},checkCb: function (event) {cc.log('Code: ' + event.getEventCode());switch (event.getEventCode()) {case jsb.EventAssetsManager.ERROR_NO_LOCAL_MANIFEST:cc.log("No local manifest file found, hot update skipped.");break;case jsb.EventAssetsManager.ERROR_DOWNLOAD_MANIFEST:case jsb.EventAssetsManager.ERROR_PARSE_MANIFEST:cc.log("Fail to download manifest file, hot update skipped.");break;case jsb.EventAssetsManager.ALREADY_UP_TO_DATE:cc.log("Already up to date with the latest remote version.");break;case jsb.EventAssetsManager.NEW_VERSION_FOUND:cc.log('New version found, please try to update.');this.hotUpdate();break;default:return;}cc.eventManager.removeListener(this._checkListener);this._checkListener = null;this._updating = false;},updateCb: function (event) {var needRestart = false;var failed = false;switch (event.getEventCode()) {case jsb.EventAssetsManager.ERROR_NO_LOCAL_MANIFEST:cc.log('No local manifest file found, hot update skipped...');failed = true;break;case jsb.EventAssetsManager.UPDATE_PROGRESSION:cc.log(event.getPercent());cc.log(event.getPercentByFile());cc.log(event.getDownloadedFiles() + ' / ' + event.getTotalFiles());cc.log(event.getDownloadedBytes() + ' / ' + event.getTotalBytes());var msg = event.getMessage();if (msg) {cc.log('Updated file: ' + msg);}break;case jsb.EventAssetsManager.ERROR_DOWNLOAD_MANIFEST:case jsb.EventAssetsManager.ERROR_PARSE_MANIFEST:cc.log('Fail to download manifest file, hot update skipped.');failed = true;break;case jsb.EventAssetsManager.ALREADY_UP_TO_DATE:cc.log('Already up to date with the latest remote version.');failed = true;break;case jsb.EventAssetsManager.UPDATE_FINISHED:cc.log('Update finished. ' + event.getMessage());needRestart = true;break;case jsb.EventAssetsManager.UPDATE_FAILED:cc.log('Update failed. ' + event.getMessage());this._updating = false;this._canRetry = true;break;case jsb.EventAssetsManager.ERROR_UPDATING:cc.log('Asset update error: ' + event.getAssetId() + ', ' + event.getMessage());break;case jsb.EventAssetsManager.ERROR_DECOMPRESS:cc.log(event.getMessage());break;default:break;}if (failed) {cc.eventManager.removeListener(this._updateListener);this._updateListener = null;this._updating = false;}if (needRestart) {cc.eventManager.removeListener(this._updateListener);this._updateListener = null;// Prepend the manifest's search pathvar searchPaths = jsb.fileUtils.getSearchPaths();var newPaths = this._am.getLocalManifest().getSearchPaths();cc.log(JSON.stringify(newPaths));Array.prototype.unshift(searchPaths, newPaths);// This value will be retrieved and appended to the default search path during game startup,// please refer to samples/js-tests/main.js for detailed usage.// !!! Re-add the search paths in main.js is very important, otherwise, new scripts won't take effect.cc.sys.localStorage.setItem('HotUpdateSearchPaths', JSON.stringify(searchPaths));jsb.fileUtils.setSearchPaths(searchPaths);cc.audioEngine.stopAll();cc.game.restart();}},retry: function () {if (!this._updating && this._canRetry) {this._canRetry = false;cc.log('Retry failed Assets...');this._am.downloadFailedAssets();}},checkForUpdate: function () {cc.log("start checking...");if (this._updating) {cc.log('Checking or updating ...');return;}if (this._am.getState() === jsb.AssetsManager.State.UNINITED) {this._am.loadLocalManifest(this.manifestUrl.nativeUrl);cc.log(this.manifestUrl.nativeUrl);}if (!this._am.getLocalManifest() || !this._am.getLocalManifest().isLoaded()) {cc.log('Failed to load local manifest ...');return;}/* descrepatethis._checkListener = new jsb.EventListenerAssetsManager(this._am, this.checkCb.bind(this));cc.eventManager.addListener(this._checkListener, 1);*/this._am.setEventCallback(this.checkCb.bind(this));this._am.checkUpdate();this._updating = true;},hotUpdate: function () {if (this._am) {/*descrepatethis._updateListener = new jsb.EventListenerAssetsManager(this._am, this.updateCb.bind(this));cc.eventManager.addListener(this._updateListener, 1);*/this._am.setEventCallback(this.updateCb.bind(this));if (this._am.getState() === jsb.AssetsManager.State.UNINITED) {this._am.loadLocalManifest(this.manifestUrl.nativeUrl);}this._failCount = 0;this._am.update();this._updating = true;}},show: function () {// if (this.updateUI.active === false) {//     this.updateUI.active = true;// }},// use this for initializationonLoad: function () {// Hot update is only available in Native buildif (!cc.sys.isNative) {return;}this._storagePath = ((jsb.fileUtils ? jsb.fileUtils.getWritablePath() : '/') + 'xiaoming-remote-asset');cc.log('Storage path for remote asset : ' + this._storagePath);// Setup your own version compare handler, versionA and B is versions in string// if the return value greater than 0, versionA is greater than B,// if the return value equals 0, versionA equals to B,// if the return value smaller than 0, versionA is smaller than B.this.versionCompareHandle = function (versionA, versionB) {cc.log("JS Custom Version Compare: version A is " + versionA + ', version B is ' + 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;}};console.log(">>>[this._storagePath:]"+this._storagePath)// Init with empty manifest url for testing custom manifestthis._am = new jsb.AssetsManager('', this._storagePath, this.versionCompareHandle);console.log(cc.sys.ENABLE_GC_FOR_NATIVE_OBJECTS+">>>[_am:]",this._am)if (!cc.sys.ENABLE_GC_FOR_NATIVE_OBJECTS) {//this._am.retain();}// Setup the verification callback, but we don't have md5 check function yet, so only print some message// Return true if the verification passed, otherwise return falsethis._am.setVerifyCallback(function (path, asset) {// When asset is compressed, we don't need to check its md5, because zip file have been deleted.var compressed = asset.compressed;// Retrieve the correct md5 value.var expectedMD5 = asset.md5;// asset.path is relative path and path is absolute.var relativePath = asset.path;// The size of asset file, but this value could be absent.var size = asset.size;if (compressed) {cc.log("Verification passed : " + relativePath);return true;}else {cc.log("Verification passed : " + relativePath + ' (' + expectedMD5 + ')');return true;}});cc.log("Hot update is ready, please check or directly update.");if (cc.sys.os === cc.sys.OS_ANDROID) {// Some Android device may slow down the download process when concurrent tasks is too much.// The value may not be accurate, please do more test and find what's most suitable for your game.this._am.setMaxConcurrentTask(2);cc.log("Max concurrent tasks count have been limited to 2");}// this.checkUpdate();},onDestroy: function () {if (this._updateListener) {cc.eventManager.removeListener(this._updateListener);this._updateListener = null;}if (this._am && !cc.sys.ENABLE_GC_FOR_NATIVE_OBJECTS) {// this._am.release();}}
});

在项目根目录下添加 version_generator.js文件  用node 执行文件来生成project.manifest version.manifest两个文件 (需要安装node)

node version_generator.js

version_generator 内容如下:

/*** 此模块用于热更新工程清单文件的生成*/var fs = require('fs');
var path = require('path');
var crypto = require('crypto');var manifest = {//服务器上资源文件存放路径(src,res的路径)packageUrl: 'http://192.192.19.199/HotUpdate/',//服务器上project.manifest路径remoteManifestUrl: 'http://192.192.19.199/HotUpdate/project.manifest',//服务器上version.manifest路径remoteVersionUrl: 'http://192.192.19.199/HotUpdate/version.manifest',version: '1.0.2',assets: {},searchPaths: []
};//生成的manifest文件存放目录
var dest = 'assets/';
//项目构建后资源的目录
var src = 'build/jsb-default/';/*** node version_generator.js -v 1.0.0 -u http://your-server-address/tutorial-hot-update/remote-assets/ -s native/package/ -d assets/*/
// Parse arguments
var i = 2;
while ( i < process.argv.length) {var arg = process.argv[i];switch (arg) {case '--url' :case '-u' :var url = process.argv[i+1];manifest.packageUrl = url;manifest.remoteManifestUrl = url + 'project.manifest';manifest.remoteVersionUrl = url + 'version.manifest';i += 2;break;case '--version' :case '-v' :manifest.version = process.argv[i+1];i += 2;break;case '--src' :case '-s' :src = process.argv[i+1];i += 2;break;case '--dest' :case '-d' :dest = process.argv[i+1];i += 2;break;default :i++;break;}
}function readDir (dir, obj) {var stat = fs.statSync(dir);if (!stat.isDirectory()) {return;}var subpaths = fs.readdirSync(dir), subpath, size, md5, compressed, relative;for (var i = 0; i < subpaths.length; ++i) {if (subpaths[i][0] === '.') {continue;}subpath = path.join(dir, subpaths[i]);stat = fs.statSync(subpath);if (stat.isDirectory()) {readDir(subpath, obj);}else if (stat.isFile()) {// Size in Bytessize = stat['size'];md5 = crypto.createHash('md5').update(fs.readFileSync(subpath, 'binary')).digest('hex');compressed = path.extname(subpath).toLowerCase() === '.zip';relative = path.relative(src, subpath);relative = relative.replace(/\\/g, '/');relative = encodeURI(relative);obj[relative] = {'size' : size,'md5' : md5};if (compressed) {obj[relative].compressed = true;}}}
}var mkdirSync = function (path) {try {fs.mkdirSync(path);} catch(e) {if ( e.code != 'EEXIST' ) throw e;}
}// Iterate res and src folder
readDir(path.join(src, 'src'), manifest.assets);
readDir(path.join(src, 'res'), manifest.assets);var destManifest = path.join(dest, 'project.manifest');
var destVersion = path.join(dest, 'version.manifest');mkdirSync(dest);fs.writeFile(destManifest, JSON.stringify(manifest), (err) => {if (err) throw err;console.log('Manifest successfully generated');
});delete manifest.assets;
delete manifest.searchPaths;
fs.writeFile(destVersion, JSON.stringify(manifest), (err) => {if (err) throw err;console.log('Version successfully generated');
});

回到编辑界面 如下图 把刚刚用version_generator新生成到asset的project.mainfest 文件拖进去

如下图 文件绑定控件使用新写法

version_generator 文件中 url路径需要修改成你使用的资源服务器路径 ,dest 和src路径也要对应,把该文件中的version 版本号改成1.0.0,打一个旧包,安装在手机上,

(我看的教程里需要修改main.js文件附加上搜索路径的设置,我试了下2.0版本不添加也可以正常热更 )

随意改个场景图片,然后修改version_generator文件的版本号为1.0.1,生成新的mainfest文件 再次打包新的apk,

构建编译成功后 把\build\jsb-default 文件下的 res src文件  和assets 文件夹下的 project.manifest version.manifest添加到资源服务器 如下图:

运行1.0.0旧版本即可更新。

然后是大厅子游戏模式,创建子游戏demo 和大厅一样添加version_generator.js 修改 main.js文件

(function () {'use strict';if (window.jsb) {/// 1.初始化资源Lib路径Root.var subgameSearchPath = (jsb.fileUtils ? jsb.fileUtils.getWritablePath() : '/')+'ALLGame/kaixinxiaoxiaole/';/// 2.subgame资源未映射,则初始化资源映射表,否则略过映射.if(!cc.HallAndSubGameGlobal){cc.HallAndSubGameGlobal = {}}if(!cc.HallAndSubGameGlobal.subgameGlobal){cc.HallAndSubGameGlobal.subgameGlobal = {};/// 加载settings.jswindow.require(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';}console.log('>>>导入project文件《《《')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);});}
})();

把 main.js 拷贝到打包好的src目录下,还是和大厅一样把 res src 和两个manifes文件拷贝到资源服务器,如下图

到这里子游戏就打包上传好了,需要在大厅项目里添加subgameManager,内容是:

const SubgameManager = {_storagePath: [],_getfiles: function(name, type, downloadCallback, finishCallback) {console.log(">>>_getfiles")this._storagePath[name] = ((jsb.fileUtils ? jsb.fileUtils.getWritablePath() : '/') + 'ALLGame/' + name);this._downloadCallback = downloadCallback;this._finishCallback = finishCallback;this._fileName = name;/// 替换该地址var UIRLFILE = "http://192.168.10.121/HotUpdate/" + 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));this._am.setEventCallback(this._updateCb.bind(this));} else if (type == 2) {//this._updateListener = new jsb.EventListenerAssetsManager(this._am, this._checkCb.bind(this));this._am.setEventCallback(this._checkCb.bind(this));} else {//this._updateListener = new jsb.EventListenerAssetsManager(this._am, this._needUpdate.bind(this));this._am.setEventCallback(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.systemEvent.registerSystemEvent//  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) {console.log(">>>downloadSubgame")this._getfiles(name, 2, progress, finish);},/*** 进入子游戏* @param {string} name - 游戏名*/enterSubgame: function(name) {if (!this._storagePath[name]) {this.downloadSubgame(name);return;}cc.director.emit('updateEnd',this._storagePath[name])window.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) {console.log(">>>needUpdateSubgame")this._getfiles(name, 3, failCallback, isUpdateCallback);},
};module.exports = SubgameManager;

然后修改helloworld文件如下:

const SubgameManager = require('SubgameManager');cc.Class({extends: cc.Component,properties: {downloadBtn: {default: null,type: cc.Node},downloadLabel: {default: null,type: cc.Label}},updateEndCall(data){{this.downloadLabel.string = data}},onLoad: function () {cc.director.on('updateEnd',(e)=>{this.updateEndCall(e)},this);let self = thisconst name = 'kaixinxiaoxiaole'; this.downloadBtn.on('click',function(){//下载子游戏/更新子游戏this.downloadLabel.string = "开始下载子游戏";SubgameManager.downloadSubgame(name, (progress) => {console.log('下载进度-0')if (isNaN(progress)) {progress = 0;this.downloadLabel.string = "下载进度-0";}this.downloadLabel.string = "资源下载中   " + parseInt(progress * 100) + "%";}, function(success) {if (success) {console.log('can enterSubGame')self.downloadLabel.string = "启动游戏 " SubgameManager.enterSubgame('kaixinxiaoxiaole');} else {// this.downloadLabel.string = "下载失败";cc.log('下载失败');}});},this)//判断子游戏有没有下载if (SubgameManager.isSubgameDownLoad(name)) {//已下载,判断是否需要更新SubgameManager.needUpdateSubgame(name, (success) => {if (success) {this.downloadLabel.string = "子游戏需要更新";} else {this.downloadLabel.string = "子游戏不需要更新";}}, () => {this.downloadLabel.string = "出错了";cc.log('出错了');});} else {this.downloadLabel.string = "子游戏未下载";}},
});

编译大厅并安装运行就可以测试了

一些小坑:

1 jsb.EventListenerAssetsManager方法已不可用 可以使用 setEventCallback方法

2 cc.eventManager.addListener 方法也一样弃用,官方文档上没有找到说明,直接注释掉就可以了

3 main.js 文件 加载settings.js 的时候需要注意使用 window.require

4 游戏路径一定要对应

子游戏的main.js千万不要重复加载大厅已经require 的文件

测试用的资源服务器推荐hfs 相当轻便,

测试时可以使用android studio 来实时查看日志。

--------------------

2019/12/4

实用只到大厅热更新,子游戏热更新,大厅子游戏切换这里,用到项目里还需要填uuid冲突,公共和私有资源下载。。。等一堆坑

教程参考:

https://www.jianshu.com/p/094cd0e95e55

https://www.jianshu.com/p/fe54ca980384

北上杭码农聚集地,qq群号: 199678137  欢迎大佬小白进群讨论技术问题(〃'▽'〃)

Laya 真香 哈哈哈哈 20200402

cocoscreator2.0.10 热更新大厅子游戏模式 学习使用记录相关推荐

  1. 微信7.0.10内测更新!除了适配暗黑模式,还有这些实用功能!

    微信7.0.10内测更新,其实本没打算写这篇文章~ 因为,我自己在使用魅族手机,早都可以愉快地使用暗黑模式了~对于,这次更新适配暗黑模式,并没有那么兴奋! 不过,我还是第一时间下载更新体验一下,说说有 ...

  2. 阿里云Sophix 3.0版本热更新快速入门

    废话不多说 什么优缺点 大家肯定已经比较过,就想找到一种既方便有安全而且快捷的集成方案,一下满满的干活: 1:首先登录阿里云地址: https://www.aliyun.com/product/hot ...

  3. Vite创建的Vue3.0项目热更新失效,写的代码没效果

    一.项目技术栈:Vue3+TypeScript+Vite 二.问题描述: 项目之前好好的,突然代码热更新失效,即:每次写完 CSS 代码,怎么刷新都不行,需要重新 npm run dev,新增的代码才 ...

  4. 【Unity】热更新插件【ULua】学习教程整理

    前言 IOS不能热更新,不是因为不能用反射,是因为 System.Reflection.Assembly.Load 无法使用 System.Reflection.Emit 无法使用 System.Co ...

  5. 【博学谷学习记录】超强总结,用心分享 | 狂野大数据HDFS~个人学习复习记录~PART-01

    namenode存放文件的元数据信息,文件的block存储在哪些主机,权限,以及文件被切分成几个block,默认一个block128M,副本机制使得每个文件或者每个block存多个一模一样的,默认备份 ...

  6. android 8.0设置横幅通知,安卓微信8.0.3正式更新:新增公告横幅提醒等8大更新!...

    | 本文由我原创,来源首发及正式版下载 → http://dwz.date/eTeZ "安卓微信8.0.3正式版最新最快的更新解析~" 经过了差不多的一周内测,就在昨天安卓微信8. ...

  7. Python在游戏中的热更新

    介绍: 热更新,就是在服务器不重启的的情况下,对游戏增加新的功能或者修复出现bug 的代码.游戏更新迭代速度快,催生了热更技术的需求,在我经历过的游戏项目中,无论是服务端还是客户端,版本的更新都是围绕 ...

  8. 乐变黄杲:当前如何选择App热更新服务

    在2017年6月这个时间点,我们有必要谈谈热更新这个技术到底何去何从. \\ 上半年苹果的两次警告,通知iOS开发者在6月12日前移除热更新相关代码,否则将会下架相关App,一时间风声鹤唳,那么App ...

  9. Unity热更新技术整理

    一.热更新学习介绍 1.什么是热更新 举例来说: 游戏上线后,玩家下载第一个版本(70M左右或者更大),在运营的过程中,如果需要更换UI显示,或者修改游戏的逻辑,这个时候,如果不使用热更新,就需要重新 ...

最新文章

  1. struts.properties配置详解
  2. [转]url传递中文的解决方案总结
  3. java heroku_使用Spring Boot和Heroku在20分钟内完成Java的单点登录
  4. Windows安装Python包下载工具pip遇到的问题
  5. php设置cookie值,PHP如何设置和取得Cookie值
  6. Linux设备驱动程序(第三版)/深入理解计算机系统(原书第2版)/[Android系统原理及开发要点详解].(韩超,梁泉)百度云盘下载
  7. 4 error C2220: 警告被视为错误 - 没有生成“object”文件 (..\..\src\caffe\util\math_functions.cpp)
  8. ggplot2 | ggplot2作图语法入门
  9. 云栖日报丨收购中天微,阿里芯了解一下!
  10. 线程并发与进程并发各有什么you_【白话计算机基础】并发与并行,进程、线程与协程, 同步与异步,阻塞与非阻塞...
  11. word域代码 乱序
  12. python-坦克射击飞机
  13. Reactor 反应堆
  14. 下列哪种软件不能编辑html语言,强国挑战答题答案:下面哪种语言最适合用来编写网页中嵌入运行的应用程序?()...
  15. js的slice,splice,split的使用
  16. 今晚 8 点,开发者赏金计划正式开启
  17. Huawei EROFS 初探
  18. 共享单车之摩拜与ofo优缺大盘点
  19. 阿里云盘的目录文件列表程序Alist
  20. 任正非:华为考虑向苹果等竞争对手销售5G芯片

热门文章

  1. 【自考总结】走过的弯路,都是你成长的旅途
  2. ProcessDB实时/时序数据库——C/C++操作数据库对象
  3. shiro配置 在springboot中前后端分离中,集成shiro认证授权框架
  4. python爬取小说代码_Python scrapy爬取小说代码案例详解
  5. 如何在vue项目中设置首页
  6. SpringMVC拦截器源码解析
  7. SharedUserData
  8. 诺基亚今天推出6500系列两款新机---诺基亚 6500 classic 和诺基亚 6500 slide
  9. WAR3 天地劫技能代码节选004
  10. ECharts饼图显示人民币符号千分位逗号隔开