您的位置:首页 > 移动开发 > Cocos引擎

Cocos Creator热更新操作(超详细)避免踩坑

2020-07-27 15:24 2116 查看

Cocos Creator热更新操作(超详细)

解决热更新进入后第一次有效,之后再次进入又恢复到之前版本

1.安装插件


可以安装到项目中

也可以安装到本地 (全局)

安装完成之后要重启一下cocos

2.代码

1.创建更新场景

用来跳转,更新完成之后就执行什么代码。(这里是执行完成之后把按钮显示出来,点击跳入main场景)

在这里写了一个进度条来查看进度。里面部分配置文件的 由来后面有说


)

2.客户端代码

hot.js

// jsb.EventAssetsManager.ERROR_NO_LOCAL_MANIFEST = 0;
// jsb.EventAssetsManager.ERROR_DOWNLOAD_MANIFEST = 1;
// jsb.EventAssetsManager.ERROR_PARSE_MANIFEST = 2;
// jsb.EventAssetsManager.NEW_VERSION_FOUND = 3;
// jsb.EventAssetsManager.ALREADY_UP_TO_DATE = 4;
// jsb.EventAssetsManager.UPDATE_PROGRESSION = 5;
// jsb.EventAssetsManager.ASSET_UPDATED = 6;
// jsb.EventAssetsManager.ERROR_UPDATING = 7;
// jsb.EventAssetsManager.UPDATE_FINISHED = 8;
// jsb.EventAssetsManager.UPDATE_FAILED = 9;
// jsb.EventAssetsManager.ERROR_DECOMPRESS = 10;

cc.Class({
extends: cc.Component,

properties: {
manifestUrl: {
type: cc.Asset,
default: null
},
_updating: false,
_canRetry: false,
_storagePath: '',
},

checkCb(event) {
let isUpdate = false;
console.log("checkCb", event.getEventCode());
switch (event.getEventCode())
{
case jsb.EventAssetsManager.ALREADY_UP_TO_DATE:
cc.systemEvent.emit("hot_is_new");   // 已经是最新版本 调用对应代码段(跳转主场景)
break;
case jsb.EventAssetsManager.NEW_VERSION_FOUND:  // 发现新版本
cc.systemEvent.emit("hot_update_begin");    // 开始更新,将显示(一个给用户看的label)改为正在更新。
isUpdate = true;
break;
case jsb.EventAssetsManager.UPDATE_PROGRESSION:
cc.systemEvent.emit("hot_check_updating");
default:
// cc.systemEvent.emit("hot_check_failed",event.getEventCode());    // 检测失败
break;
}

this._am.setEventCallback(null);
this._checkListener = null;
this._updating = false;

if (isUpdate){
console.log("updateTimes", this._updateTimes);
if (this._updateTimes > 3){
cc.systemEvent.emit("hot_update_out_times");    // 更新超过次数
}
else{
this._updateTimes++;
this.hotUpdate();
}
}
},

updateCb(event) {       //  event.getEventCode() 数字0-10 对应10个不同的状态,执行对应的代码
var needRestart = false;
var failed = false;
console.log("updateCb", event.getEventCode());
switch (event.getEventCode())
{
case jsb.EventAssetsManager.UPDATE_PROGRESSION:
let data = {
curFile:event.getDownloadedFiles(),
totalFile:event.getTotalFiles(),
curByte:event.getDownloadedBytes(),
totalByte:event.getTotalBytes(),
};
cc.systemEvent.emit("hot_update_progress", data);   // 更新进度
break;
case jsb.EventAssetsManager.ALREADY_UP_TO_DATE:
cc.systemEvent.emit("hot_is_new");   // 已经是最新版本
failed = true;
break;
case jsb.EventAssetsManager.UPDATE_FINISHED:    // 更新完成,重启游戏
failed = true;
needRestart = true;
break;
default:
this._updating = false;
break;
}

if (failed) {
this._am.setEventCallback(null);
this._updateListener = null;
this._updating = false;
}

if (needRestart) {
this.isUpdate = false;
this._am.setEventCallback(null);
this._updateListener = null;
// Prepend the manifest's search path
var searchPaths = jsb.fileUtils.getSearchPaths();
var newPaths = this._am.getLocalManifest().getSearchPaths();
Array.prototype.unshift.apply(searchPaths, newPaths);
cc.sys.localStorage.setItem('HotUpdateSearchPaths', JSON.stringify(searchPaths));
jsb.fileUtils.setSearchPaths(searchPaths);

cc.audioEngine.stopAll();
cc.game.restart();
}
},

loadCustomManifest() {
if (this._am.getState() === jsb.AssetsManager.State.UNINITED) {
var manifest = new jsb.Manifest(customManifestStr, this._storagePath);
this._am.loadLocalManifest(manifest, this._storagePath);
console.log('Using custom manifest');
}
},

retry() {
if (!this._updating && this._canRetry) {
this._canRetry = false;

console.log('Retry failed Assets...');
this._am.downloadFailedAssets();
}
},

checkUpdate() {
if (this._updating) {
console.log('Checking or updating ...');
return;
}
if (this._am.getState() === jsb.AssetsManager.State.UNINITED) {
// Resolve md5 url
var url = this.manifestUrl.nativeUrl;
if (cc.loader.md5Pipe) {
url = cc.loader.md5Pipe.transformURL(url);
}
this._am.loadLocalManifest(url);
}
if (!this._am.getLocalManifest() || !this._am.getLocalManifest().isLoaded()) {
console.log('Failed to load local manifest ...');
cc.systemEvent.emit("hot_no_manifest");
return;
}
this._am.setEventCallback((event)=>{ this.checkCb(event); });

this._am.checkUpdate();
this._updating = true;
},

hotUpdate() {
if (this._am && !this._updating) {
this._am.setEventCallback((event)=>{ this.updateCb(event); });

if (this._am.getState() === jsb.AssetsManager.State.UNINITED) {
// Resolve md5 url
var url = this.manifestUrl.nativeUrl;
if (cc.loader.md5Pipe) {
url = cc.loader.md5Pipe.transformURL(url);
}
this._am.loadLocalManifest(url);
}

this._failCount = 0;
this._am.update();
this._updating = true;
}
},

// use this for initialization
onLoad() {
// Hot update is only available in Native build
if (!cc.sys.isNative) {
return;
}
this._updateTimes = 0;
this._storagePath = ((jsb.fileUtils ? jsb.fileUtils.getWritablePath() : '/') + 'blackjack-remote-asset');

// 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 = (versionA, versionB)=>{
console.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 {
setTimeout(()=>{    // 版本不同
this.hotUpdate();
},500);
return a - b;
}
}
setTimeout(()=>{    // 版本相同
cc.systemEvent.emit("hot_is_new");
},100);
if (vB.length > vA.length) {
return -1;
}
else {
return 0;
}
};

// Init with empty manifest url for testing custom manifest
this._am = new jsb.AssetsManager('', this._storagePath, this.versionCompareHandle);

// 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((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) {
return true;
}
else {
return true;
}
});

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(1);
}

cc.systemEvent.on("hot_auto_update", ()=>{
this.checkUpdate();
});
},

onDestroy() {
console.log('destroy hot');
if (this._updateListener) {
this._am.setEventCallback(null);
this._updateListener = null;
}
cc.systemEvent.off("hot_auto_update");
}
});

panelUpdate.js

cc.Class({
extends: cc.Component,

properties: {
},

onClick(){
cc.systemEvent.emit("hot_auto_update");
},

// LIFE-CYCLE CALLBACKS:

// onLoad () {},

start () {
this.info = this.node.getChildByName("info").getComponent(cc.Label);
this.filePro = this.node.getChildByName("filePro").getComponent(cc.ProgressBar);
this.fileProLabel = this.filePro.node.getChildByName("pro").getComponent(cc.Label);
this.downPro = this.node.getChildByName("downPro").getComponent(cc.ProgressBar);
this.downProLabel = this.downPro.node.getChildByName("pro").getComponent(cc.Label);

this.btnStart = this.node.getChildByName("btnStart");
this.btnStart.active = false;
this.btnStart.on(cc.Node.EventType.TOUCH_START,()=>{    //更新完成后对应跳转的场景
cc.director.loadScene("main");
})

cc.systemEvent.on("hot_is_new", ()=>{
console.log("切换场景");
this.btnStart.active = true;
});
cc.systemEvent.on("hot_update_begin", ()=>{
console.log("开始更新");
this.info.string = "开始更新";
});
cc.systemEvent.on("hot_check_updating", ()=>{
console.log("正在更新");
this.info.string = "正在更新";
});
cc.systemEvent.on("hot_check_failed", (e)=>{
console.log("检测更新失败");
this.info.string = "检测更新失败";
});
cc.systemEvent.on("hot_update_out_times", ()=>{
console.log("更新次数达到最大");
this.info.string = "更新次数达到最大";
});
cc.systemEvent.on("hot_update_progress", (data)=>{
console.log("更新进度");
let { curFile, totalFile, curByte, totalByte } = data;
this.filePro.progress = curFile / totalFile;
this.downPro.progress = curByte / totalByte;
this.fileProLabel.string = `${curFile}/${totalFile}`;
this.downProLabel.string = `${curByte}/${totalByte}`;
});
cc.systemEvent.on("hot_no_manifest", ()=>{
console.log("没有Manifest文件");
this.info.string = "没有Manifest文件";
});

setTimeout(()=>{
cc.systemEvent.emit("hot_auto_update");
}, 100);
},

// update (dt) {},
});

3.服务端代码

图中红框里的内容是之后生成的压缩包里的内容。

app.js

let express = require("express");
let expressWS = require("express-ws");
let path = require("path");

let app = express();
app.all('*', function(req, res, next) {
res.header("Access-Control-Allow-Origin", "*");
next();
});
// expressWS(app);
app.use(express.static("static"));
app.listen(7714);

4.更新原理

在第一个版本中,客户端和服务端都需要配置生成的热更新文件。(具体操作在后面)
客户端

服务端

服务端和客户端的 project 和 version 是一样的。

在Canvas上绑定两个东西,一个是hot热更新脚本。

另外一个一开始没有,在后面 构建完=>热更新工具生成的配置文件中才有

场景布置完毕后,就点击构建

具体操作:后续版本对应之前版本,将服务端开启

手机上打开软件,连接在同一个wifi下。之前配置ip地址的wifi

客户端会用自己的配置文件和服务端的配置文件进行比较,若版本号大于自己,则更新。

新版本就生成了。

3.详细操作(第一版本)

1.构建项目

*在这里只构建,先不进行后续操作


2.打开热更新工具生成配置文件


若在选栏里没有此项,则是没有安装热更新插件或者没有安装成功。

  1. 版本号随便填一个,只是之后的后续的版本号必须比之前大
  2. 资源服务器url是后续版本的客户端更新时,需要连接服务器找到更新的配置文件,若有新版本则会更新。
  3. http://192.168.0.0:7777/hotUpdate ip地址为自己的服务器ip地址,端口号也是自己设置的,在哪个文件夹下面
  4. 点击生成,然后打开目录将生成的压缩包解压

3.将解压的文件在客户端和服务端配置

客户端, 在resources放创一个目录hot,放project与version文件,(两个文件来自生成的压缩包中)

然后保存场景

服务端的,hotUpdate里面覆盖之前全部文件。

4.再次构建


(只构建)

5.编译并且打包app(这一步有一个bug需要改动)

这里有一个bug,若是不修改,在后续版本更新中,部分场景的更新可能只有打开的第一次有,之后再次打开部分场景又恢复到之前版本。(贼坑)

1.找到当前打包项目的 main.js文件

2.将 这几行看起来极其不协调的代码段剪切,贴到后面去

3.放到 window.boot(); 上面

4.记得保存main.js,然后开始编译

这个main.js文件每一次点构建都会重新初始化,不过只需要初始版本的main.js修改就行了

构建 => 修改main.js => 点击编译 => 发安装包给手机。

后续版本不需要再更改main.js了

5.安装app

找到生成的app装到手机上。

这是发布第一版本的操作 (后续版本的操作会简单很多)

4.热更新操作(后续版本)

(1) 更改项目内容

后续版本要更改的内容。

(2) 再次构建


更改完内容后保存,再次构建,只构建!

(3) 再次启动热更新工具,将版本号+1,

版本号只能加 不能减!

(4) 将新生成的服务端配置文件,删掉,换成新版本的配置文件

(5) 开启服务端,手机和服务端确保在同一个局域网下,重启app即更新成功

最后提醒大家测试 的时候记得看看IP地址是不是对的,

服务端和客户端是不是在用一个局域网下面

配置文件有没有覆盖原有文件。

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: