http://docs.cocos.com/creator/manual/zh/advanced-topics/hot-update.html
http://docs.cocos.com/creator/manual/zh/advanced-topics/assets-manager.htmljavascript
客戶端存在一個project.manifest文件,該文件包含幾個信息:php
客戶端經過本地的project.manifest中url,能夠獲取服務器上project.manifest文件,比較二者的version屬性,若是客戶端的version比服務器低,則啓動更新。
更新的內容:assets是文件列表,裏面列出了項目中的完整資源,每一個資源都有md5表示,客戶端根據本地project.manifest中的assets列表和服務器的assets列表對比,下載不一樣的資源到臨時文件夾,若是最後全部資源都正常,則把臨時文件夾的內容替換到本地緩存文件夾中,而且修改優先搜索路徑爲該文件夾。因此重啓遊戲以後的使用的資源優先從緩存文件夾中搜索。html
下載官方範例,解壓。該案例已經把客戶端和服務器的資源都打包好了,更新包在remote-assets文件夾中。
java
服務器--我使用的是nodejs。node
1 .新建一個文件夾nodejs,在nodejs中新建hotUpdate文件夾,在把官方案例中的remote-assets複製到hotUpdate文件夾中。git
2 .在nodejs中新建一個js腳本,腳本內容以下github
var express = require('express'); var path = require('path'); var app = express(); app.use(express.static(path.join(__dirname, 'hotUpdate'))); app.listen(80);
3.在nodejs文件夾下執行node app.js命令,啓動服務器,能夠訪問http://127.0.0.1/remote-assets/project.manifest,若是成功訪問則服務器啓動成功。
express
接下來修改manifest文件裏面的url,有三個文件須要修改,服務器remote-assets中的兩個manifest後綴文件,官方項目assets文件夾下的project.manifest
windows
由於某些緣由,模擬器更新完成,重啓不能使用更新的資源,因此須要編譯成原生,個人電腦不能編譯windows,就不上圖了。打包成apk自測能夠成功更新。緩存
製做新版本
該項目有兩個場景,做爲是新版本,資源存放在服務器上。刪除一個場景做爲舊版本,編譯安裝在手機上。目標是使用舊版本熱更新成新版本,併成功切換場景。
新建一個項目,新建兩個場景,helloworld場景是測試熱更行是否成功的場景(舊版本不存在helloworld,經過更新能夠跳轉到該場景)。hotUpdate場景,該場景中添加兩個進度條,對應字節和文件個數兩種進度,三個label,對應兩種進度以及提示信息,三個按鈕,分別爲,檢查更新,更新,切換場景。
腳本 --copy官方示例
新建一個hotUpdate腳本,添加在Canvas上。腳本中增長五個變量,把場景中對應的節點拖拽上去。,增長三個回調方法,把他綁定在按鈕上。
cc.Class({
extends: cc.Component, properties: { byteLabel: cc.Label, fileLabel: cc.Label, byteProgress: cc.ProgressBar, fileProgress: cc.ProgressBar, label: cc.Label, manifestUrl: cc.RawAsset, }, onLoad() { var self = this; this._storagePath = jsb.fileUtils.getWritablePath(); this._am = new jsb.AssetsManager('', this._storagePath, this.versionCompareHandle); if (!cc.sys.ENABLE_GC_FOR_NATIVE_OBJECTS) { this._am.retain(); } this._am.setVerifyCallback(function (path, asset) { var compressed = asset.compressed; var expectedMD5 = asset.md5; var relativePath = asset.path; var size = asset.size; if (compressed) { self .label.string = "Verification passed : " + relativePath; return true; } else { self .label.string = "Verification passed : " + relativePath + ' (' + expectedMD5 + ')'; return true; } }); this.byteProgress.progress = 0; this.fileProgress.progress = 0; }, hotUpdate() { if (this._am && !this._updating) { this._updateListener = new jsb.EventListenerAssetsManager(this._am, this.updateCb.bind(this)); cc.eventManager.addListener(this._updateListener, 1); if (this._am.getState() === jsb.AssetsManager.State.UNINITED) { this._am.loadLocalManifest(this.manifestUrl); } this._am.update(); this._updating = true; } }, checkUpdata() { if (this._updating) { this.label.string = 'Checking or updating ...'; return; } if (this._am.getState() === jsb.AssetsManager.State.UNINITED) { this._am.loadLocalManifest(this.manifestUrl); } if (!this._am.getLocalManifest() || !this._am.getLocalManifest().isLoaded()) { this.label.string = 'Failed to load local manifest ...'; return; } this._checkListener = new jsb.EventListenerAssetsManager(this._am, this.checkCb.bind(this)); cc.eventManager.addListener(this._checkListener, 1); this._am.checkUpdate(); this._updating = true; }, changeScene() { cc.director.loadScene('helloworld'); }, checkCb: function (event) { switch (event.getEventCode()) { case jsb.EventAssetsManager.ERROR_NO_LOCAL_MANIFEST: this.label.string = "No local manifest file found, hot update skipped."; break; case jsb.EventAssetsManager.ERROR_DOWNLOAD_MANIFEST: case jsb.EventAssetsManager.ERROR_PARSE_MANIFEST: this.label.string = "Fail to download manifest file, hot update skipped."; break; case jsb.EventAssetsManager.ALREADY_UP_TO_DATE: this.label.string = "Already up to date with the latest remote version."; break; case jsb.EventAssetsManager.NEW_VERSION_FOUND: this.label.string = 'New version found, please try to update.'; this.fileProgress.progress = 0; this.byteProgress.progress = 0; 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: this.label.string = 'No local manifest file found, hot update skipped.'; failed = true; break; case jsb.EventAssetsManager.UPDATE_PROGRESSION: this.byteProgress.progress = event.getPercent(); this.fileProgress.progress = event.getPercentByFile(); this.fileLabel.string = event.getDownloadedFiles() + ' / ' + event.getTotalFiles(); this.byteLabel.string = event.getDownloadedBytes() + ' / ' + event.getTotalBytes(); var msg = event.getMessage(); if (msg) { this.label.string = 'Updated file: ' + msg; } break; case jsb.EventAssetsManager.ERROR_DOWNLOAD_MANIFEST: case jsb.EventAssetsManager.ERROR_PARSE_MANIFEST: this.label.string = 'Fail to download manifest file, hot update skipped.'; failed = true; break; case jsb.EventAssetsManager.ALREADY_UP_TO_DATE: this.label.string = 'Already up to date with the latest remote version.'; failed = true; break; case jsb.EventAssetsManager.UPDATE_FINISHED: this.label.string = 'Update finished. ' + event.getMessage(); needRestart = true; break; case jsb.EventAssetsManager.UPDATE_FAILED: this.label.string = 'Update failed. ' + event.getMessage(); this._updating = false; this._canRetry = true; break; case jsb.EventAssetsManager.ERROR_UPDATING: this.label.string = 'Asset update error: ' + event.getAssetId() + ', ' + event.getMessage(); break; case jsb.EventAssetsManager.ERROR_DECOMPRESS: this.label.string = 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; var searchPaths = jsb.fileUtils.getSearchPaths(); var newPaths = this._am.getLocalManifest().getSearchPaths(); Array.prototype.unshift(searchPaths, newPaths); cc.sys.localStorage.setItem('HotUpdateSearchPaths', JSON.stringify(searchPaths)); jsb.fileUtils.setSearchPaths(searchPaths); cc.game.restart(); } }, });
構建完原生項目,打開官方項目文件夾,拷貝出 version_generator.js 文件到helloworld項目的根目錄(以下圖),並在helloworld根目錄打開命令窗口,執行命令,node version_generator.js -v 1.7.0 -u http://127.0.0.1/remote-assets/ -s build/jsb-default/ -d assets/
在assets文件夾下會多出兩個文件,把他們複製到nodejs--hotUpdate--remote-assets文件夾下。
最終遠程資源如圖
在nodejs文件夾下執行node app.js命令,啓動服務器,能夠訪問http://127.0.0.1/remote-assets/project.manifest,若是成功訪問則服務器啓動成功。
刪除helloworld場景,腳本,texture文件夾,以及以前生成的manifest文件。保存以後構建項目,構建選項不要變更
在assets生成兩個manifest文件。把project.manifest拖到屬性檢查器上面。
從新構建原平生臺,在main.js加下面這段代碼,而後編譯
if (cc.sys.isNative) { var hotUpdateSearchPaths = cc.sys.localStorage.getItem('HotUpdateSearchPaths'); if (hotUpdateSearchPaths) { jsb.fileUtils.setSearchPaths(JSON.parse(hotUpdateSearchPaths)); } }
如今已經完成了舊版本的製做。這是我在安卓手機上的效果。