Cocos Creator 热更新 [Lv.1]
Posted VermillionTear
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Cocos Creator 热更新 [Lv.1] 相关的知识,希望对你有一定的参考价值。
摘要
承接上文 Cocos Creator 热更新 [Lv.1] (1)
在上文中对热更新进行了相关的初始化工作,本文在此基础上实现热更新的检测以及更新。
正式开始
检测新版本
首先创建一个按钮,用于检测是否有新的版本。
在hotUpdateController.js
中实现功能。
...
...
let _isChecking = false // 检测更新的标志。
cc.Class({
...
properties: {
...
checkButton: cc.Node, // 检测按钮。
},
onLoad () {
...
if (!_am.getLocalManifest() || !_am.getLocalManifest().isLoaded()) {
this.checkButton.active = false // 如果热更新模块初始化失败,则隐藏检测按钮。
...
} else {
...
this.checkButton.active = true // 热更新模块初始化成功,显示检测按钮。
...
}
},
...
eventCallback: function (event) {
...
switch (event.getEventCode())
{
case jsb.EventAssetsManager.ERROR_NO_LOCAL_MANIFEST:
...
_isChecking = false
...
case jsb.EventAssetsManager.ERROR_DOWNLOAD_MANIFEST:
case jsb.EventAssetsManager.ERROR_PARSE_MANIFEST:
...
_isChecking = false
...
case jsb.EventAssetsManager.ALREADY_UP_TO_DATE: // 已经是最新版本。
this.info.string = "Already up to date with the latest remote version."
this.checkButton.active = false // 已经是最新版本,检测按钮隐藏。
_isChecking = false
break
case jsb.EventAssetsManager.NEW_VERSION_FOUND: // 发现了新版本。
// 获取并显示的新版本的大小。
this.info.string = 'New version found, please try to update. (' + _am.getTotalBytes() + ')'
this.checkButton.active = false // 检测到了新版本,检测按钮隐藏,之后应该显示更新按钮。
_isChecking = false
break
...
}
},
isBusy: function () {
let busy = false
if (_isChecking) {
this.info.string = 'Checking ...'
busy = true
}
return busy
},
checkUpdate: function () {
// 判断是否正在进行热更的某个步骤。
if (this.isBusy()) {
return
}
_isChecking = true
_am.checkUpdate() // 热更模块检测新版本。
},
});
将节点挂载到脚本对应变量上。
指定检测
按钮点击后的回调函数。
创建服务器
为了能检测到新版本,我们需要创建一个新版本,放到服务器上,并且启动服务器。
再次构建工程,然后使用version_generator.js
脚本生成1.0.1
版本的project.manifest
和version.manifest
。
$ node version_generator.js -v 1.0.1 -u http://127.0.0.1:1234/ -s D:/build/jsb-link/ -d assets/
Version successfully generated
Manifest successfully generated
进入remote_assets
目录,我们将以此作为服务器的根目录。
将构建好的assets
和src
目录以及新生成的project.manifest
和version.manifest
均拷贝到remote_assets
目录中。
这样服务器的资源就配置好了,应该是如下结构,
在remote_assets
目录下启动SimpleHTTPServer
,
$ python -m SimpleHTTPServer 1234
Serving HTTP on 0.0.0.0 port 1234 ...
取巧的方法
我们为了创建服务器的版本,把工程中的manifest
文件也变成了1.0.1
版本。而如果想要检测到更新,就需要回退到1.0.0
版本。
按照正规的流程,你可以改动一下界面或是代码,然后构建工程,然后用脚本生成1.0.0
版本的manifest
文件。
但这里也可以采用一个取巧的方法,对现在的工程不做任何改动,直接用脚本生成1.0.0
版本的manifest
文件。
$ node version_generator.js -v 1.0.0 -u http://127.0.0.1:1234/ -s D:/build/jsb-link/ -d assets/
Version successfully generated
Manifest successfully generated
然后打开刚生成的prject.mainifest
,会看到类似于如下的信息,
{
"packageUrl":"http://127.0.0.1:1234/",
"remoteManifestUrl":"http://127.0.0.1:1234/project.manifest",
"remoteVersionUrl":"http://127.0.0.1:1234/version.manifest",
"version":"1.0.0",
"assets":{
"assets/main/native/a8/a8027877-d8d6-4645-97a0-52d4a0123dba.png":{"size":82,"md5":"cea68f0d7cba38440224f6f74531e2d8"},
"assets/main/native/b4/b43ff3c2-02bb-4874-81f7-f2dea6970f18.png":{"size":1114,"md5":"83fcc9912e01ae5411c357651fb8b1cf"},
"assets/main/native/e8/e851e89b-faa2-4484-bea6-5c01dd9f06e2.png":{"size":1082,"md5":"90cf45d059d0408bec327f66eae5764c"}},
"searchPaths":[]
}
当然,如果你直接打开,肯定是没有换行的,密密麻麻一大片。我这里为了方便讲解,做了一些换行。
看到每个资源后面都有个md5
字段了吗,他就是使用资源内存储的数据进行md5
运算得来的,同时他也标识着当前资源的版本。如果资源的md5
发生变化,热更模块就会认定该资源有新的版本。
所以基于这点,我们只需要修改某个资源的md5
值,就能骗过热更模块,让其认定有新的热更版本。
这里就找最后一个png后面的md5
,修改其中的一个字符就可以了。
比如我这里把90cf45d059d0408bec327f66eae5764c
修改成了91cf45d059d0408bec327f66eae5764c
把第二个字符从0改成了1。
接下来运行Demo,点击检查更新
按钮,就会发现检测到了新的版本。
使用这种方法测试,一是为了方便,二是为了避免一些没必要的错误。
不然每次部署服务器资源后,为了回退到原先的版本,都需要改动工程,重新构建,才能使用version_generator.js
脚本生成本地的manifest
文件。
改动的步骤越多,出错的几率越大。
检测到新版本后,writablePath下的变动
热更模块检测到新版本的同时,在writablePath
目录下创建了一些文件。
hotUpdate // 我们指定的热更目录,也就是 Storage Path。
hotUpdate_temp // 热更的临时目录。在热更成功之前,所有下载到的资源都存储在这里。
|- project.manifest.temp
|- version.manifest
打开project.manifest.temp
看看,实际上就是服务器上的project.manifest
。
为什么后面要有个.temp
,是因为实际需要热更的文件还没有下载下来,也就是热更还没有完全成功。等到热更新成功,这个project.manifest.temp
就会被重命名为project.manifest
,然后放到hotUpdate
目录下。project.manifest.temp
目录中所有热更下来的资源也会被放到hotUpdate
目录下,hotUpdate_temp
目录随即会被删除。
更新新版本
创建一个按钮,用于更新。
在hotUpdateController.js
中实现按钮的功能。
...
let _isUpdating = false // 更新的标志。
cc.Class({
...
properties: {
...
updateButton: cc.Node, // 更新按钮。
},
onLoad () {
...
this.updateButton.active = false // 更新按钮默认不显示,只有检测到新版本才显示。
},
...
eventCallback: function (event) {
...
switch (event.getEventCode())
{
...
case jsb.EventAssetsManager.ERROR_UPDATING:
this.info.string = 'Asset update error: ' + event.getAssetId() + ', ' + message
_isUpdating = false
break
case jsb.EventAssetsManager.ERROR_DECOMPRESS:
this.info.string = message
_isUpdating = false
break
...
case jsb.EventAssetsManager.NEW_VERSION_FOUND: // 发现新版本。
...
this.updateButton.active = true // 显示更新按钮。
...
break
case jsb.EventAssetsManager.UPDATE_FINISHED: // 更新成功。
this.info.string = 'Update finished. ' + message
this.updateButton.active = false // 更新成功,隐藏更新按钮。之后会显示重启按钮。
_isUpdating = false
break
case jsb.EventAssetsManager.UPDATE_FAILED: // 更新失败。
this.info.string = 'Update failed. ' + message
this.updateButton.active = false // 更新失败,隐藏更新按钮。之后会显示重试按钮。
_isUpdating = false
break
default:
return
}
},
isBusy: function () {
...
if (_isChecking) {
...
} else if (_isUpdating) {
this.info.string = 'Updating ...'
busy = true
}
...
},
...
doUpdate: function () {
if (this.isBusy()) {
return
}
_isUpdating = true
_am.update() // 热更模块更新版本。
},
...
});
将节点挂载到脚本对应变量上。
指定更新
按钮点击后的回调函数。
运行起来,检测到更新后点击立即更新
按钮。一瞬间,发现已经更新成功了。
更新了新版本后,writablePath下的变动
hotUpdate
|- assets
|- ...
|- project.manifest
热更新成功了,hotUpdate_temp
目录下的文件都放到了hotUpdate
目录中,同时hotUpdate_temp
目录被删除。
如果此时重新运行程序,会发现提示已经是最新版本。
这是因为hotUpdate
目录中存在project.manifest
文件,并且与服务器的进行比对,发现版本号一致,都是1.0.1
。
如果此时想重新测试,需要删除掉hotUpdate
目录。
显示出来的Storage Path
就是为了方便找到热更目录。
显示进度
创建文本框以及进度条。
在hotUpdateController.js
中实现功能。
...
cc.Class({
...
properties: {
...
fileProgress: cc.ProgressBar, // 总文件数进度条。
filePercent: cc.Label, // 总文件数百分比。
byteProgress: cc.ProgressBar, // 总字节数进度条。
bytePercent: cc.Label, // 总字节数百分比。
},
onLoad () {
...
this.fileProgress.progress = 0
this.byteProgress.progress = 0
this.filePercent.string = ''
this.bytePercent.string = ''
},
...
eventCallback: function (event) {
...
switch (event.getEventCode())
{
...
case jsb.EventAssetsManager.UPDATE_PROGRESSION:
this.byteProgress.progress = event.getPercent() // 更新总字节数进度。
this.fileProgress.progress = event.getPercentByFile() // 更新总文件数进度。
// 已下载的文件数 / 总文件数
this.filePercent.string = event.getDownloadedFiles() + ' / ' + event.getTotalFiles()
cc.log('filePercent.string: ' + this.filePercent.string)
// 已下载的字节数 / 总字节数
this.bytePercent.string = event.getDownloadedBytes() + ' / ' + event.getTotalBytes()
cc.log('bytePercent.string: ' + this.bytePercent.string)
if (message) {
this.info.string = 'Updated file: ' + message
cc.log(event.getPercent() / 100 + '% : ' + message)
}
break
case jsb.EventAssetsManager.UPDATE_FINISHED:
this.byteProgress.progress = 1
this.fileProgress.progress = 1
this.filePercent.string = ''
this.bytePercent.string = ''
...
...
}
},
...
});
将节点挂载到脚本对应变量上。
运行起来,一瞬间进度就满了。
重试更新新版本
如果在热更新的过程中,由于网络等原因导致热更新失败。热更新模块也提供了相应的函数重新尝试更新。
首先创建一个重试按钮。
在hotUpdateController.js
中实现按钮的功能。
...
cc.Class({
...
properties: {
...
retryButton: cc.Node, // 重试按钮。
},
onLoad () {
...
this.retryButton.active = false // 重试按钮默认不显示,只有更新失败才显示。
},
...
eventCallback: function (event) {
...
switch (event.getEventCode())
{
...
case jsb.EventAssetsManager.UPDATE_FINISHED:
...
this.retryButton.active = false // 更新成功,隐藏重试按钮。
...
break
case jsb.EventAssetsManager.UPDATE_FAILED:
...
this.retryButton.active = true // 更新失败,显示重试按钮。
...
break
...
}
},
...
retry: function () {
if (this.isBusy()) {
return
}
_isUpdating = true
_am.downloadFailedAssets() // 热更模块重试更新。
},
...
});
将节点挂载到脚本对应变量上。
指定重试
按钮点击后的回调函数。
运行起来,先检测更新。然后关闭服务器,再点立即更新。
等待一段时间后,会发现提示更新失败,与此同时重试
按钮也显示了出来。
重启服务器,再点重试
按钮,提示更新成功。
划重点
- 检测新版本
AssetsManager.checkUpdate()
。 - 更新新版本
AssetsManager.update()
。 - 显示进度
event.getPercent()
,event.getPercentByFile()
,event.getDownloadedFiles()
,event.getTotalFiles()
,event.getDownloadedBytes()
,event.getTotalBytes()
。 - 重试更新
AssetsManager.downloadFailedAssets()
。
以上是关于Cocos Creator 热更新 [Lv.1] 的主要内容,如果未能解决你的问题,请参考以下文章