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