热更新主要用到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 path
var 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;
}
/* descrepate
this._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) {
/*descrepate
this._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 initialization
onLoad: function () {
// Hot update is only available in Native build
if (!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 manifest
this._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 false
this._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 Bytes
size = 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.js
window.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 asset
if (typeof type === 'number') {
entry[1] = assetTypes[type];
}
// retrieve uuid
realEntries[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.js
var 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 = this
const 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