cocos2d-x-html5之遊戲啓動2

[上一篇]講了調用了cc.game.run後,會執行cc.game.onStart函數,然後項目組在onStart函數中實現了自己的邏輯。 這一篇就具體解析一下是怎麼執行到cc.game.onStart函數。
在CCBoot.js的實現中,我們可以看到 cc.game.run中做了一下操作
1、調用cc.game.prepare() :
2、加載project.json : cc.game._loadConfg()
3、加載引擎 代碼: 加載moduleConfig.json裏面module對應的配置
4、加載項目代碼: 加載project.json裏面 jsList 包含的代碼列表
5、運行 director.mainLoop.js 進入主循環,時間模塊初始化等邏輯,並執行 cc.game.onStart

接下來看下具體代碼:


//+++++++++++++++++++++++++something about CCGame begin+++++++++++++++++++++++++++
/**
 * An object to boot the game.
 * @class
 * @name cc.game
 *
 */
cc.game = /** @lends cc.game# */{
	/**
     * Run game with configuration object and onStart function.
     * @param {Object|Function} [config] Pass configuration object or onStart function
     * @param {onStart} [onStart] onStart function to be executed after game initialized
     */
    run: function (config, onStart) {
        // 如果第一個參數爲函數,則賦值給onStart(比較多是直接 cc.game.onStart = function() {...})
        if (typeof config === 'function') {
            cc.game.onStart = config;
        } else {
            // config參數存在時
            if (config) {
                // 如果爲字符串 
                if (typeof config === 'string') {
                    // 不存在config時,加載config cc.game.config在_loadConfig()執行成功後,內容爲 project.json的內容
                    if (!cc.game.config) this._loadConfig();
                    // 賦值給cc.game.config.id
                    cc.game.config[cc.game.CONFIG_KEY.id] = config;
                } else { // 不是字符串,直接賦值給cc.game.config
                    cc.game.config = config;
                }
            }
            // onStart爲函數時
            if (typeof onStart === 'function') {
                cc.game.onStart = onStart;
            }
        }
		// 運行prepare函數
        this.prepare(cc.game.onStart && cc.game.onStart.bind(cc.game));
    },
	
	//  @Game loading
    /**
     * Prepare game.
     * @param cb project.json解析、加載引擎代碼、加載遊戲代碼等準備工作完成後調用  目前爲 cc.game.onStart
     */
    prepare: function (cb) {
        var self = this,
            config = self.config,
            CONFIG_KEY = self.CONFIG_KEY;

        // Config loaded
        // config 未加載
        if (!this._configLoaded) {
            // 加載配置文件 project.josn
            this._loadConfig(function () {
                // 加載後重新調用 準備
                self.prepare(cb);
            });
            return;
        }

        // Already prepared
        // 如果已經準備好了,直接執行回調
        if (this._prepared) {
            if (cb) cb();
            return;
        }
        // Prepare called, but not done yet
        // 到這裏是 引擎代碼加載過,但是遊戲代碼還未加載
        // 疑問 如果cb存在不存起來,後面遊戲代碼加載後 執行?
        if (this._prepareCalled) {
            return;
        }
        // Prepare never called and engine ready
        // 引擎代碼已經加載過
        if (cc._engineLoaded) {
            this._prepareCalled = true;
            // 初始化 渲染
            this._initRenderer(config[CONFIG_KEY.width], config[CONFIG_KEY.height]);

            /**
             * cc.view is the shared view object.
             * @type {cc.EGLView}
             * @name cc.view
             * @memberof cc
             */
            cc.view = cc.EGLView._getInstance();

            /**
             * @type {cc.Director}
             * @name cc.director
             * @memberof cc
             */
            cc.director = cc.Director._getInstance();
            if (cc.director.setOpenGLView)
                cc.director.setOpenGLView(cc.view);
            /**
             * cc.winSize is the alias object for the size of the current game window.
             * @type {cc.Size}
             * @name cc.winSize
             * @memberof cc
             */
            // 賦值cc.winSize
            cc.winSize = cc.director.getWinSize();

            // 初始化事件
            this._initEvents();

            // 設置幀率
            this._setAnimFrame();
            // 運行主循環
            this._runMainLoop();

            // Load game scripts
            // 加載遊戲代碼
            var jsList = config[CONFIG_KEY.jsList];
            if (jsList) {
                cc.loader.loadJsWithImg(jsList, function (err) {
                    if (err) throw new Error(err);
                    self._prepared = true;
                    // 加載完成後,運行回調 (這裏就是cc.game.onStart)
                    if (cb) cb();
                });
            } else {
                if (cb) cb();
            }

            return;
        }

        // Engine not loaded yet
        // 初始化引擎,執行後,cc._engineLoaded 設爲true
        cc.initEngine(this.config, function () {
            self.prepare(cb);
        });

        // 總結:這裏 this._configLoaded、 this._prepared 、this._prepareCalled、cc._engineLoaded
        // 這幾個變量的控制,讓 project.json文件解析、引擎代碼加載、遊戲代碼加載 這幾個異步流程 沒有異步嵌套 
    },
    
    //  @Game loading section
    // 加載 project.json 文件
    _loadConfig: function (cb) {
        // Load config
        var config = this.config || document["ccConfig"];
        // Already loaded or Load from document.ccConfig
        // config存在,直接初始化配置
        if (config) {
            this._initConfig(config);
            cb && cb();
        }
        // Load from project.json
        else {
            // 所有的script元素
            var cocos_script = document.getElementsByTagName('script');
            for (var i = 0; i < cocos_script.length; i++) {
                var _t = cocos_script[i].getAttribute('cocos');
                // 找到第一個有cocos屬性,用於修改projection.json所在的路徑
                if (_t === '' || _t) {
                    break;
                }
            }
            var self = this;
            var loaded = function (err, txt) {
                // 加載出來的內容 用JSON 解析一下
                var data = JSON.parse(txt);
                // 初始化配置
                self._initConfig(data);
                cb && cb();
            };
            var _src, txt, _resPath;
            if (i < cocos_script.length) {
                _src = cocos_script[i].src;
                if (_src) {
                    // 找到 a/b/c.js 的文件夾路徑  a/b/
                    _resPath = /(.*)\//.exec(_src)[0];
                    cc.loader.resPath = _resPath;
                    _src = cc.path.join(_resPath, 'project.json');
                }
                cc.loader.loadTxt(_src, loaded);
            }
            if (!txt) {
                cc.loader.loadTxt("project.json", loaded);
            }
        }
    },

    _initConfig: function (config) {
        // 配置的key
        var CONFIG_KEY = this.CONFIG_KEY,
            modules = config[CONFIG_KEY.modules];

        // Configs adjustment
        // 是否顯示幀率信息
        config[CONFIG_KEY.showFPS] = typeof config[CONFIG_KEY.showFPS] === 'undefined' ? true : config[CONFIG_KEY.showFPS];
        // 引擎所在文件夾
        config[CONFIG_KEY.engineDir] = config[CONFIG_KEY.engineDir] || "frameworks/cocos2d-html5";
        // debug模式
        if (config[CONFIG_KEY.debugMode] == null)
            config[CONFIG_KEY.debugMode] = 0;
        // 暴露類名讓 Chrome DevTools 可以識別,如果開啓會稍稍降低類的創建過程的性能,但對對象構造沒有影響
        config[CONFIG_KEY.exposeClassName] = !!config[CONFIG_KEY.exposeClassName];
        // 幀率
        config[CONFIG_KEY.frameRate] = config[CONFIG_KEY.frameRate] || 60;
        // 渲染模式
        if (config[CONFIG_KEY.renderMode] == null)
            config[CONFIG_KEY.renderMode] = 0;
        // 註冊系統事件
        if (config[CONFIG_KEY.registerSystemEvent] == null)
            config[CONFIG_KEY.registerSystemEvent] = true;

        // Modules adjustment
        // 如果包含沒有core的話,在第一個插入 “core”
        if (modules && modules.indexOf("core") < 0) modules.splice(0, 0, "core");
        modules && (config[CONFIG_KEY.modules] = modules);
        this.config = config;
        // 標識 已經加載過
        this._configLoaded = true;
    },
};


//+++++++++++++++++++++++++Engine initialization function begin+++++++++++++++++++++++++++
(function () {

    // 確定渲染類型
    function _determineRenderType(config) {
        var CONFIG_KEY = cc.game.CONFIG_KEY,
            userRenderMode = parseInt(config[CONFIG_KEY.renderMode]) || 0;

        // Adjust RenderType
        if (isNaN(userRenderMode) || userRenderMode > 2 || userRenderMode < 0)
            config[CONFIG_KEY.renderMode] = 0;

        // Determine RenderType
        cc._renderType = cc.game.RENDER_TYPE_CANVAS;
        cc._supportRender = false;

        if (userRenderMode === 0) {
            if (cc.sys.capabilities["opengl"]) {
                cc._renderType = cc.game.RENDER_TYPE_WEBGL;
                cc._supportRender = true;
            } else if (cc.sys.capabilities["canvas"]) {
                cc._renderType = cc.game.RENDER_TYPE_CANVAS;
                cc._supportRender = true;
            }
        } else if (userRenderMode === 1 && cc.sys.capabilities["canvas"]) {
            cc._renderType = cc.game.RENDER_TYPE_CANVAS;
            cc._supportRender = true;
        } else if (userRenderMode === 2 && cc.sys.capabilities["opengl"]) {
            cc._renderType = cc.game.RENDER_TYPE_WEBGL;
            cc._supportRender = true;
        }
    }

    // 初始化引擎函數 用於加載引擎代碼
    cc.initEngine = function (config, cb) {
        if (_engineInitCalled) {
            var previousCallback = _engineLoadedCallback;
            _engineLoadedCallback = function () {
                previousCallback && previousCallback();
                cb && cb();
            }
            return;
        }

        _engineLoadedCallback = cb;

        // Config uninitialized and given, initialize with it
        if (!cc.game.config && config) {
            cc.game.config = config;
        }
        // No config given and no config set before, load it
        else if (!cc.game.config) {
            cc.game._loadConfig();
        }
        config = cc.game.config;

        _determineRenderType(config);

        document.body ? _load(config) : cc._addEventListener(window, 'load', _windowLoaded, false);
        _engineInitCalled = true;
    };
})();
//+++++++++++++++++++++++++Engine initialization function end+++++++++++++++++++++++++++++

通過上面,我們瞭解到了調用cc.game.run之後,怎麼調用cc.game.onStart函數。
下一講,我們繼續分解一下,進入我們的邏輯後,界面是怎麼渲染的。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章