cocoscreator熱更新

首先是官方文檔

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

  • packageUrl:url,服務器更新數據包根目錄;
  • remoteManifestUrl:url, [可選項]服務器上project.manifest文件的url,
  • remoteVersionUrl:url,服務器上version.mainifest文件的url;
  • version:x.x.x,項目版本;
  • assets:{},資源列表;
    key : 資源的相對路徑(相對於資源根目錄)
    md5 : md5 值表明資源文件的版本信息
    compressed: [可選項] 若是值爲 true,文件被下載後會自動被解壓,目前僅支持 zip 壓縮格式
    size: [可選項] 文件的字節尺寸,用於快速獲取進度信息
  • searchPaths:" ",搜索路徑

客戶端經過本地的project.manifest中url,能夠獲取服務器上project.manifest文件,比較二者的version屬性,若是客戶端的version比服務器低,則啓動更新。
更新的內容:assets是文件列表,裏面列出了項目中的完整資源,每一個資源都有md5表示,客戶端根據本地project.manifest中的assets列表和服務器的assets列表對比,下載不一樣的資源到臨時文件夾,若是最後全部資源都正常,則把臨時文件夾的內容替換到本地緩存文件夾中,而且修改優先搜索路徑爲該文件夾。因此重啓遊戲以後的使用的資源優先從緩存文件夾中搜索。html

須要環境

  • nodejs
  • cocoscreator

官方案例

  • 下載官方範例,解壓。該案例已經把客戶端和服務器的資源都打包好了,更新包在remote-assets文件夾中。
    java

     
    image.png

     

  • 服務器--我使用的是nodejs。node

1 .新建一個文件夾nodejs,在nodejs中新建hotUpdate文件夾,在把官方案例中的remote-assets複製到hotUpdate文件夾中。git


 
image.png

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

 
image.png

 

接下來修改manifest文件裏面的url,有三個文件須要修改,服務器remote-assets中的兩個manifest後綴文件,官方項目assets文件夾下的project.manifest
windows

 
image.png

 
image.png

只修改三個url
 
image.png

修改完成以後,打開項目,用模擬器運行看看效果。
點擊檢查更新按鈕
 
image.png

點擊當即更新按鈕
 
image.png

 

由於某些緣由,模擬器更新完成,重啓不能使用更新的資源,因此須要編譯成原生,個人電腦不能編譯windows,就不上圖了。打包成apk自測能夠成功更新。緩存

從零開始

  • 製做新版本
    該項目有兩個場景,做爲是新版本,資源存放在服務器上。刪除一個場景做爲舊版本,編譯安裝在手機上。目標是使用舊版本熱更新成新版本,併成功切換場景。
    新建一個項目,新建兩個場景,helloworld場景是測試熱更行是否成功的場景(舊版本不存在helloworld,經過更新能夠跳轉到該場景)。hotUpdate場景,該場景中添加兩個進度條,對應字節和文件個數兩種進度,三個label,對應兩種進度以及提示信息,三個按鈕,分別爲,檢查更新,更新,切換場景。

     
    image.png

     

  • 腳本 --copy官方示例
    新建一個hotUpdate腳本,添加在Canvas上。腳本中增長五個變量,把場景中對應的節點拖拽上去。,增長三個回調方法,把他綁定在按鈕上。

     
    image.png

     

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(); } }, }); 
  • 打包資源
    首先構建原生
     
    image.png

    構建完成在項目中會生成原生項目,熱更新須要的資源是res,src兩個文件夾以及裏面的內容。
    把nodejs--hotUpdate--remote-assets文件夾下全部東西都刪除,把res,src複製到nodejs--hotUpdate--remote-assets文件夾下。
     
    image.png

構建完原生項目,打開官方項目文件夾,拷貝出 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/

 
image.png

在assets文件夾下會多出兩個文件,把他們複製到nodejs--hotUpdate--remote-assets文件夾下。


 
image.png

最終遠程資源如圖


 
image.png

在nodejs文件夾下執行node app.js命令,啓動服務器,能夠訪問http://127.0.0.1/remote-assets/project.manifest,若是成功訪問則服務器啓動成功。

  • 製做舊版本

刪除helloworld場景,腳本,texture文件夾,以及以前生成的manifest文件。保存以後構建項目,構建選項不要變更

 
image.png

在helloworld根目錄打開命令窗口,執行命令,node version_generator.js -v 1.0.0 -u http://127.0.0.1/remote-assets/ -s build/jsb-default/ -d assets/ 注意這裏的-v 1.0.0,以前是1.7.0,舊版本版本號要小於新版本

 

在assets生成兩個manifest文件。把project.manifest拖到屬性檢查器上面。


 
image.png

從新構建原平生臺,在main.js加下面這段代碼,而後編譯

if (cc.sys.isNative) { var hotUpdateSearchPaths = cc.sys.localStorage.getItem('HotUpdateSearchPaths'); if (hotUpdateSearchPaths) { jsb.fileUtils.setSearchPaths(JSON.parse(hotUpdateSearchPaths)); } } 
 
image.png

如今已經完成了舊版本的製做。這是我在安卓手機上的效果。


 
image.png

 
image.png
做者:歐特雨 連接:https://www.jianshu.com/p/cec263b6b9ac 來源:簡書 簡書著做權歸做者全部,任何形式的轉載都請聯繫做者得到受權並註明出處。
相關文章
相關標籤/搜索