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函数。
下一讲,我们继续分解一下,进入我们的逻辑后,界面是怎么渲染的。

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