cocoscreator2.0.10 热更新&&大厅子游戏模式 学习使用记录

 

热更新主要用到cocos2d的热更新模块RawAsset    AssetsManager

先按教程写个demo

创建大厅空项目 ,添加场景helloworld ,如下图在场景中加热更需要的控件 check和update按钮 label显示状态,图片guanggao_1 区别新旧版本。

创建脚本文件 HotUpdata.js 关联到场景hotUpdata内容:


/**
 * 负责热更新逻辑的组件
 */
cc.Class({
    extends: cc.Component,

    properties: {
        //manifestUrl: cc.RawAsset,  //本地project.manifest资源清单文件
        manifestUrl:{
            type:cc.Asset,
            default:null
        },
        _updating: false,
        _canRetry: false,
        _storagePath: ''
    },

    checkCb: function (event) {
        cc.log('Code: ' + event.getEventCode());
        switch (event.getEventCode()) {
            case jsb.EventAssetsManager.ERROR_NO_LOCAL_MANIFEST:
                cc.log("No local manifest file found, hot update skipped.");
                break;
            case jsb.EventAssetsManager.ERROR_DOWNLOAD_MANIFEST:
            case jsb.EventAssetsManager.ERROR_PARSE_MANIFEST:
                cc.log("Fail to download manifest file, hot update skipped.");
                break;
            case jsb.EventAssetsManager.ALREADY_UP_TO_DATE:
                cc.log("Already up to date with the latest remote version.");
                break;
            case jsb.EventAssetsManager.NEW_VERSION_FOUND:
                cc.log('New version found, please try to update.');
                this.hotUpdate();
                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:
                cc.log('No local manifest file found, hot update skipped...');
                failed = true;
                break;
            case jsb.EventAssetsManager.UPDATE_PROGRESSION:
                cc.log(event.getPercent());
                cc.log(event.getPercentByFile());
                cc.log(event.getDownloadedFiles() + ' / ' + event.getTotalFiles());
                cc.log(event.getDownloadedBytes() + ' / ' + event.getTotalBytes());

                var msg = event.getMessage();
                if (msg) {
                    cc.log('Updated file: ' + msg);
                }
                break;
            case jsb.EventAssetsManager.ERROR_DOWNLOAD_MANIFEST:
            case jsb.EventAssetsManager.ERROR_PARSE_MANIFEST:
                cc.log('Fail to download manifest file, hot update skipped.');
                failed = true;
                break;
            case jsb.EventAssetsManager.ALREADY_UP_TO_DATE:
                cc.log('Already up to date with the latest remote version.');
                failed = true;
                break;
            case jsb.EventAssetsManager.UPDATE_FINISHED:
                cc.log('Update finished. ' + event.getMessage());
                needRestart = true;
                break;
            case jsb.EventAssetsManager.UPDATE_FAILED:
                cc.log('Update failed. ' + event.getMessage());
                this._updating = false;
                this._canRetry = true;
                break;
            case jsb.EventAssetsManager.ERROR_UPDATING:
                cc.log('Asset update error: ' + event.getAssetId() + ', ' + event.getMessage());
                break;
            case jsb.EventAssetsManager.ERROR_DECOMPRESS:
                cc.log(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;
            // 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(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.audioEngine.stopAll();
            cc.game.restart();
        }
    },


    retry: function () {
        if (!this._updating && this._canRetry) {
            this._canRetry = false;

            cc.log('Retry failed Assets...');
            this._am.downloadFailedAssets();
        }
    },

    checkForUpdate: function () {
        cc.log("start checking...");
        if (this._updating) {
            cc.log('Checking or updating ...');
            return;
        }
        if (this._am.getState() === jsb.AssetsManager.State.UNINITED) {
            this._am.loadLocalManifest(this.manifestUrl.nativeUrl);
            cc.log(this.manifestUrl.nativeUrl);
        }
        if (!this._am.getLocalManifest() || !this._am.getLocalManifest().isLoaded()) {
            cc.log('Failed to load local manifest ...');
            return;
        }
        /* descrepate
        this._checkListener = new jsb.EventListenerAssetsManager(this._am, this.checkCb.bind(this));
        cc.eventManager.addListener(this._checkListener, 1);*/
        this._am.setEventCallback(this.checkCb.bind(this));
        
        this._am.checkUpdate();

        this._updating = true;
    },

    hotUpdate: function () {
        if (this._am) {
            /*descrepate
            this._updateListener = new jsb.EventListenerAssetsManager(this._am, this.updateCb.bind(this));
            cc.eventManager.addListener(this._updateListener, 1);*/
            this._am.setEventCallback(this.updateCb.bind(this));

            if (this._am.getState() === jsb.AssetsManager.State.UNINITED) {
                this._am.loadLocalManifest(this.manifestUrl.nativeUrl);
            }

            this._failCount = 0;
            this._am.update();
            this._updating = true;
        }
    },

    show: function () {
        // if (this.updateUI.active === false) {
        //     this.updateUI.active = true;
        // }
    },

    // use this for initialization
    onLoad: function () {
        // Hot update is only available in Native build
        if (!cc.sys.isNative) {
            return;
        }
        this._storagePath = ((jsb.fileUtils ? jsb.fileUtils.getWritablePath() : '/') + 'xiaoming-remote-asset');
        cc.log('Storage path for remote asset : ' + this._storagePath);

        // Setup your own version compare handler, versionA and B is versions in string
        // if the return value greater than 0, versionA is greater than B,
        // if the return value equals 0, versionA equals to B,
        // if the return value smaller than 0, versionA is smaller than B.
        this.versionCompareHandle = function (versionA, versionB) {
            cc.log("JS Custom Version Compare: version A is " + versionA + ', version B is ' + versionB);
            var vA = versionA.split('.');
            var vB = versionB.split('.');
            for (var i = 0; i < vA.length; ++i) {
                var a = parseInt(vA[i]);
                var b = parseInt(vB[i] || 0);
                if (a === b) {
                    continue;
                }
                else {
                    return a - b;
                }
            }
            if (vB.length > vA.length) {
                return -1;
            }
            else {
                return 0;
            }
        };
      
        console.log(">>>[this._storagePath:]"+this._storagePath)
       
        // Init with empty manifest url for testing custom manifest
        
        this._am = new jsb.AssetsManager('', this._storagePath, this.versionCompareHandle);
        console.log(cc.sys.ENABLE_GC_FOR_NATIVE_OBJECTS+">>>[_am:]",this._am)
        if (!cc.sys.ENABLE_GC_FOR_NATIVE_OBJECTS) {
            //this._am.retain();
        }
        // Setup the verification callback, but we don't have md5 check function yet, so only print some message
        // Return true if the verification passed, otherwise return false
        this._am.setVerifyCallback(function (path, asset) {
            // When asset is compressed, we don't need to check its md5, because zip file have been deleted.
            var compressed = asset.compressed;
            // Retrieve the correct md5 value.
            var expectedMD5 = asset.md5;
            // asset.path is relative path and path is absolute.
            var relativePath = asset.path;
            // The size of asset file, but this value could be absent.
            var size = asset.size;
            if (compressed) {
                cc.log("Verification passed : " + relativePath);
                return true;
            }
            else {
                cc.log("Verification passed : " + relativePath + ' (' + expectedMD5 + ')');
                return true;
            }
        });
        cc.log("Hot update is ready, please check or directly update.");

        if (cc.sys.os === cc.sys.OS_ANDROID) {
            // Some Android device may slow down the download process when concurrent tasks is too much.
            // The value may not be accurate, please do more test and find what's most suitable for your game.
            this._am.setMaxConcurrentTask(2);
            cc.log("Max concurrent tasks count have been limited to 2");
        }
        // this.checkUpdate();
    },

    onDestroy: function () {
        if (this._updateListener) {
            cc.eventManager.removeListener(this._updateListener);
            this._updateListener = null;
        }
        if (this._am && !cc.sys.ENABLE_GC_FOR_NATIVE_OBJECTS) {
           // this._am.release();
        }
    }
});

在项目根目录下添加 version_generator.js文件  用node 执行文件来生成project.manifest version.manifest两个文件 (需要安装node)

node version_generator.js

version_generator 内容如下:

/**
 * 此模块用于热更新工程清单文件的生成
 */

var fs = require('fs');
var path = require('path');
var crypto = require('crypto');

var manifest = {
    //服务器上资源文件存放路径(src,res的路径)
    packageUrl: 'http://192.192.19.199/HotUpdate/',
    //服务器上project.manifest路径
    remoteManifestUrl: 'http://192.192.19.199/HotUpdate/project.manifest',
    //服务器上version.manifest路径
    remoteVersionUrl: 'http://192.192.19.199/HotUpdate/version.manifest',
    version: '1.0.2',
    assets: {},
    searchPaths: []
};

//生成的manifest文件存放目录
var dest = 'assets/';
//项目构建后资源的目录
var src = 'build/jsb-default/';

/**
 * node version_generator.js -v 1.0.0 -u http://your-server-address/tutorial-hot-update/remote-assets/ -s native/package/ -d assets/
 */
// Parse arguments
var i = 2;
while ( i < process.argv.length) {
    var arg = process.argv[i];

    switch (arg) {
    case '--url' :
    case '-u' :
        var url = process.argv[i+1];
        manifest.packageUrl = url;
        manifest.remoteManifestUrl = url + 'project.manifest';
        manifest.remoteVersionUrl = url + 'version.manifest';
        i += 2;
        break;
    case '--version' :
    case '-v' :
        manifest.version = process.argv[i+1];
        i += 2;
        break;
    case '--src' :
    case '-s' :
        src = process.argv[i+1];
        i += 2;
        break;
    case '--dest' :
    case '-d' :
        dest = process.argv[i+1];
        i += 2;
        break;
    default :
        i++;
        break;
    }
}


function readDir (dir, obj) {
    var stat = fs.statSync(dir);
    if (!stat.isDirectory()) {
        return;
    }
    var subpaths = fs.readdirSync(dir), subpath, size, md5, compressed, relative;
    for (var i = 0; i < subpaths.length; ++i) {
        if (subpaths[i][0] === '.') {
            continue;
        }
        subpath = path.join(dir, subpaths[i]);
        stat = fs.statSync(subpath);
        if (stat.isDirectory()) {
            readDir(subpath, obj);
        }
        else if (stat.isFile()) {
            // Size in Bytes
            size = stat['size'];
            md5 = crypto.createHash('md5').update(fs.readFileSync(subpath, 'binary')).digest('hex');
            compressed = path.extname(subpath).toLowerCase() === '.zip';

            relative = path.relative(src, subpath);
            relative = relative.replace(/\\/g, '/');
            relative = encodeURI(relative);
            obj[relative] = {
                'size' : size,
                'md5' : md5
            };
            if (compressed) {
                obj[relative].compressed = true;
            }
        }
    }
}

var mkdirSync = function (path) {
    try {
        fs.mkdirSync(path);
    } catch(e) {
        if ( e.code != 'EEXIST' ) throw e;
    }
}

// Iterate res and src folder
readDir(path.join(src, 'src'), manifest.assets);
readDir(path.join(src, 'res'), manifest.assets);

var destManifest = path.join(dest, 'project.manifest');
var destVersion = path.join(dest, 'version.manifest');

mkdirSync(dest);

fs.writeFile(destManifest, JSON.stringify(manifest), (err) => {
  if (err) throw err;
  console.log('Manifest successfully generated');
});

delete manifest.assets;
delete manifest.searchPaths;
fs.writeFile(destVersion, JSON.stringify(manifest), (err) => {
  if (err) throw err;
  console.log('Version successfully generated');
});

回到编辑界面 如下图 把刚刚用version_generator新生成到asset的project.mainfest 文件拖进去

如下图 文件绑定控件使用新写法 

version_generator 文件中 url路径需要修改成你使用的资源服务器路径 ,dest 和src路径也要对应,把该文件中的version 版本号改成1.0.0,打一个旧包,安装在手机上,

(我看的教程里需要修改main.js文件附加上搜索路径的设置,我试了下2.0版本不添加也可以正常热更 )

随意改个场景图片,然后修改version_generator文件的版本号为1.0.1,生成新的mainfest文件 再次打包新的apk,

构建编译成功后 把\build\jsb-default 文件下的 res src文件  和assets 文件夹下的 project.manifest version.manifest添加到资源服务器 如下图:

运行1.0.0旧版本即可更新。

然后是大厅子游戏模式,创建子游戏demo 和大厅一样添加version_generator.js 修改 main.js文件

(function () {
    'use strict';

    if (window.jsb) {
        /// 1.初始化资源Lib路径Root.
        var subgameSearchPath = (jsb.fileUtils ? jsb.fileUtils.getWritablePath() : '/')+'ALLGame/kaixinxiaoxiaole/';

        /// 2.subgame资源未映射,则初始化资源映射表,否则略过映射.
        if(!cc.HallAndSubGameGlobal){
            cc.HallAndSubGameGlobal = {}
        }
        if(!cc.HallAndSubGameGlobal.subgameGlobal){
            cc.HallAndSubGameGlobal.subgameGlobal = {};

            /// 加载settings.js
            window.require(subgameSearchPath + 'src/settings.js');
            var settings = window._CCSettings;
            window._CCSettings = undefined;

            if ( !settings.debug ) {
                var uuids = settings.uuids;

                var rawAssets = settings.rawAssets;
                var assetTypes = settings.assetTypes;
                var realRawAssets = settings.rawAssets = {};
                for (var mount in rawAssets) {
                    var entries = rawAssets[mount];
                    var realEntries = realRawAssets[mount] = {};
                    for (var id in entries) {
                        var entry = entries[id];
                        var type = entry[1];
                        // retrieve minified raw asset
                        if (typeof type === 'number') {
                            entry[1] = assetTypes[type];
                        }
                        // retrieve uuid
                        realEntries[uuids[id] || id] = entry;
                    }
                }

                var scenes = settings.scenes;
                for (var i = 0; i < scenes.length; ++i) {
                    var scene = scenes[i];

                    if (typeof scene.uuid === 'number') {
                        scene.uuid = uuids[scene.uuid];
                    }
                }

                var packedAssets = settings.packedAssets;
                for (var packId in packedAssets) {
                    var packedIds = packedAssets[packId];
                    for (var j = 0; j < packedIds.length; ++j) {
                        if (typeof packedIds[j] === 'number') {
                            packedIds[j] = uuids[packedIds[j]];
                        }
                    }
                }
            }

            /// 加载project.js
            var projectDir = 'src/project.js';
            if ( settings.debug ) {
                projectDir = 'src/project.dev.js';
            }
            console.log('>>>导入project文件《《《')
            require(subgameSearchPath + projectDir);

            /// 如果当前搜索路径没有subgame,则添加进去搜索路径。
            var currentSearchPaths = jsb.fileUtils.getSearchPaths();
            if(currentSearchPaths && currentSearchPaths.indexOf(subgameSearchPath) === -1){
                jsb.fileUtils.addSearchPath(subgameSearchPath, true);
                console.log('subgame main.js 之前未添加,添加下subgameSearchPath' + currentSearchPaths);
            }

            cc.AssetLibrary.init({
                libraryPath: 'res/import',
                rawAssetsBase: 'res/raw-',
                rawAssets: settings.rawAssets,
                packedAssets: settings.packedAssets,
                md5AssetsMap: settings.md5AssetsMap
            });

            cc.HallAndSubGameGlobal.subgameGlobal.launchScene = settings.launchScene;

            /// 将subgame的场景添加到cc.game中,使得cc.director.loadScene可以从cc.game._sceneInfos查找到相关场景
            for(var i = 0; i < settings.scenes.length; ++i){
                cc.game._sceneInfos.push(settings.scenes[i]);
            }
        }

        /// 3.加载初始场景
        var launchScene = cc.HallAndSubGameGlobal.subgameGlobal.launchScene;
        cc.director.loadScene(launchScene, null,
            function () {
                console.log('subgame main.js 成功加载初始场景' + launchScene);
            }
        );
    }
})();

把 main.js 拷贝到打包好的src目录下,还是和大厅一样把 res src 和两个manifes文件拷贝到资源服务器,如下图

到这里子游戏就打包上传好了,需要在大厅项目里添加subgameManager,内容是:

const SubgameManager = {
    _storagePath: [],

    _getfiles: function(name, type, downloadCallback, finishCallback) {
        console.log(">>>_getfiles")
        this._storagePath[name] = ((jsb.fileUtils ? jsb.fileUtils.getWritablePath() : '/') + 'ALLGame/' + name);
        this._downloadCallback = downloadCallback;
        this._finishCallback = finishCallback;
        this._fileName = name;
       
        /// 替换该地址
        var UIRLFILE = "http://192.168.10.121/HotUpdate/" + name + "/remote-assets";
        var filees = this._storagePath[name] + '/project.manifest';

        var customManifestStr = JSON.stringify({
            'packageUrl': UIRLFILE,
            'remoteManifestUrl': UIRLFILE + '/project.manifest',
            'remoteVersionUrl': UIRLFILE + '/version.manifest',
            'version': '0.0.1',
            'assets': {},
            'searchPaths': []
        });

        var versionCompareHandle = function(versionA, versionB) {
            var vA = versionA.split('.');
            var vB = versionB.split('.');
            for (var i = 0; i < vA.length; ++i) {
                var a = parseInt(vA[i]);
                var b = parseInt(vB[i] || 0);
                if (a === b) {
                    continue;
                } else {
                    return a - b;
                }
            }
            if (vB.length > vA.length) {
                return -1;
            } else {
                return 0;
            }
        };
        
        this._am = new jsb.AssetsManager('', this._storagePath[name], versionCompareHandle);
        
        if (!cc.sys.ENABLE_GC_FOR_NATIVE_OBJECTS) {
            //this._am.retain();
        }

        this._am.setVerifyCallback(function(path, asset) {
            var compressed = asset.compressed;
            if (compressed) {
                return true;
            } else {
                return true;
            }
        });

        
        if (cc.sys.os === cc.sys.OS_ANDROID) {
            this._am.setMaxConcurrentTask(2);
        }

        if (type === 1) {
           // this._updateListener = new jsb.EventListenerAssetsManager(this._am, this._updateCb.bind(this));
            this._am.setEventCallback(this._updateCb.bind(this));
        } else if (type == 2) {
            //this._updateListener = new jsb.EventListenerAssetsManager(this._am, this._checkCb.bind(this));
            this._am.setEventCallback(this._checkCb.bind(this));
        } else {
            //this._updateListener = new jsb.EventListenerAssetsManager(this._am, this._needUpdate.bind(this));
            this._am.setEventCallback(this._needUpdate.bind(this));
        }
        
        //cc.eventManager.addListener(this._updateListener, 1);
        
        if (this._am.getState() === jsb.AssetsManager.State.UNINITED) {
            var manifest = new jsb.Manifest(customManifestStr, this._storagePath[name]);
            this._am.loadLocalManifest(manifest, this._storagePath[name]);
        }
        
        if (type === 1) {
            this._am.update();
            this._failCount = 0;
        } else {
            this._am.checkUpdate();
        }
        this._updating = true;
        cc.log('更新文件:' + filees);
    },

    // type = 1
    _updateCb: function(event) {
        var failed = false;
        let self = this;
        switch (event.getEventCode()) {
            case jsb.EventAssetsManager.ERROR_NO_LOCAL_MANIFEST:
                /*0 本地没有配置文件*/
                cc.log('updateCb本地没有配置文件');
                failed = true;
                break;

            case jsb.EventAssetsManager.ERROR_DOWNLOAD_MANIFEST:
                /*1下载配置文件错误*/
                cc.log('updateCb下载配置文件错误');
                failed = true;
                break;

            case jsb.EventAssetsManager.ERROR_PARSE_MANIFEST:
                /*2 解析文件错误*/
                cc.log('updateCb解析文件错误');
                failed = true;
                break;

            case jsb.EventAssetsManager.NEW_VERSION_FOUND:
                /*3发现新的更新*/
                cc.log('updateCb发现新的更新');
                break;

            case jsb.EventAssetsManager.ALREADY_UP_TO_DATE:
                /*4 已经是最新的*/
                cc.log('updateCb已经是最新的');
                failed = true;
                break;

            case jsb.EventAssetsManager.UPDATE_PROGRESSION:
                /*5 最新进展 */
                self._downloadCallback && self._downloadCallback(event.getPercentByFile());
                break;


            case jsb.EventAssetsManager.ASSET_UPDATED:
                /*6需要更新*/
                break;

            case jsb.EventAssetsManager.ERROR_UPDATING:
                /*7更新错误*/
                cc.log('updateCb更新错误');
                break;

            case jsb.EventAssetsManager.UPDATE_FINISHED:
                /*8更新完成*/
                self._finishCallback && self._finishCallback(true);
                break;

            case jsb.EventAssetsManager.UPDATE_FAILED:
                /*9更新失败*/
                self._failCount++;
                if (self._failCount <= 3) {
                    self._am.downloadFailedAssets();
                    cc.log(('updateCb更新失败' + this._failCount + ' 次'));
                } else {
                    cc.log(('updateCb失败次数过多'));
                    self._failCount = 0;
                    failed = true;
                    self._updating = false;
                }
                break;

            case jsb.EventAssetsManager.ERROR_DECOMPRESS:
                /*10解压失败*/
                cc.log('updateCb解压失败');
                break;
        }

        if (failed) {
            cc.systemEvent.registerSystemEvent
          //  cc.eventManager.removeListener(self._updateListener);
            self._updateListener = null;
            self._updating = false;
            self._finishCallback && self._finishCallback(false);
        }
    },

    // type = 2
    _checkCb: function(event) {
        var failed = false;
        let self = this;
        switch (event.getEventCode()) {
            case jsb.EventAssetsManager.ERROR_NO_LOCAL_MANIFEST:
                /*0 本地没有配置文件*/
                cc.log('checkCb本地没有配置文件');
                break;

            case jsb.EventAssetsManager.ERROR_DOWNLOAD_MANIFEST:
                /*1下载配置文件错误*/
                cc.log('checkCb下载配置文件错误');
                failed = true;
                break;

            case jsb.EventAssetsManager.ERROR_PARSE_MANIFEST:
                /*2 解析文件错误*/
                cc.log('checkCb解析文件错误');
                failed = true;
                break;

            case jsb.EventAssetsManager.NEW_VERSION_FOUND:
                /*3发现新的更新*/
                self._getfiles(self._fileName, 1, self._downloadCallback, self._finishCallback);
                break;

            case jsb.EventAssetsManager.ALREADY_UP_TO_DATE:
                /*4 已经是最新的*/
                cc.log('checkCb已经是最新的');
                self._finishCallback && self._finishCallback(true);
                break;

            case jsb.EventAssetsManager.UPDATE_PROGRESSION:
                /*5 最新进展 */
                break;

            case jsb.EventAssetsManager.ASSET_UPDATED:
                /*6需要更新*/
                break;

            case jsb.EventAssetsManager.ERROR_UPDATING:
                /*7更新错误*/
                cc.log('checkCb更新错误');
                failed = true;
                break;


            case jsb.EventAssetsManager.UPDATE_FINISHED:
                /*8更新完成*/
                cc.log('checkCb更新完成');
                break;

            case jsb.EventAssetsManager.UPDATE_FAILED:
                /*9更新失败*/
                cc.log('checkCb更新失败');
                failed = true;
                break;

            case jsb.EventAssetsManager.ERROR_DECOMPRESS:
                /*10解压失败*/
                cc.log('checkCb解压失败');
                break;

        }
        this._updating = false;
        if (failed) {
            self._finishCallback && self._finishCallback(false);
        }
    },

    // type = 3
    _needUpdate: function(event) {
        let self = this;
        switch (event.getEventCode()) {
            case jsb.EventAssetsManager.ALREADY_UP_TO_DATE:
                cc.log('子游戏已经是最新的,不需要更新');
                self._finishCallback && self._finishCallback(false);
                break;

            case jsb.EventAssetsManager.NEW_VERSION_FOUND:
                cc.log('子游戏需要更新');
                self._finishCallback && self._finishCallback(true);
                break;

            // 检查是否更新出错
            case jsb.EventAssetsManager.ERROR_DOWNLOAD_MANIFEST:
            case jsb.EventAssetsManager.ERROR_PARSE_MANIFEST:
            case jsb.EventAssetsManager.ERROR_UPDATING:
            case jsb.EventAssetsManager.UPDATE_FAILED:
                self._downloadCallback();
                break;
        }
    },

    /**
     * 下载子游戏
     * @param {string} name - 游戏名
     * @param progress - 下载进度回调
     * @param finish - 完成回调
     * @note finish 返回true表示下载成功,false表示下载失败
     */
    downloadSubgame: function(name, progress, finish) {
        console.log(">>>downloadSubgame")
        this._getfiles(name, 2, progress, finish);
    },

    /**
     * 进入子游戏
     * @param {string} name - 游戏名
     */
    enterSubgame: function(name) {
        if (!this._storagePath[name]) {
            this.downloadSubgame(name);
            return;
        }
        cc.director.emit('updateEnd',this._storagePath[name])
        window.require(this._storagePath[name] + '/src/main.js');
        
    },

    /**
     * 判断子游戏是否已经下载
     * @param {string} name - 游戏名
     */
    isSubgameDownLoad: function (name) {
        let file = (jsb.fileUtils ? jsb.fileUtils.getWritablePath() : '/') + 'ALLGame/' + name + '/project.manifest';
        if (jsb.fileUtils.isFileExist(file)) {
            return true;
        } else {
            return false;
        }
    },

    /**
     * 判断子游戏是否需要更新
     * @param {string} name - 游戏名
     * @param isUpdateCallback - 是否需要更新回调
     * @param failCallback - 错误回调
     * @note isUpdateCallback 返回true表示需要更新,false表示不需要更新
     */
    needUpdateSubgame: function (name, isUpdateCallback, failCallback) {
        console.log(">>>needUpdateSubgame")
        this._getfiles(name, 3, failCallback, isUpdateCallback);
    },
};

module.exports = SubgameManager;

 

然后修改helloworld文件如下:

const SubgameManager = require('SubgameManager');

cc.Class({
    extends: cc.Component,

    properties: {
        downloadBtn: {
            default: null,
            type: cc.Node
        },
        downloadLabel: {
            default: null,
            type: cc.Label
        }
    },
    updateEndCall(data){
       {
            this.downloadLabel.string = data
        }
    },
    onLoad: function () {
        cc.director.on('updateEnd',(e)=>{
            this.updateEndCall(e)
        },this);
        let self = this
        const name = 'kaixinxiaoxiaole'; 
        this.downloadBtn.on('click',function(){
            //下载子游戏/更新子游戏
            this.downloadLabel.string = "开始下载子游戏";
            SubgameManager.downloadSubgame(name, (progress) => {
                console.log('下载进度-0')
                if (isNaN(progress)) {
                    progress = 0;
                    this.downloadLabel.string = "下载进度-0";
                }
                this.downloadLabel.string = "资源下载中   " + parseInt(progress * 100) + "%";
            }, function(success) {
                if (success) {
                    console.log('can enterSubGame')
                    self.downloadLabel.string = "启动游戏 " 
                    SubgameManager.enterSubgame('kaixinxiaoxiaole');
                } else {
                   // this.downloadLabel.string = "下载失败";
                    cc.log('下载失败');
                }
            });
        }
        ,this)

        //判断子游戏有没有下载
        if (SubgameManager.isSubgameDownLoad(name)) {
            //已下载,判断是否需要更新
            SubgameManager.needUpdateSubgame(name, (success) => {
                if (success) {
                    this.downloadLabel.string = "子游戏需要更新";
                } else {
                    this.downloadLabel.string = "子游戏不需要更新";
                }
            }, () => {
                this.downloadLabel.string = "出错了";
                cc.log('出错了');
            });
        } else {
            this.downloadLabel.string = "子游戏未下载";
        }
        
       
    },
});

编译大厅并安装运行就可以测试了

一些小坑:

1 jsb.EventListenerAssetsManager方法已不可用 可以使用 setEventCallback方法

2 cc.eventManager.addListener 方法也一样弃用,官方文档上没有找到说明,直接注释掉就可以了

3 main.js 文件 加载settings.js 的时候需要注意使用 window.require

4 游戏路径一定要对应

子游戏的main.js千万不要重复加载大厅已经require 的文件

测试用的资源服务器推荐hfs 相当轻便,

测试时可以使用android studio 来实时查看日志。

 --------------------

2019/12/4

实用只到大厅热更新,子游戏热更新,大厅子游戏切换这里,用到项目里还需要填uuid冲突,公共和私有资源下载。。。等一堆坑

 

 

教程参考:

https://www.jianshu.com/p/094cd0e95e55

https://www.jianshu.com/p/fe54ca980384

 

北上杭码农聚集地,qq群号: 199678137  欢迎大佬小白进群讨论技术问题(〃'▽'〃)

 

 

 

Laya 真香 哈哈哈哈 20200402

 

  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 5
    评论
### 回答1: JetBrains 对于 v2.0.10 版本提供以下支持: 首先,JetBrains 公司提供了详细的官方文档和教程,可以帮助用户了解和使用 v2.0.10 版本。用户可以通过查阅文档来了解软件的功能、设置和使用方法,以便更好地利用软件进行开发工作。 其次,JetBrains 为 v2.0.10 版本提供了全面的技术支持。用户可以通过官方网站提供的技术支持渠道,例如在线帮助中心、社区论坛和电邮件支持,获取关于软件的各种技术问题的帮助和解答。无论是遇到安装问题、配置错误还是功能使用上的疑问,用户都可以得到专业的支持和指导。 此外,JetBrains 还通过定期的升级和更新来提供对 v2.0.10 版本的支持。这些升级包括软件的稳定性改进、bug 修复和新功能的添加,以确保用户在使用中获得更好的体验和效果。用户可以通过官方渠道获取这些升级,并根据自己的需要进行安装和升级操作。 总之,JetBrains 公司对于 v2.0.10 版本提供了全面的支持,包括官方文档、技术支持和定期升级等。这些支持措施帮助用户更好地理解和使用软件,提高开发效率和质量。用户可以放心地使用和依赖于 JetBrains 提供的支持,从而更好地完成软件开发工作。 ### 回答2: JetBrains IDE是JetBrains公司的一系列集成开发环境软件,包括IntelliJ IDEA、PyCharm、WebStorm等等。这些软件都是为开发者提供便利的工具和环境,帮助用户高效地开发各种类型的应用程序。 JetBrains IDE在支持v2.0.10方面有一系列的功能和特性。首先,它提供了对这个版本的全面支持,确保用户可以正常使用该版本进行开发工作。无论是在代码编辑、调试还是其他开发过程中,JetBrains IDE都能提供完整的工具和功能,让开发者能够更加方便地完成任务。 其次,JetBrains IDE针对v2.0.10还提供了一些特定的功能和改进。例如,它可能会在该版本中引入新的语言支持或者增强已有的功能。这些改进使得开发者能够有更好的体验和更高的生产力,同时也能够更好地利用新的技术和特性来开发应用程序。 另外,JetBrains IDE还可能通过对v2.0.10的支持来修复一些已知的bug和问题。这些修复可以提高软件的稳定性和性能,保证用户在开发过程中不会遇到一些意外的错误和障碍。 总的来说,JetBrains IDE对v2.0.10的支持是为了帮助开发者更好地使用这个版本进行开发工作。它提供了全面的支持、特定的功能改进和bug修复,以提升开发者的体验和生产力。无论是从代码编写到调试测试,JetBrains IDE都能够满足开发者的需求,并提供一站式的开发解决方案。 ### 回答3: jetbrainsidesupportfor-v2.0.10是JetBrains IDE(Integrated Development Environment,集成开发环境)的一个版本号。JetBrains是一家世界领先的IDE开发工具提供商,他们提供多种具有高度智能化功能的IDE,可以帮助开发人员更高效地进行软件开发。 jetbrainsidesupportfor-v2.0.10意味着JetBrains对这个版本的IDE提供了支持。这种支持可以包括修复已知的bug和漏洞、添加新功能和改进已有功能等。 用户可以从JetBrains的官方网站上下载和升级到这个版本。 JetBrains IDE是为多种编程语言和技术提供支持的,如Java、Python、JavaScript等。它们在代码编辑、调试、测试和部署等方面提供了全面的功能。这些IDE通常具有智能代码完成、重构、调试工具、版本控制集成等高级功能,它们可以提高开发人员的效率和代码质量。 对于jetbrainsidesupportfor-v2.0.10,用户可以期待一些新的功能和改进,使他们在开发过程中更加高效和便捷。此外,JetBrains还通常提供很多教育资源和技术支持,以帮助用户更好地使用他们的IDE。 总之,jetbrainsidesupportfor-v2.0.10是JetBrains为其集成开发环境的一个版本,代表其提供支持和更新,使开发人员能够更好地创建高质量的软件。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值