cocos2d / cocos2d-html5

Cocos2d for Web Browsers. Built using JavaScript.
https://www.cocos.com
3.07k stars 902 forks source link

Need a method to reset the entire cocos2d engine #2364

Open JakeLin opened 10 years ago

JakeLin commented 10 years ago

I am using Meteor and I found a serious issue with cocos2d-js at the moment. Most of the new JavaScript frameworks such as Meteor and Angular.js will use client side routing system. It means when the user navigates from one page to the other page, the page doesn't hard load the entire HTML. It may use something like ajax to retrieve the content and dynamically update the DOM without reloading the entire HTML again. It comes a problem with cocos2d-js in that case. cocos2d-js has a global variable called cc. If the browser reloads the entire HTML, all javascript files (or cached files) will be reloaded (re-executed), the variable cc will be re-init again. But if we use some Javascript frameworks like Meteor or Angular.js. the variable cc will remain the same. And when we try to init the game (another game in the second page) by using cc.game.run('gameCanvas');. Some of properties (for instance, _setupCalled) had been set before (when we inited the first game). And we can't start the game on the new page because _setupCalled is true. Cocos2d can't setup the Canvas properly.

I have tried to wrapped all the source code in one init() method, and call this method in document.ready() method. The code is open source on https://github.com/JakeLin/cocos2d-meteor

It seams working, but I get another issue with cc.game.setFrameRate(30). Every time, I call this method, the FPS will decrease about half. For example, the first game is 30, the second game is 15, the third one is 10, the forth one is 7.5. I can't find out the reason when I dive into the source code. Could you please give some hints?

Thanks Jake

pandamicro commented 10 years ago

Good point, I think we need this feature, it's very useful in some case

JakeLin commented 10 years ago

@pandamicro Do you have any thoughts on the cc.game.setFrameRate(30) issue? Thanks.

pandamicro commented 10 years ago

@JakeLin Unfortunately I can't reproduce the effect by calling cc.game.setFrameRate(30), and I can't find the init function in your code. Can you point it out ?

JakeLin commented 10 years ago

@pandamicro ,Thanks for looking at this issue. If you look at my code at https://raw.githubusercontent.com/JakeLin/cocos2d-meteor/master/cocos2d-js-v3.0.js

var ccInit = ccInit || {}; ccInit.init = function () { //all cocos2d js file };

You may find a object called ccInit and it has an init method.

In my game project. I put all my logic (Scene, Layer, etc) within

Template.game.rendered = function() {
  // init cocos2d
  ccInit.init();

  // all logic are placed below the init() method.
  var GameLayer = cc.Layer.extend({ 
    // ...
  }); 

  // Scene
  var GameScene = cc.Scene.extend({
    onEnter: function () {
      this._super();
      // add a layer to the scene
      gameLayer = new GameLayer();
      this.addChild(gameLayer);
    }
  });

  // Start the game
  cc.game.onStart = function(){
    //load resources
    cc.LoaderScene.preload([], function () {
      cc.director.runScene(new GameScene());
      cc.director.setDisplayStats(true); // show FPS
      // http://discuss.cocos2d-x.org/t/how-to-set-fps/3349/2
      // this method doesn't work
      // cc.director.setAnimationInterval(1.0/fps);
      }, this);
  };
  // cc._setupCalled = false; // hack for Meteor. actually, doesn't work
  // cc.game.config['showFPS'] = true; // doesn't work either
  cc.game.run('gameCanvas'); // that works because I have reinited the engine by using ccInit.init();
  cc.game.setFrameRate(fps); // that causes problem, it will decrease the FPS every time. for example, 1st time, 30. 2nd time, 15, 3rd 10, 4th 7.5. 5th 5...
};

You can treat Template.game.rendered as same as document.ready() in jQuery. I put all cocos2d game code below ccInit.init(); which is at https://raw.githubusercontent.com/JakeLin/cocos2d-meteor/master/cocos2d-js-v3.0.js . cc.game.run('gameCanvas'); starts working since I have reinited the engine by using ccInit.init();, However, cc.game.setFrameRate(fps); causes problem, it will decrease the FPS every time. for example, 1st time, 30. 2nd time, 15, 3rd 10, 4th 7.5. 5th 5...

Thanks for your time, I could like to fix it if I have time, the architecture of cocos2d-js seems quite complicated already. I think we need some refactoring to improve the engine.

Thanks Jake

gaokun commented 8 years ago

Hi Guys, I am using AngularJS and have same issue. And I think we have more tough problems in the case, like "how to resume scene when user back to page", "how to make project.json for each game. (we can only have one project.json file for now in cocos2d-html5)". I am trying to work on this issue. SPA is more and more popular, hope cocos will catch it up. ^_^

Thanks Ken

gaokun commented 8 years ago

I have a workaround on this issue, and can restart game after router page changed in Angularjs.

Changes like below: (Cocos2d-html5 V3.5)

1. in cc._setup function:
old code:
    if (cc._renderType === cc._RENDER_TYPE_WEBGL)
        cc._renderContext = cc.webglContext = cc.create3DContext(localCanvas, {
            'stencil': true,
            'preserveDrawingBuffer': true,
            'antialias': !cc.sys.isMobile,
            'alpha': false
        });
    if (cc._renderContext) {
        win.gl = cc._renderContext; // global variable declared in CCMacro.js
        cc._drawingUtil = new cc.DrawingPrimitiveWebGL(cc._renderContext);
        cc._rendererInitialized = true;
        cc.textureCache._initializingRenderer();
        cc.shaderCache._init();
    } else {
        cc._renderContext = new cc.CanvasContextWrapper(localCanvas.getContext("2d"));
        cc._drawingUtil = cc.DrawingPrimitiveCanvas ? new cc.DrawingPrimitiveCanvas(cc._renderContext) : null;
    }
new code: just support canvas model for now, project pushs me, and we just need canvas render for now
    if (cc._renderType === cc._RENDER_TYPE_WEBGL) {
        cc._renderContext = cc.webglContext = cc.create3DContext(localCanvas, {
            'stencil': true,
            'preserveDrawingBuffer': true,
            'antialias': !cc.sys.isMobile,
            'alpha': false
        });
        if (cc._renderContext) {
            win.gl = cc._renderContext; // global variable declared in CCMacro.js
            cc._drawingUtil = new cc.DrawingPrimitiveWebGL(cc._renderContext);
            cc._rendererInitialized = true;
            cc.textureCache._initializingRenderer();
            cc.shaderCache._init();
        } else {
            cc._renderContext = new cc.CanvasContextWrapper(localCanvas.getContext("2d"));
            cc._drawingUtil = cc.DrawingPrimitiveCanvas ? new cc.DrawingPrimitiveCanvas(cc._renderContext) : null;
        }
    }
    else {
        cc._renderContext = new cc.CanvasContextWrapper(localCanvas.getContext("2d"));
        cc._drawingUtil = cc.DrawingPrimitiveCanvas ? new cc.DrawingPrimitiveCanvas(cc._renderContext) : null;
    }
2. then you can run code below when you enter game page:
        cc._setupCalled = false;
        cc.game._prepareCalled = false;
        cc.game._prepared = false;
        cc.EGLView && (cc.EGLView._instance = null);

        cc.game.onStart = function(){
            if(!cc.sys.isNative && document.getElementById("cocosLoading")) //If referenced loading.js, please remove it
                document.body.removeChild(document.getElementById("cocosLoading"));

            // Pass true to enable retina display, disabled by default to improve performance
            cc.view.enableRetina(false);
            // Adjust viewport meta
            cc.view.adjustViewPort(true);
            // Setup the resolution policy and design resolution size
            cc.view.setDesignResolutionSize(800, 450, cc.ResolutionPolicy.SHOW_ALL);
            // The game will be resized when browser size change
            cc.view.resizeWithBrowserSize(true);
            //load resources
            cc.LoaderScene.preload(g_resources, function () {
                cc.director.runScene(new HelloWorldScene());
            }, this);
        };
        cc.game.run();

But there is only one project.json file, so can't support multi-games configration for now, still working on it

Thanks Ken

gaokun commented 8 years ago

OK, we can use cocos for mutil-games in Angularjs now.

        cc._setupCalled = false;
        cc.game._prepareCalled = false;
        cc.game._prepared = false;
        cc.EGLView && (cc.EGLView._instance = null);
        cc.loader._jsCache = {}; // remove js cache

        //load different jsList for each game. You can wrap it like json.
        //also for modules
        if($stateParams.gameId==1) {
            cc.game.config.jsList = [
                "./classdojo/games/demo/src/resource.js",
                "./classdojo/games/demo/src/app.js"
            ];
        }
        else {
            cc.game.config.jsList = [
                "./classdojo/games/demo2/src/resource.js",
                "./classdojo/games/demo2/src/app.js"
            ];
        }

        cc.game.onStart = function(){
            if(!cc.sys.isNative && document.getElementById("cocosLoading")) //If referenced loading.js, please remove it
                document.body.removeChild(document.getElementById("cocosLoading"));

            // Pass true to enable retina display, disabled by default to improve performance
            cc.view.enableRetina(false);
            // Adjust viewport meta
            cc.view.adjustViewPort(true);
            // Setup the resolution policy and design resolution size
            cc.view.setDesignResolutionSize(800, 450, cc.ResolutionPolicy.SHOW_ALL);
            // The game will be resized when browser size change
            cc.view.resizeWithBrowserSize(true);
            //load resources
            cc.LoaderScene.preload(g_resources, function () {
                cc.director.runScene(new HelloWorldScene());
            }, this);
        };
        cc.game.run();

Thanks Ken

JakeLin commented 8 years ago

well done @gaokun

gaokun commented 8 years ago

One more issue about system event. We must registerSystemEvent after restart, but cc.inputManager was registered by last setup. And cc.inputManager is not loaded when cc.game.run()

so I hacked the cc._setup function:

cc.myHackSetup = function() {
    var setupString = cc._setup.toString();
    function forDebug() {
        //make a breakpoint here for debug cc._setup
        //console.log('hack cc._setup function');
        cc.inputManager._isRegisterEvent = false;
    }
    setupString = setupString.replace('if (cc._renderContext)','forDebug();\nif(false)');
    setupString = 'cc._setup = '+setupString;
    eval(setupString);
};

The code above just support canvas render model.

I will update this topic when I get any progress on this. Thanks Ken

vohtaski commented 7 years ago

is there a better solution that does not involve the hack?