CocosCreator 熱更新從零開始

官方文檔:https://docs.cocos.com/creator/manual/zh/advanced-topics/hot-update.html

官方熱更新Github 倉庫獲取地址

官方的示例及文檔腻暮,這里就不累贅說明了苫拍。這里直接從零開始制作實現(xiàn)一個簡單的熱更新示例惭蟋。這里分享下本人在這過程中遇到的問題。費話不多說,直接進入正題邻邮。

開發(fā)環(huán)境:

nodejs(nodejs的安裝請參考http://www.reibang.com/p/d9dac85846b3)

CocosCreator 1.10.2?

創(chuàng)建熱更新工程

1,創(chuàng)建HotUpate項目

直接默認(rèn)就好,這個場景呆會我們會直接用來做為1.2.0版本的升級使用


2征绎,創(chuàng)建新更新場景hotupdate

基本內(nèi)容為三個label 三個按鈕 兩個進度條,如下圖

filelabel fileprogressBar 文件總數(shù)下載進度

bytelabel byteLabel 文件下載進度

info 顯示信息

check 檢查更新

gotoscene 場景跳轉(zhuǎn)

update 更新


3,腳本編寫

創(chuàng)建HotUpdate.ts腳本并掛載到hotupdate場景的Canvas上


腳本如下:

const {ccclass, property} = cc._decorator;

@ccclass

export default class HotUpdate extends cc.Component {

? ? @property(cc.Label)

? ? fileLabel : cc.Label = null;

? ? @property(cc.Label)

? ? byteLabel : cc.Label = null;

? ? @property(cc.Label)

? ? info: cc.Label = null;

? ? @property(cc.Node)

? ? fileProgressNode : cc.Node = null;

? ? @property(cc.Node)

? ? byteProgressNode : cc.Node = null;

? ? @property(cc.RawAsset)

? ? mainifestUrl: cc.RawAsset= null;

? ? private fileProgressBar : cc.ProgressBar = null;

? ? private byteProgressBar : cc.ProgressBar = null;

? ? private _storagePath = "";

? ? private _am = null;

? ? private _updating = false;

? ? checkUpdate( ){

? ? ? ? cc.log(`--checkUpdate--`);

? ? ? ? if ( this._updating ){

? ? ? ? ? ? this.info.string = "Checking or updating...";

? ? ? ? ? ? return;

? ? ? ? }

? ? ? ? if ( this._am.getState() == jsb.AssetsManager.State.UNINITED){

? ? ? ? ? ? let url = this.mainifestUrl;

? ? ? ? ? ? cc.log(`mainifestUrl : ${this.mainifestUrl}`);

? ? ? ? ? ? this._am.loadLocalManifest(url);

? ? ? ? }

? ? ? ? if ( !this._am.getLocalManifest() || !this._am.getLocalManifest().isLoaded()){

? ? ? ? ? ? this.info.string = "Failed to load local manifest ....";

? ? ? ? ? ? return;

? ? ? ? }

? ? ? ? this._updating = true;

? ? ? ? this._am.setEventCallback(this.checkCb.bind(this));

? ? ? ? this._am.checkUpdate();


? ? }

? ? checkCb( event ){

? ? ? ? let needRestart = false;

? ? ? ? let failed = false;

? ? ? ? cc.log(`checkCb event code : ${event.getEventCode()}`);

? ? ? ? switch (event.getEventCode())

? ? ? ? {

? ? ? ? ? ? case jsb.EventAssetsManager.ERROR_NO_LOCAL_MANIFEST:

? ? ? ? ? ? ? ? this.info.string = "No local manifest file found, hot update skipped.";

? ? ? ? ? ? ? ? break;

? ? ? ? ? ? case jsb.EventAssetsManager.ERROR_DOWNLOAD_MANIFEST:

? ? ? ? ? ? case jsb.EventAssetsManager.ERROR_PARSE_MANIFEST:

? ? ? ? ? ? ? ? this.info.string = "Fail to download manifest file, hot update skipped.";

? ? ? ? ? ? ? ? break;

? ? ? ? ? ? case jsb.EventAssetsManager.ALREADY_UP_TO_DATE:

? ? ? ? ? ? ? ? this.info.string = "Already up to date with the latest remote version.";

? ? ? ? ? ? ? ? break;

? ? ? ? ? ? case jsb.EventAssetsManager.NEW_VERSION_FOUND:

? ? ? ? ? ? ? ? this.info.string = 'New version found, please try to update.';

? ? ? ? ? ? ? ? //this.checkBtn.active = false;

? ? ? ? ? ? ? ? this.fileProgressBar.progress = 0;

? ? ? ? ? ? ? ? this.byteProgressBar.progress = 0;

? ? ? ? ? ? ? ? break;

? ? ? ? ? ? default:

? ? ? ? ? ? ? ? return;

? ? ? ? }


? ? ? ? this._am.setEventCallback(null);

? ? ? ? this._updating = false;

? ? }

? ? hotUpdate( ){

? ? ? ? if ( this._am && !this._updating ){

? ? ? ? ? ? this._am.setEventCallback(this.updateCb.bind(this));

? ? ? ? ? ? if ( this._am.getState() === jsb.AssetsManager.State.UNINITED){

? ? ? ? ? ? ? ? this._am.loadLocalManifest(this.mainifestUrl);

? ? ? ? ? ? }

? ? ? ? ? ? //this._updating = true;

? ? ? ? ? ? this._am.update();

? ? ? ? }

? ? }

? ? private updateCb( event ){

? ? ? ? var needRestart = false;

? ? ? ? var failed = false;

? ? ? ? cc.log( `--update cb code : ${event.getEventCode()}`)

? ? ? ? switch (event.getEventCode())

? ? ? ? {

? ? ? ? ? ? case jsb.EventAssetsManager.ERROR_NO_LOCAL_MANIFEST:

? ? ? ? ? ? ? ? this.info.string = 'No local manifest file found, hot update skipped.';

? ? ? ? ? ? ? ? failed = true;

? ? ? ? ? ? ? ? break;

? ? ? ? ? ? case jsb.EventAssetsManager.UPDATE_PROGRESSION:

? ? ? ? ? ? ? ? this.byteProgressBar.progress = event.getPercent();

? ? ? ? ? ? ? ? this.fileProgressBar.progress = event.getPercentByFile();

? ? ? ? ? ? ? ? this.fileLabel.string = event.getDownloadedFiles() + ' / ' + event.getTotalFiles();

? ? ? ? ? ? ? ? this.byteLabel.string = event.getDownloadedBytes() + ' / ' + event.getTotalBytes();

? ? ? ? ? ? ? ? var msg = event.getMessage();

? ? ? ? ? ? ? ? if (msg) {

? ? ? ? ? ? ? ? ? ? this.info.string = 'Updated file: ' + msg;

? ? ? ? ? ? ? ? }

? ? ? ? ? ? ? ? break;

? ? ? ? ? ? case jsb.EventAssetsManager.ERROR_DOWNLOAD_MANIFEST:

? ? ? ? ? ? case jsb.EventAssetsManager.ERROR_PARSE_MANIFEST:

? ? ? ? ? ? ? ? this.info.string = 'Fail to download manifest file, hot update skipped.';

? ? ? ? ? ? ? ? failed = true;

? ? ? ? ? ? ? ? break;

? ? ? ? ? ? case jsb.EventAssetsManager.ALREADY_UP_TO_DATE:

? ? ? ? ? ? ? ? this.info.string = 'Already up to date with the latest remote version.';

? ? ? ? ? ? ? ? failed = true;

? ? ? ? ? ? ? ? break;

? ? ? ? ? ? case jsb.EventAssetsManager.UPDATE_FINISHED:

? ? ? ? ? ? ? ? this.info.string = 'Update finished. ' + event.getMessage();

? ? ? ? ? ? ? ? needRestart = true;

? ? ? ? ? ? ? ? break;

? ? ? ? ? ? case jsb.EventAssetsManager.UPDATE_FAILED:

? ? ? ? ? ? ? ? this.info.string = 'Update failed. ' + event.getMessage();

? ? ? ? ? ? ? ? this._updating = false;

? ? ? ? ? ? ? ? break;

? ? ? ? ? ? case jsb.EventAssetsManager.ERROR_UPDATING:

? ? ? ? ? ? ? ? this.info.string = 'Asset update error: ' + event.getAssetId() + ', ' + event.getMessage();

? ? ? ? ? ? ? ? break;

? ? ? ? ? ? case jsb.EventAssetsManager.ERROR_DECOMPRESS:

? ? ? ? ? ? ? ? this.info.string = event.getMessage();

? ? ? ? ? ? ? ? break;

? ? ? ? ? ? default:

? ? ? ? ? ? ? ? break;

? ? ? ? }

? ? ? ? if (failed) {

? ? ? ? ? ? this._am.setEventCallback(null);

? ? ? ? ? ? this._updating = false;

? ? ? ? }

? ? ? ? if (needRestart) {

? ? ? ? ? ? this._am.setEventCallback(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.apply(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.game.restart();

? ? ? ? }

? ? ? ? cc.log(`update cb? failed : ${failed}? , need restart : ${needRestart} , updating : ${this._updating}`);

? ? }

? ? changeScene() {

? ? ? ? cc.director.loadScene("helloworld");

? ? }

? ? // LIFE-CYCLE CALLBACKS:

? ? onLoad () {

? ? ? ? this.fileProgressBar = this.fileProgressNode.getComponent(cc.ProgressBar);

? ? ? ? this.byteProgressBar = this.byteProgressNode.getComponent(cc.ProgressBar);

? ? ? ? this.fileProgressBar.progress = 0;

? ? ? ? this.byteProgressBar.progress = 0;

? ? ? ? if ( cc.sys.isNative ){

? ? ? ? ? ? this._storagePath = jsb.fileUtils.getWritablePath();

? ? ? ? ? ? cc.log(`Storage path for remote asset : ${this._storagePath}`);

? ? ? ? ? ? this._am = new jsb.AssetsManager('', this._storagePath, this.versionCompareHanle.bind(this));

? ? ? ? ? ? let self = this;

? ? ? ? ? ? this._am.setVerifyCallback( function(path , asset) {

? ? ? ? ? ? ? ? let compressed = asset.compressed;

? ? ? ? ? ? ? ? let expectedMD5 = asset.md5;

? ? ? ? ? ? ? ? let relativePath = asset.path;

? ? ? ? ? ? ? ? let size = asset.size;

? ? ? ? ? ? ? ? if( compressed ){

? ? ? ? ? ? ? ? ? ? self.info.string = "Verification passed : " + relativePath;

? ? ? ? ? ? ? ? ? ? return true;

? ? ? ? ? ? ? ? }

? ? ? ? ? ? ? ? else{

? ? ? ? ? ? ? ? ? ? self.info.string = "Verification passed : " + relativePath + "(" + expectedMD5 + ")";

? ? ? ? ? ? ? ? ? ? return true;

? ? ? ? ? ? ? ? }

? ? ? ? ? ? });


? ? ? ? ? ? this.info.string = "Hot update is ready , please check or directly update.";

? ? ? ? }

? ? }

? ? private versionCompareHanle( versionA : string , versionB : string ){

? ? ? ? cc.log(`JS Custom Version Compare : version A is ${versionA} , version B is ${versionB}`);

? ? ? ? let vA = versionA.split('.');

? ? ? ? let vB = versionB.split('.');

? ? ? ? cc.log(`version A ${vA} , version B ${vB}`);

? ? ? ? for( let i = 0 ; i < vA.length && i < vB.length ; ++i ){

? ? ? ? ? ? let a = parseInt(vA[i]);

? ? ? ? ? ? let b = parseInt(vB[i]);

? ? ? ? ? ? if ( a === b ){

? ? ? ? ? ? ? ? continue;

? ? ? ? ? ? }

? ? ? ? ? ? else{

? ? ? ? ? ? ? ? return a - b;

? ? ? ? ? ? }

? ? ? ? }

? ? ? ? if ( vB.length > vA.length){

? ? ? ? ? ? return -1;

? ? ? ? }

? ? ? ? return 0;

? ? }

? ? start () {

? ? }

? ? onDestroy(){

? ? ? ? this._am.setEventCallback(null);

? ? }

? ? // update (dt) {}

}

4,構(gòu)建編譯版本

設(shè)置初始化場景為hotupdate場景


編譯磨取,構(gòu)建版本完成后人柿,我們會在build目錄下生成一個jsb-link的目錄如下


5,生成新舊版本

這里我們先生成一個新的版本資源忙厌,再生成舊版本的資源凫岖。到官方的示例中把version_generator.js復(fù)制到我們的項目目錄下


cocos 官方熱更新示例


HotUpdate項目version_generator.js存放位置

生成新版本


執(zhí)行命令:?node version_generator.js -v 1.2.0 -u http://localhost/remote-assets/ -s build/jsb-link/ -d assets/


生成成功,此時項目assets目錄下會生成兩個版本文件


打開工程逢净,配置mainifestUrl ,如圖


6,本地版本服務(wù)器測試環(huán)境搭建

新建nodejs目錄


編寫腳本

var express = require('express')

var path = require('path')

var app = express();

app.use(express.static(path.join(__dirname,'hotUpdate')));

app.listen(80);

另存為app.js

在nodejs下新建hotUpdate 作為熱更新資源的目錄?


在hotUpdate目錄下新建remote-assets遠程版本資源目錄

將生成的版本信息文件哥放、代碼、資源復(fù)制到該目錄下


新版本配置文件


編譯生成的資源文件及代碼


nodejs 資源服務(wù)器最終配置

運行資源服務(wù)器

控制臺執(zhí)行 node app.js?


瀏覽器運行查看資源服務(wù)器是否正常啟動爹土,在瀏覽器中輸入http://localhost/remote-assets/project.manifest ,如果看到如下內(nèi)容甥雕,證明資源服務(wù)器正常運行

7,舊版本的制作?

這里為了方便,直接刪除提helloworld場景作為舊版本


已刪除的helloworld場景

重新構(gòu)建編譯胀茵,這里就不重寫說明構(gòu)建編譯的過程了

構(gòu)建編譯完成后社露,生成舊版本,控制臺執(zhí)行

node version_generator.js -v 1.1.0 -u http://localhost/remote-assets/ -s build/jsb-link/ -d assets/


注意是生成生成舊版本

8,修改main.js文件

?找到build/jsb-link/main.js


在對應(yīng)位置添加如下代碼琼娘,


代碼:

if (window.jsb) {

? ? ? ? var hotUpdateSearchPaths = cc.sys.localStorage.getItem('HotUpdateSearchPaths');

? ? ? ? if (hotUpdateSearchPaths) {

? ? ? ? ? ? jsb.fileUtils.setSearchPaths(JSON.parse(hotUpdateSearchPaths));

? ? ? ? }

? ? ? ? require('src/settings.js');

? ? ? ? require('src/jsb_polyfill.js');

? ? ? ? boot();

? ? ? ? return;

? ? }


9峭弟,運行測試熱更新


打開jsb-link/frameworks/runtime-src/proj.win32工程

如果沒有自己導(dǎo)出c++類到j(luò)s中使用赁濒,這個步驟可以不做。

運行


以下是相關(guān)運行截圖

檢查更新(check)


更新(update)


下載的更新window上會自動下載到該目錄下

C:\Users\Administrator\AppData\Local\你的項目名

跳轉(zhuǎn)(goto):


參考鏈接:http://www.reibang.com/p/cec263b6b9ac

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末孟害,一起剝皮案震驚了整個濱河市拒炎,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌挨务,老刑警劉巖击你,帶你破解...
    沈念sama閱讀 217,277評論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異谎柄,居然都是意外死亡丁侄,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,689評論 3 393
  • 文/潘曉璐 我一進店門朝巫,熙熙樓的掌柜王于貴愁眉苦臉地迎上來鸿摇,“玉大人,你說我怎么就攤上這事劈猿∽炯” “怎么了?”我有些...
    開封第一講書人閱讀 163,624評論 0 353
  • 文/不壞的土叔 我叫張陵揪荣,是天一觀的道長筷黔。 經(jīng)常有香客問我,道長仗颈,這世上最難降的妖魔是什么佛舱? 我笑而不...
    開封第一講書人閱讀 58,356評論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮挨决,結(jié)果婚禮上请祖,老公的妹妹穿的比我還像新娘。我一直安慰自己脖祈,他們只是感情好肆捕,可當(dāng)我...
    茶點故事閱讀 67,402評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著撒犀,像睡著了一般福压。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上或舞,一...
    開封第一講書人閱讀 51,292評論 1 301
  • 那天,我揣著相機與錄音蒙幻,去河邊找鬼映凳。 笑死,一個胖子當(dāng)著我的面吹牛邮破,可吹牛的內(nèi)容都是我干的诈豌。 我是一名探鬼主播仆救,決...
    沈念sama閱讀 40,135評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼矫渔!你這毒婦竟也來了彤蔽?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,992評論 0 275
  • 序言:老撾萬榮一對情侶失蹤庙洼,失蹤者是張志新(化名)和其女友劉穎顿痪,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體油够,經(jīng)...
    沈念sama閱讀 45,429評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡蚁袭,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,636評論 3 334
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了石咬。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片揩悄。...
    茶點故事閱讀 39,785評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖鬼悠,靈堂內(nèi)的尸體忽然破棺而出删性,到底是詐尸還是另有隱情,我是刑警寧澤焕窝,帶...
    沈念sama閱讀 35,492評論 5 345
  • 正文 年R本政府宣布镇匀,位于F島的核電站,受9級特大地震影響袜啃,放射性物質(zhì)發(fā)生泄漏汗侵。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,092評論 3 328
  • 文/蒙蒙 一群发、第九天 我趴在偏房一處隱蔽的房頂上張望晰韵。 院中可真熱鬧,春花似錦熟妓、人聲如沸雪猪。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,723評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽只恨。三九已至,卻和暖如春抬虽,著一層夾襖步出監(jiān)牢的瞬間官觅,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,858評論 1 269
  • 我被黑心中介騙來泰國打工阐污, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留休涤,地道東北人。 一個月前我還...
    沈念sama閱讀 47,891評論 2 370
  • 正文 我出身青樓,卻偏偏與公主長得像功氨,于是被迫代替她去往敵國和親序苏。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,713評論 2 354

推薦閱讀更多精彩內(nèi)容