[上一篇]講了調用了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函數。
下一講,我們繼續分解一下,進入我們的邏輯後,界面是怎麼渲染的。