美文网首页
CocosCreator大厅+子游戏(v1.10.2)

CocosCreator大厅+子游戏(v1.10.2)

作者: Dane_404 | 来源:发表于2019-04-24 14:06 被阅读0次

    基于热更新的基础上,将子游戏构建生成的main.js文件一并移入src目录,在运行子游戏的时候,我们只需要require main.js这个文件即可。

    大厅跳到子游戏

    首先是大厅封装好的子游戏管理类,包括子游戏下载、更新、进入

    export  class SubgameManager  {
    
        private static serverUrl;
        private static storagePath:[] = [];
    
    
        private static assertsMg;
        private static jsbCallback;
    
        private static subgameUpdateCallback;
        private static progressCallback;
        private static finishCallback;
    
        public static init(serverUrl:string){
            this.serverUrl = serverUrl;
        }
    
        public static isSubgameDownload(name:string):boolean{
    
            let file = (jsb.fileUtils ? jsb.fileUtils.getWritablePath() : '/') + 'ALLGame/' + name + '/project.manifest';
            if (jsb.fileUtils.isFileExist(file)) {
                return true;
            } else {
                return false;
            }
        }
    
        public static isNeedUpdateSubgame(name:string,subgameUpdateCallback?:Function){
            this.prepareJsb(name);
            this.subgameUpdateCallback = subgameUpdateCallback;
            this.jsbCallback =  new jsb.EventListenerAssetsManager(this.assertsMg, this.needUpdateCallback.bind(this));
            cc.eventManager.addListener(this.jsbCallback, 1);
            this.assertsMg.checkUpdate();
        }
    
        private static needUpdateCallback(event){
            let self = this;
            switch (event.getEventCode()) {
                case jsb.EventAssetsManager.ALREADY_UP_TO_DATE:
                    cc.log('子游戏已经是最新的,不需要更新');
                    self.subgameUpdateCallback && self.subgameUpdateCallback(false);
                    break;
    
                case jsb.EventAssetsManager.NEW_VERSION_FOUND:
                    cc.log('子游戏需要更新');
                    self.subgameUpdateCallback && self.subgameUpdateCallback(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;
            }
        }
    
        public static downloadSubgame(name:string,progressCallback?:Function,finishCallback?:Function){
            this.prepareJsb(name);
            this.progressCallback = progressCallback;
            this.finishCallback = finishCallback;
            this.jsbCallback =  new jsb.EventListenerAssetsManager(this.assertsMg, this.downloadCallback.bind(this));
            cc.eventManager.addListener(this.jsbCallback, 1);
            this.assertsMg.update();
        }
    
        private static downloadCallback(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 最新进展 */
                    cc.log("event.getPercentByFile()"+event.getPercentByFile());
                    self.progressCallback && self.progressCallback(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更新完成*/
                    cc.log("UPDATE_FINISHED");
                    self.finishCallback && self.finishCallback(true);
                    break;
    
                case jsb.EventAssetsManager.UPDATE_FAILED:
                    /*9更新失败*/
                    cc.log('UPDATE_FAILED');
                    self.assertsMg.downloadFailedAssets();
                    
                    break;
    
                case jsb.EventAssetsManager.ERROR_DECOMPRESS:
                    /*10解压失败*/
                    cc.log('解压失败');
                    break;
            }
    
            if (failed) {
                cc.eventManager.removeListener(self.jsbCallback);
                self.jsbCallback = null;
                self.finishCallback && self.finishCallback(false);
            }
        }
    
        public static enterSubgame(name) {
            if (!this.storagePath[name]) {
                this.downloadSubgame(name);
                return;
            }
    
            window.require(this.storagePath[name] + '/src/main.js');
        }
    
    
        private static prepareJsb(name:string){
            this.storagePath[name] = ((jsb.fileUtils ? jsb.fileUtils.getWritablePath() : '/') + 'ALLGame/' + name);
          
            var UIRLFILE = this.serverUrl +"/"+ name;
        
            var customManifestStr = JSON.stringify({
                'packageUrl': UIRLFILE,
                'remoteManifestUrl': UIRLFILE + '/project.manifest',
                'remoteVersionUrl': UIRLFILE + '/version.manifest',
                'version': '0.0.1',
                'assets': {},
                'searchPaths': []
            });
    
            this.assertsMg = new jsb.AssetsManager('', this.storagePath[name], this.versionCompare);
    
            if (!cc.sys.ENABLE_GC_FOR_NATIVE_OBJECTS) {
                this.assertsMg.retain();
            }
    
            this.assertsMg.setVerifyCallback(function(path, asset) {
                var compressed = asset.compressed;
                if (compressed) {
                    return true;
                } else {
                    return true;
                }
            });
    
            if (cc.sys.os === cc.sys.OS_ANDROID) {
                this.assertsMg.setMaxConcurrentTask(2);
            }
    
            if (this.assertsMg.getState() === jsb.AssetsManager.State.UNINITED) {
                var manifest = new jsb.Manifest(customManifestStr, this.storagePath[name]);
                this.assertsMg.loadLocalManifest(manifest, this.storagePath[name]);
            }
    
        }
    
        private static versionCompare(versionA, versionB):number{
    
            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;
                }
        }
     
    }
    

    接着看使用这个类:

    import { SubgameManager } from "./SubgameManager";
    
    const {ccclass, property} = cc._decorator;
    
    @ccclass
    export default class Subgame extends cc.Component {
    
        @property(cc.Label)
        label: cc.Label = null;
    
        private subgame = "Niuniu"
    
        onLoad(){
    
            SubgameManager.init("http://192.168.0.136:8000");
    
            if(SubgameManager.isSubgameDownload(this.subgame)){
    
                SubgameManager.isNeedUpdateSubgame(this.subgame,(isSuccess)=>{
                    this.label.string = isSuccess ? "子游戏需要更新" : "子游戏不需要更新";
                });
    
            }else{
                this.label.string = "子游戏未下载";
            }
    
        }
    
        click(){
            SubgameManager.downloadSubgame(this.subgame,(progress)=>{
                if (isNaN(progress)) {
                    progress = 0;
                }
                this.label.string = "资源下载中   " + ~~(progress * 100) + "%";
            },(success)=>{
                if (success) {
                    SubgameManager.enterSubgame(this.subgame);
                } 
            })
        }
    }
    

    准备好之后,开始准备小游戏,首先将小游戏构建下,模板是default,如果使用脚本加密,那么大厅与子游戏脚本加密的key一定要相同!!因为主程序是大厅,解密脚本都是用大厅的key。构建成功后,将main.js复制一份到src下,然后打开修改两个地方。无论creator哪个版本,以构建出来的main.js为主,然后同样修改这两地方就好了

     //~~~~~~~~~1、添加这段~~~~~~~~~~~~~~~
         cc.director.startAnimation();  //官方说解决个BUG
         'use strict';
         //后面的路径根据自己的游戏修改
         cc.INGAME = (jsb.fileUtils ? jsb.fileUtils.getWritablePath() : '/')+'ALLGame/Niuniu/';  
     //~~~~~~~~~~~~~~~~~~~~~~~~~~
     //~~~~~~~~~2.修改这段~~~~~~~~~~~~~~~
          require(cc.INGAME + 'src/settings.js');
          require(cc.INGAME  +window._CCSettings ? 'src/project.dev.js' : 'src/project.js');
          // require('src/jsb_polyfill.js');
     //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    
    

    整份main.js如下(v1.10.2):

    (function () {
    
        //~~~~~~~~~1、添加这段~~~~~~~~~~~~~~~
         cc.director.startAnimation();  //官方说解决个BUG
         'use strict';
         cc.INGAME = (jsb.fileUtils ? jsb.fileUtils.getWritablePath() : '/')+'ALLGame/Niuniu/';  
         //~~~~~~~~~~~~~~~~~~~~~~~~~~
    
        function boot () {
    
            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]];
                        }
                    }
                }
            }
    
            // init engine
            var canvas;
    
            if (cc.sys.isBrowser) {
                canvas = document.getElementById('GameCanvas');
            }
    
            if (false) {
                var ORIENTATIONS = {
                    'portrait': 1,
                    'landscape left': 2,
                    'landscape right': 3
                };
                BK.Director.screenMode = ORIENTATIONS[settings.orientation];
                initAdapter();
            }
    
            function setLoadingDisplay () {
                // Loading splash scene
                var splash = document.getElementById('splash');
                var progressBar = splash.querySelector('.progress-bar span');
                cc.loader.onProgress = function (completedCount, totalCount, item) {
                    var percent = 100 * completedCount / totalCount;
                    if (progressBar) {
                        progressBar.style.width = percent.toFixed(2) + '%';
                    }
                };
                splash.style.display = 'block';
                progressBar.style.width = '0%';
    
                cc.director.once(cc.Director.EVENT_AFTER_SCENE_LAUNCH, function () {
                    splash.style.display = 'none';
                });
            }
    
            var onStart = function () {
                cc.loader.downloader._subpackages = settings.subpackages;
    
                if (false) {
                    BK.Script.loadlib();
                }
    
                cc.view.resizeWithBrowserSize(true);
    
                if (!false && !false) {
                    if (cc.sys.isBrowser) {
                        setLoadingDisplay();
                    }
    
                    if (cc.sys.isMobile) {
                        if (settings.orientation === 'landscape') {
                            cc.view.setOrientation(cc.macro.ORIENTATION_LANDSCAPE);
                        }
                        else if (settings.orientation === 'portrait') {
                            cc.view.setOrientation(cc.macro.ORIENTATION_PORTRAIT);
                        }
                        cc.view.enableAutoFullScreen([
                            cc.sys.BROWSER_TYPE_BAIDU,
                            cc.sys.BROWSER_TYPE_WECHAT,
                            cc.sys.BROWSER_TYPE_MOBILE_QQ,
                            cc.sys.BROWSER_TYPE_MIUI,
                        ].indexOf(cc.sys.browserType) < 0);
                    }
    
                    // Limit downloading max concurrent task to 2,
                    // more tasks simultaneously may cause performance draw back on some android system / browsers.
                    // You can adjust the number based on your own test result, you have to set it before any loading process to take effect.
                    if (cc.sys.isBrowser && cc.sys.os === cc.sys.OS_ANDROID) {
                        cc.macro.DOWNLOAD_MAX_CONCURRENT = 2;
                    }
                }
    
                // init assets
                cc.AssetLibrary.init({
                    libraryPath: 'res/import',
                    rawAssetsBase: 'res/raw-',
                    rawAssets: settings.rawAssets,
                    packedAssets: settings.packedAssets,
                    md5AssetsMap: settings.md5AssetsMap
                });
    
                if (false) {
                    cc.Pipeline.Downloader.PackDownloader._doPreload("WECHAT_SUBDOMAIN", settings.WECHAT_SUBDOMAIN_DATA);
                }
    
                var launchScene = settings.launchScene;
    
                // load scene
                cc.director.loadScene(launchScene, null,
                    function () {
                        if (cc.sys.isBrowser) {
                            // show canvas
                            canvas.style.visibility = '';
                            var div = document.getElementById('GameDiv');
                            if (div) {
                                div.style.backgroundImage = '';
                            }
                        }
                        cc.loader.onProgress = null;
                        console.log('Success to load scene: ' + launchScene);
                    }
                );
            };
    
            // jsList
            var jsList = settings.jsList;
    
            if (!false) {
                var bundledScript = settings.debug ? 'src/project.dev.js' : 'src/project.js';
                if (jsList) {
                    jsList = jsList.map(function (x) {
                        return 'src/' + x;
                    });
                    jsList.push(bundledScript);
                }
                else {
                    jsList = [bundledScript];
                }
            }
    
            // anysdk scripts
            if (cc.sys.isNative && cc.sys.isMobile) {
    //            jsList = jsList.concat(['src/anysdk/jsb_anysdk.js', 'src/anysdk/jsb_anysdk_constants.js']);
            }
    
            var option = {
                //width: width,
                //height: height,
                id: 'GameCanvas',
                scenes: settings.scenes,
                debugMode: settings.debug ? cc.DebugMode.INFO : cc.DebugMode.ERROR,
                showFPS: (!false && !false) && settings.debug,
                frameRate: 60,
                jsList: jsList,
                groupList: settings.groupList,
                collisionMatrix: settings.collisionMatrix,
                renderMode: 0
            }
    
            cc.game.run(option, onStart);
        }
    
        if (false) {
            BK.Script.loadlib('GameRes://libs/qqplay-adapter.js');
            BK.Script.loadlib('GameRes://src/settings.js');
            BK.Script.loadlib();
            BK.Script.loadlib('GameRes://libs/qqplay-downloader.js');
            qqPlayDownloader.REMOTE_SERVER_ROOT = "";
            var prevPipe = cc.loader.md5Pipe || cc.loader.assetLoader;
            cc.loader.insertPipeAfter(prevPipe, qqPlayDownloader);
            // <plugin script code>
            boot();
            return;
        }
    
        if (false) {
            require(window._CCSettings.debug ? 'cocos2d-js.js' : 'cocos2d-js-min.js');
            require('./libs/weapp-adapter/engine/index.js');
            var prevPipe = cc.loader.md5Pipe || cc.loader.assetLoader;
            cc.loader.insertPipeAfter(prevPipe, wxDownloader);
            boot();
            return;
        }
    
        if (window.jsb) {
    
            //~~~~~~~~~2.修改这段~~~~~~~~~~~~~~~
            require(cc.INGAME + 'src/settings.js');
            require(cc.INGAME  +window._CCSettings ? 'src/project.dev.js' : 'src/project.js');
              // require('src/jsb_polyfill.js');
            //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
          
            boot();
            return;
        }
    
        if (window.document) {
            var splash = document.getElementById('splash');
            splash.style.display = 'block';
    
            var cocos2d = document.createElement('script');
            cocos2d.async = true;
            cocos2d.src = window._CCSettings.debug ? 'cocos2d-js.js' : 'cocos2d-js-min.js';
    
            var engineLoaded = function () {
                document.body.removeChild(cocos2d);
                cocos2d.removeEventListener('load', engineLoaded, false);
                if (typeof VConsole !== 'undefined') {
                    window.vConsole = new VConsole();
                }
                boot();
            };
            cocos2d.addEventListener('load', engineLoaded, false);
            document.body.appendChild(cocos2d);
        }
    
    })();
    

    修改完成后,利用上一篇热更新提到的version_generator.js,生成project. manifest和version. manifest,这里步骤不能变,一定先构建好子游戏,复制main.js到src并修改,再利用version_generator.js生成project. manifest和version. manifest。准备好之后,将src、res、project. manifest、version. manifest放在服务器:

    image.png
    然后可以测试跳到子游戏了。

    子游戏返回大厅

    在大厅跳到子游戏时,我们利用了main.js,同理的,返回大厅也是。首先准好返回大厅的代码,注意我目前的版本需要window.require,网上其他文章好像1.5.1以前只需要require

     returnHall(){
          let subgameSearchPath = (jsb.fileUtils ? jsb.fileUtils.getWritablePath() : '/')+'ALLGame/Niuniu/';
          window.require(subgameSearchPath + 'src/hall.js');
      }
    

    然后设置好点击事件构建后,与上面的步骤一样复制main.js到src并修改,然后将修改完的main.js复制一份,改名为hall.js,修改hall.js的 cc.INGAME,这里区分Android与iOS

     if (cc.sys.os === cc.sys.OS_ANDROID) {
          cc.INGAME = 'assets/';
     }else if(cc.sys.os == cc.sys.OS_IOS){
          cc.INGAME = jsb.reflection.callStaticMethod("AppController", "getHallPath")+"/";
     }
    

    iOS还需要在xcode中,AppController类下加入方法getHallPath:

    + (NSString *)getHallPath
    {
        return [[NSBundle mainBundle] bundlePath];
    }
    

    解决游戏之间cid、classname冲突问题

    A Class already exists with the same cid
    cid冲突可能是复制原因造成的,解决的方法是把冲突的脚本移出工程,再等creator刷新后,重新导入进来。

    A Class already exists with the same classname
    classname冲突,如果是公用的脚本,比如一些通用类,在各个游戏一样的话,可以忽略,creator不会重新加载,但那些有区别的类名又相同的,目前的做法是每个游戏都类名都加游戏前缀。

    解决内存问题

    已知的问题:

    假如进去子游戏一次,退出到大厅,发现更新了,更新子游戏了,再进去子游戏没有更新到,因为子游戏的数据还在内存,不会再去重新load。

    子游戏退出到大厅,内存数据还在,下次进入子游戏的数据还是最后一次修改的数据,不会重置。

    目前没有很好的方案,我们用了一种偏方,返回大厅都用cc.game.restart,黑屏的问题,利用原生交互弹一张loading,因为cc.game.restart不会重启应用,用一张loading图先盖住creator,等大厅onenable是时候隐藏了loading。

    相关文章

      网友评论

          本文标题:CocosCreator大厅+子游戏(v1.10.2)

          本文链接:https://www.haomeiwen.com/subject/ewcvgqtx.html