CocosCreator 热更新从零开始

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

官方热更新Github 仓库获取地址

官方的示例及文档,这里就不累赘说明了。这里直接从零开始制作实现一个简单的热更新示例。这里分享下本人在这过程中遇到的问题。费话不多说,直接进入正题。

开发环境:

nodejs(nodejs的安装请参考https://www.jianshu.com/p/d9dac85846b3)

CocosCreator 1.10.2 

创建热更新工程

1,创建HotUpate项目

直接默认就好,这个场景呆会我们会直接用来做为1.2.0版本的升级使用


2,创建新更新场景hotupdate

基本内容为三个label 三个按钮 两个进度条,如下图

filelabel fileprogressBar 文件总数下载进度

bytelabel byteLabel 文件下载进度

info 显示信息

check 检查更新

gotoscene 场景跳转

update 更新


3,脚本编写

创建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,构建编译版本

设置初始化场景为hotupdate场景


编译,构建版本完成后,我们会在build目录下生成一个jsb-link的目录如下


5,生成新旧版本

这里我们先生成一个新的版本资源,再生成旧版本的资源。到官方的示例中把version_generator.js复制到我们的项目目录下


cocos 官方热更新示例


HotUpdate项目version_generator.js存放位置

生成新版本


执行命令: node version_generator.js -v 1.2.0 -u http://localhost/remote-assets/ -s build/jsb-link/ -d assets/


生成成功,此时项目assets目录下会生成两个版本文件


打开工程,配置mainifestUrl ,如图


6,本地版本服务器测试环境搭建

新建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远程版本资源目录

将生成的版本信息文件、代码、资源复制到该目录下


新版本配置文件


编译生成的资源文件及代码


nodejs 资源服务器最终配置

运行资源服务器

控制台执行 node app.js 


浏览器运行查看资源服务器是否正常启动,在浏览器中输入http://localhost/remote-assets/project.manifest ,如果看到如下内容,证明资源服务器正常运行

7,旧版本的制作 

这里为了方便,直接删除提helloworld场景作为旧版本


已删除的helloworld场景

重新构建编译,这里就不重写说明构建编译的过程了

构建编译完成后,生成旧版本,控制台执行

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


在对应位置添加如下代码,


代码:

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工程

如果没有自己导出c++类到js中使用,这个步骤可以不做。

运行


以下是相关运行截图

检查更新(check)


更新(update)


下载的更新window上会自动下载到该目录下

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

跳转(goto):


参考链接:https://www.jianshu.com/p/cec263b6b9ac

你可能感兴趣的:(CocosCreator 热更新从零开始)