/****************************************************************************
/* event-target 是一个事件类,事件对象 ,基于观察者模式的时间notify 分发*/
var EventTarget = require('./event/event-target');
/*CCAudioEngine 音效播放类,音效的播放 暂停 loop等 */
require('../audio/CCAudioEngine');
/* CCDebug 主要包括的 log warn error的封装 */
const debug = require('./CCDebug');
/* render/index 主要
提供基础渲染接口的渲染器对象,渲染层的基础接口将逐步开放给用户
包括drawcall 属性等
*/
const renderer = require('./renderer/index.js');
/* 图集管理,最大图集,碎图管理,最大尺寸等信息维护在这个类 */
const dynamicAtlasManager = require('../core/renderer/utils/dynamic-atlas/manager');
/**
* @module cc
*/
/**
* !#en An object to boot the game.
* !#zh 包含游戏主体信息并负责驱动游戏的游戏对象。
* @class Game
* @extends EventTarget
*/
var game = {
/**
* !#en Event triggered when game hide to background.
* Please note that this event is not 100% guaranteed to be fired on Web platform,
* on native platforms, it corresponds to enter background event, os status bar or notification center may not trigger this event.
* !#zh 游戏进入后台时触发的事件。
* 请注意,在 WEB 平台,这个事件不一定会 100% 触发,这完全取决于浏览器的回调行为。
* 在原生平台,它对应的是应用被切换到后台事件,下拉菜单和上拉状态栏等不一定会触发这个事件,这取决于系统行为。
* @property EVENT_HIDE
* @type {String}
* @example
* cc.game.on(cc.game.EVENT_HIDE, function () {
* cc.audioEngine.pauseMusic();
* cc.audioEngine.pauseAllEffects();
* });
*/
EVENT_HIDE: "game_on_hide",
/**
* !#en Event triggered when game back to foreground
* Please note that this event is not 100% guaranteed to be fired on Web platform,
* on native platforms, it corresponds to enter foreground event.
* !#zh 游戏进入前台运行时触发的事件。
* 请注意,在 WEB 平台,这个事件不一定会 100% 触发,这完全取决于浏览器的回调行为。
* 在原生平台,它对应的是应用被切换到前台事件。
* @property EVENT_SHOW
* @constant
* @type {String}
*/
EVENT_SHOW: "game_on_show",
/**
* !#en Event triggered when game restart
* !#zh 调用restart后,触发事件。
* @property EVENT_RESTART
* @constant
* @type {String}
*/
EVENT_RESTART: "game_on_restart",
/**
* Event triggered after game inited, at this point all engine objects and game scripts are loaded
* @property EVENT_GAME_INITED
* @constant
* @type {String}
*/
EVENT_GAME_INITED: "game_inited",
/**
* Event triggered after engine inited, at this point you will be able to use all engine classes.
* It was defined as EVENT_RENDERER_INITED in cocos creator v1.x and renamed in v2.0
* @property EVENT_ENGINE_INITED
* @constant
* @type {String}
*/
EVENT_ENGINE_INITED: "engine_inited",
// deprecated
EVENT_RENDERER_INITED: "engine_inited",
/**
* Web Canvas 2d API as renderer backend
* @property RENDER_TYPE_CANVAS
* @constant
* @type {Number}
*/
RENDER_TYPE_CANVAS: 0,/* 渲染类型为canvas */
/**
* WebGL API as renderer backend
* @property RENDER_TYPE_WEBGL
* @constant
* @type {Number}
*/
RENDER_TYPE_WEBGL: 1,/* 渲染类型为webgl */
/**
* OpenGL API as renderer backend
* @property RENDER_TYPE_OPENGL
* @constant
* @type {Number}
*/
RENDER_TYPE_OPENGL: 2,/* 渲染类型opengl */
_persistRootNodes: {},/* 根节点容器,存储预加载的node节点 */
// states
_paused: true,//whether the game is paused 游戏是否暂停了
/* 【配置是否已经加载完毕了,这个配置在build后的main.js里面,
包括渲染模式、debug模式,showFPS,帧率frameRate
groupList: settings.groupList, 分组
collisionMatrix: settings.collisionMatrix,
分组可进行碰撞的分组配对,在项目-项目设置里面的面板配置
】 */
_configLoaded: false,//whether config loaded
/* 反序列化或实例化 */
_isCloning: false, // deserializing or instantiating
/* 引擎已经初始化完毕,此时内置的class都能调用,引擎配置[]都已经载入 */
_prepared: false, //whether the engine has prepared
/* 渲染器已经初始化 */
_rendererInitialized: false,
/* 渲染器??? */
_renderContext: null,
/* 主循环的interval时间间隔 */
_intervalId: null,//interval target of main
_lastTime: null,
_frameTime: null,
/**
* !#en The outer frame of the game canvas, parent of game container.
* !#zh 游戏画布的外框,container 的父容器。
* @property frame
* @type {Object}
*/
frame: null,
/**
* !#en The container of game canvas.
* !#zh 游戏画布的容器。
* @property container
* @type {HTMLDivElement}
*/
container: null,
/**
* !#en The canvas of the game.
* !#zh 游戏的画布。
* @property canvas
* @type {HTMLCanvasElement}
*/
canvas: null,
/**
* !#en The renderer backend of the game.
* !#zh 游戏的渲染器类型。
* @property renderType
* @type {Number}
*/
renderType: -1,
/**
* !#en
* The current game configuration, including:<br/>
* 1. debugMode<br/>
* "debugMode" possible values :<br/>
* 0 - No message will be printed. <br/>
* 1 - cc.error, cc.assert, cc.warn, cc.log will print in console. <br/>
* 2 - cc.error, cc.assert, cc.warn will print in console. <br/>
* 3 - cc.error, cc.assert will print in console. <br/>
* 4 - cc.error, cc.assert, cc.warn, cc.log will print on canvas, available only on web.<br/>
* 5 - cc.error, cc.assert, cc.warn will print on canvas, available only on web. <br/>
* 6 - cc.error, cc.assert will print on canvas, available only on web. <br/>
* 2. showFPS<br/>
* Left bottom corner fps information will show when "showFPS" equals true, otherwise it will be hide.<br/>
* 3. exposeClassName<br/>
* Expose class name to chrome debug tools, the class intantiate performance is a little bit slower when exposed.<br/>
* 4. frameRate<br/>
* "frameRate" set the wanted frame rate for your game, but the real fps depends on your game implementation and the running environment.<br/>
* 5. id<br/>
* "gameCanvas" sets the id of your canvas element on the web page, it's useful only on web.<br/>
* 6. renderMode<br/>
* "renderMode" sets the renderer type, only useful on web :<br/>
* 0 - Automatically chosen by engine<br/>
* 1 - Forced to use canvas renderer<br/>
* 2 - Forced to use WebGL renderer, but this will be ignored on mobile browsers<br/>
*<br/>
* Please DO NOT modify this object directly, it won't have any effect.<br/>
* !#zh
* 当前的游戏配置,包括: <br/>
* 1. debugMode(debug 模式,但是在浏览器中这个选项会被忽略) <br/>
* "debugMode" 各种设置选项的意义。 <br/>
* 0 - 没有消息被打印出来。 <br/>
* 1 - cc.error,cc.assert,cc.warn,cc.log 将打印在 console 中。 <br/>
* 2 - cc.error,cc.assert,cc.warn 将打印在 console 中。 <br/>
* 3 - cc.error,cc.assert 将打印在 console 中。 <br/>
* 4 - cc.error,cc.assert,cc.warn,cc.log 将打印在 canvas 中(仅适用于 web 端)。 <br/>
* 5 - cc.error,cc.assert,cc.warn 将打印在 canvas 中(仅适用于 web 端)。 <br/>
* 6 - cc.error,cc.assert 将打印在 canvas 中(仅适用于 web 端)。 <br/>
* 2. showFPS(显示 FPS) <br/>
* 当 showFPS 为 true 的时候界面的左下角将显示 fps 的信息,否则被隐藏。 <br/>
* 3. exposeClassName <br/>
* 暴露类名让 Chrome DevTools 可以识别,如果开启会稍稍降低类的创建过程的性能,但对对象构造没有影响。 <br/>
* 4. frameRate (帧率) <br/>
* “frameRate” 设置想要的帧率你的游戏,但真正的FPS取决于你的游戏实现和运行环境。 <br/>
* 5. id <br/>
* "gameCanvas" Web 页面上的 Canvas Element ID,仅适用于 web 端。 <br/>
* 6. renderMode(渲染模式) <br/>
* “renderMode” 设置渲染器类型,仅适用于 web 端: <br/>
* 0 - 通过引擎自动选择。 <br/>
* 1 - 强制使用 canvas 渲染。
* 2 - 强制使用 WebGL 渲染,但是在部分 Android 浏览器中这个选项会被忽略。 <br/>
* <br/>
* 注意:请不要直接修改这个对象,它不会有任何效果。
* @property config
* @type {Object}
*/
config: null,
/**
* !#en Callback when the scripts of engine have been load.
* !#zh 当引擎完成启动后的回调函数。
* @method onStart
* @type {Function}
*/
onStart: null,
//@Public Methods
// @Game play control
/**
* !#en Set frame rate of game.
* !#zh 设置游戏帧率。
* @method setFrameRate
* @param {Number} frameRate
*/
setFrameRate: function (frameRate) {
var config = this.config;
config.frameRate = frameRate;
/*
window.cancelAnimFrame(this._intervalId);
取消一个先前通过调用window.requestAnimationFrame()方法添加到计划中的动画帧请求。
*/
if (this._intervalId)
window.cancelAnimFrame(this._intervalId);
this._intervalId = 0;
/* 设置帧率,设置flag,pause暂停为true */
this._paused = true;
this._setAnimFrame();
this._runMainLoop();
},
/**
* !#en Get frame rate set for the game, it doesn't represent the real frame rate.
* !#zh 获取设置的游戏帧率(不等同于实际帧率)。
* @method getFrameRate
* @return {Number} frame rate
*/
getFrameRate: function () {
return this.config.frameRate;
},
/**
* !#en Run the game frame by frame.
* !#zh 执行一帧游戏循环。
* @method step
*/
step: function () {
cc.director.mainLoop();
},
/**
* !#en Pause the game main loop. This will pause:
* game logic execution, rendering process, event manager, background music and all audio effects.
* This is different with cc.director.pause which only pause the game logic execution.
* !#zh 暂停游戏主循环。包含:游戏逻辑,渲染,事件处理,背景音乐和所有音效。这点和只暂停游戏逻辑的 cc.director.pause 不同。
* @method pause
*/
pause: function () {
/* 如果已经pause,返回 */
if (this._paused) return;
/* 执行pause,暂停 */
this._paused = true;
// Pause audio engine
/* 暂停音频 */
if (cc.audioEngine) {
cc.audioEngine._break();
}
// Pause main loop
/* 暂停主循环 */
if (this._intervalId)
window.cancelAnimFrame(this._intervalId);
/* 重置indtervalId =0 */
this._intervalId = 0;
},
/**
* !#en Resume the game from pause. This will resume:
* game logic execution, rendering process, event manager, background music and all audio effects.
* !#zh 恢复游戏主循环。包含:游戏逻辑,渲染,事件处理,背景音乐和所有音效。
* @method resume
*/
resume: function () {
/* 已经暂停,则return */
if (!this._paused) return;
/* 执行resume,恢复 */
this._paused = false;
// Resume audio engine
/* 恢复音频 */
if (cc.audioEngine) {
cc.audioEngine._restore();
}
cc.director._resetDeltaTime();
// Resume main loop
/* 恢复主循环 */
this._runMainLoop();
},
/**
* !#en Check whether the game is paused.
* !#zh 判断游戏是否暂停。
* @method isPaused
* @return {Boolean}
*/
isPaused: function () {
return this._paused;
},
/**
* !#en Restart game.
* !#zh 重新开始游戏
* @method restart
*/
restart: function () {
/* 渲染过程之后所触发的事件。 */
cc.director.once(cc.Director.EVENT_AFTER_DRAW, function () {
/* 移除常驻节点 */
for (var id in game._persistRootNodes) {
game.removePersistRootNode(game._persistRootNodes[id]);
}
// Clear scene
/* 清除场景 */
cc.director.getScene().destroy();
cc.Object._deferredDestroy();
// Clean up audio
/* 销毁所有audio */
if (cc.audioEngine) {
cc.audioEngine.uncacheAll();
}
/* 导演重置 */
cc.director.reset();
/* 游戏暂停 */
game.pause();
/* 内置资源init后回调 */
cc.assetManager.builtins.init(() => {
/* 游戏开始 */
game.onStart();
/* notify通知 game重启 */
game.emit(game.EVENT_RESTART);
});
});
},
/**
* !#en End game, it will close the game window
* !#zh 退出游戏
* @method end
*/
end: function () {
/* 退出游戏 */
close();
},
// @Game loading
/* 初始化引擎 */
_initEngine() {
/* render加入已经初始化 则不再重复初始化 */
if (this._rendererInitialized) {
return;
}
/* 初始化 容器 画布等 */
this._initRenderer();
if (!CC_EDITOR) {
this._initEvents();
}
this.emit(this.EVENT_ENGINE_INITED);
},
_loadPreviewScript(cb) {
if (CC_PREVIEW && window.__quick_compile_project__) {
window.__quick_compile_project__.load(cb);
}
else {
cb();
}
},
_prepareFinished(cb) {
// Init engine
this._initEngine();
this._setAnimFrame();
/* 引擎内置资源初始化完毕后回调 */
cc.assetManager.builtins.init(() => {
// Log engine version
console.log('Cocos Creator v' + cc.ENGINE_VERSION);
this._prepared = true;
this._runMainLoop();
/* 播放广播事件,触发游戏初始化 */
this.emit(this.EVENT_GAME_INITED);
if (cb) cb();
});
},
eventTargetOn: EventTarget.prototype.on,
eventTargetOnce: EventTarget.prototype.once,
/**
* !#en
* Register an callback of a specific event type on the game object.
* This type of event should be triggered via `emit`.
* !#zh
* 注册 game 的特定事件类型回调。这种类型的事件应该被 `emit` 触发。
*
* @method on
* @param {String} type - A string representing the event type to listen for.
* @param {Function} callback - The callback that will be invoked when the event is dispatched.
* The callback is ignored if it is a duplicate (the callbacks are unique).
* @param {any} [callback.arg1] arg1
* @param {any} [callback.arg2] arg2
* @param {any} [callback.arg3] arg3
* @param {any} [callback.arg4] arg4
* @param {any} [callback.arg5] arg5
* @param {Object} [target] - The target (this object) to invoke the callback, can be null
* @return {Function} - Just returns the incoming callback so you can save the anonymous function easier.
* @typescript
* on<T extends Function>(type: string, callback: T, target?: any, useCapture?: boolean): T
*/
on(type, callback, target, once) {
// Make sure EVENT_ENGINE_INITED and EVENT_GAME_INITED callbacks to be invoked
if ((this._prepared && type === this.EVENT_ENGINE_INITED) ||
(!this._paused && type === this.EVENT_GAME_INITED)) {
callback.call(target);
}
else {
this.eventTargetOn(type, callback, target, once);
}
},
/**
* !#en
* Register an callback of a specific event type on the game object,
* the callback will remove itself after the first time it is triggered.
* !#zh
* 注册 game 的特定事件类型回调,回调会在第一时间被触发后删除自身。
*
* @method once
* @param {String} type - A string representing the event type to listen for.
* @param {Function} callback - The callback that will be invoked when the event is dispatched.
* The callback is ignored if it is a duplicate (the callbacks are unique).
* @param {any} [callback.arg1] arg1
* @param {any} [callback.arg2] arg2
* @param {any} [callback.arg3] arg3
* @param {any} [callback.arg4] arg4
* @param {any} [callback.arg5] arg5
* @param {Object} [target] - The target (this object) to invoke the callback, can be null
*/
once(type, callback, target) {
// Make sure EVENT_ENGINE_INITED and EVENT_GAME_INITED callbacks to be invoked
if ((this._prepared && type === this.EVENT_ENGINE_INITED) ||
(!this._paused && type === this.EVENT_GAME_INITED)) {
callback.call(target);
}
else {
this.eventTargetOnce(type, callback, target);
}
},
/**
* !#en Prepare game.
* !#zh 准备引擎,请不要直接调用这个函数。
* @param {Function} cb
* @method prepare
*/
prepare(cb) {
// Already prepared
if (this._prepared) {
if (cb) cb();
return;
}
this._loadPreviewScript(() => {
this._prepareFinished(cb);
});
},
/**
* !#en Run game with configuration object and onStart function.
* !#zh 运行游戏,并且指定引擎配置和 onStart 的回调。
* @method run
* @param {Object} config - Pass configuration object or onStart function
* @param {Function} onStart - function to be executed after game initialized
*/
run: function (config, onStart) {
this._initConfig(config);
this.onStart = onStart;
this.prepare(game.onStart && game.onStart.bind(game));
},
// @ Persist root node section
/**
* !#en
* Add a persistent root node to the game, the persistent node won't be destroyed during scene transition.<br/>
* The target node must be placed in the root level of hierarchy, otherwise this API won't have any effect.
* !#zh
* 声明常驻根节点,该节点不会被在场景切换中被销毁。<br/>
* 目标节点必须位于为层级的根节点,否则无效。
* @method addPersistRootNode
* @param {Node} node - The node to be made persistent
*/
addPersistRootNode: function (node) {
/* 不是Node类型或者uuid位空 提示warn */
if (!cc.Node.isNode(node) || !node.uuid) {
/* The target can not be made persist because it's not a cc.Node or it doesn't have _id property. */
cc.warnID(3800);
return;
}
var id = node.uuid;
/* 不在容器里面 */
if (!this._persistRootNodes[id]) {
/* 主场景 */
var scene = cc.director._scene;
/* scene 是有效的 */
if (cc.isValid(scene)) {
/* node 没有父节点 ,设置node的父节点为scene,add到scene上 */
if (!node.parent) {
node.parent = scene;
}/* 假如node的父节点类型不是scene类型提示 */
else if (!(node.parent instanceof cc.Scene)) {
/* The node can not be made persist because it's not under root node.
常驻节点的父节点 一定是scene,假如不是会提示 */
cc.warnID(3801);
return;
}/* 假如当前node节点的父节点,不是当前场景 提示 这个预加载节点,没有在当前场景下
The node can not be made persist because it's not in current scene. */
else if (node.parent !== scene) {
cc.warnID(3802);
return;
}
}
/* 添加node到容器 */
this._persistRootNodes[id] = node;
/* 设置node为常驻节点 flag为true */
node._persistNode = true;
/* _releaseManager 是一个对象,管理释放asset; */
cc.assetManager._releaseManager._addPersistNodeRef(node);
}
},
/**
* !#en Remove a persistent root node.
* !#zh 取消常驻根节点。
* @method removePersistRootNode
* @param {Node} node - The node to be removed from persistent node list
*/
removePersistRootNode: function (node) {
/* 移除某个具体的常驻节点 ,node节点的uuid获取,*/
var id = node.uuid || '';
if (node === this._persistRootNodes[id]) {
delete this._persistRootNodes[id];
node._persistNode = false;
cc.assetManager._releaseManager._removePersistNodeRef(node);
}
},
/**
* !#en Check whether the node is a persistent root node.
* !#zh 检查节点是否是常驻根节点。
* @method isPersistRootNode
* @param {Node} node - The node to be checked
* @return {Boolean}
*/
isPersistRootNode: function (node) {
return node._persistNode;
},
//@Private Methods
// @Time ticker section
_setAnimFrame: function () {
/* 获取当前时间-ms
Performance.now()和Date.now()之间最大的区别是,
Date.now()返回一个与Unix时间有关的时间戳
(从00:00:00 UTC,1970年1月1日开始的时间)。
这就是我们可能面临的一个问题。
JavaScript如何知道从这个日期开始已经过了多少时间?它从系统时钟中获取。
但是,由于系统时钟存在于我们的机器中,它可以手动或通过程序进行调整,因此,
时间精度无法保证。
*/
this._lastTime = performance.now();
/* 帧率 */
var frameRate = game.config.frameRate;
/* 1000毫秒 除以帧率【每秒多少帧】,就是每一帧需要的时间 */
this._frameTime = 1000 / frameRate;
/*_maxParticleDeltaTime 粒子系统最大步长增量时间 */
cc.director._maxParticleDeltaTime = this._frameTime / 1000 * 2;
/* 通过 CC_JSB 来判断是否为 native 环境(模拟器)。
基于jsb的说明
https://docs.cocos.com/creator/2.4/manual/zh/advanced-topics/JSB2.0-learning.html
*/
if (CC_JSB || CC_RUNTIME) {
jsb.setPreferredFramesPerSecond(frameRate);
window.requestAnimFrame = window.requestAnimationFrame;
window.cancelAnimFrame = window.cancelAnimationFrame;
}
else {
/* 当帧率不等于60 并且不等于30的时候,重写requestAnimFrame
和 cancelAnimFrame 方法 */
/*
window.requestAnimationFrame() 告诉浏览器——你希望执行一个动画,
并且要求浏览器在下次重绘之前调用指定的回调函数更新动画。
该方法需要传入一个回调函数作为参数,该回调函数会在浏览器下一次重绘之前执行。
*/
if (frameRate !== 60 && frameRate !== 30) {
window.requestAnimFrame = this._stTime;
window.cancelAnimFrame = this._ctTime;
}
else {
/* 对requestAnimFrame函数做了兼容性封装,用于兼容不同的浏览器环境, */
window.requestAnimFrame = window.requestAnimationFrame ||
window.webkitRequestAnimationFrame ||
window.mozRequestAnimationFrame ||
window.oRequestAnimationFrame ||
window.msRequestAnimationFrame ||
this._stTime;
window.cancelAnimFrame = window.cancelAnimationFrame ||
window.cancelRequestAnimationFrame ||
window.msCancelRequestAnimationFrame ||
window.mozCancelRequestAnimationFrame ||
window.oCancelRequestAnimationFrame ||
window.webkitCancelRequestAnimationFrame ||
window.msCancelAnimationFrame ||
window.mozCancelAnimationFrame ||
window.webkitCancelAnimationFrame ||
window.oCancelAnimationFrame ||
this._ctTime;
}
}
},
_stTime: function (callback) {
/* 当前时间 */
var currTime = performance.now();
/* game._frameTime 每一帧的时间 */
var timeToCall = Math.max(0, game._frameTime - (currTime - game._lastTime));
var id = window.setTimeout(function () { callback(); },
timeToCall);
game._lastTime = currTime + timeToCall;
return id;
},
/* 清除定时器 */
_ctTime: function (id) {
window.clearTimeout(id);
},
//Run game.
/* 游戏主循环 */
_runMainLoop: function () {
/* 如果是编辑器 则不执行 */
if (CC_EDITOR) {
return;
}
/* 引擎没有准备好,则return */
if (!this._prepared) return;
/* 声明多个变量
self = this
callback 为undefined
config为 配置
director导演器
frameRate 帧率 = config.frameRate;
*/
var self = this, callback, config = self.config,
director = cc.director,
skip = true, frameRate = config.frameRate;
/* 设置显示fps与否 */
debug.setDisplayStats(config.showFPS);
/* 对callback进行赋值,// 设置帧回调 */
callback = function (now) {
if (!self._paused) {
self._intervalId = window.requestAnimFrame(callback);
if (!CC_JSB && !CC_RUNTIME && frameRate === 30) {
/* 30帧的时候,在web浏览器 */
/* 赋值,skip = !skip,用于判断是否继续循环 */
if (skip = !skip) {
return;
}
}
director.mainLoop(now);
}
};
// 将在下一帧开始循环回调
self._intervalId = window.requestAnimFrame(callback);
/* 暂停设置为false */
self._paused = false;
},
// @Game loading section
_initConfig(config) {
// Configs adjustment
if (typeof config.debugMode !== 'number') {
config.debugMode = 0;
}
config.exposeClassName = !!config.exposeClassName;
if (typeof config.frameRate !== 'number') {
config.frameRate = 60;
}
let renderMode = config.renderMode;
if (typeof renderMode !== 'number' || renderMode > 2 || renderMode < 0) {
config.renderMode = 0;
}
if (typeof config.registerSystemEvent !== 'boolean') {
config.registerSystemEvent = true;
}
if (renderMode === 1) {
config.showFPS = false;
}
else {
config.showFPS = !!config.showFPS;
}
// Collide Map and Group List
this.collisionMatrix = config.collisionMatrix || [];
this.groupList = config.groupList || [];
/* 重置调试模式 */
debug._resetDebugSetting(config.debugMode);
/* 给全局变量config 赋值 */
this.config = config;
/* 配置加载完毕,设置为true */
this._configLoaded = true;
},
_determineRenderType() {
/* 声明变量config,不直接用,是可以减少作用域链查找的消耗 */
let config = this.config,
userRenderMode = parseInt(config.renderMode) || 0;
// Determine RenderType
this.renderType = this.RENDER_TYPE_CANVAS;
let supportRender = false;
if (userRenderMode === 0) {
/* 设置渲染类型,默认为webgl */
if (cc.sys.capabilities['opengl']) {
this.renderType = this.RENDER_TYPE_WEBGL;
supportRender = true;
}
else if (cc.sys.capabilities['canvas']) {
this.renderType = this.RENDER_TYPE_CANVAS;
supportRender = true;
}
}
else if (userRenderMode === 1 && cc.sys.capabilities['canvas']) {
this.renderType = this.RENDER_TYPE_CANVAS;
supportRender = true;
}
else if (userRenderMode === 2 && cc.sys.capabilities['opengl']) {
this.renderType = this.RENDER_TYPE_WEBGL;
supportRender = true;
}
/* 不支持渲染模式 抛出异常 */
if (!supportRender) {
throw new Error(debug.getError(3820, userRenderMode));
}
},
/* 初始化渲染相关-画布,div等元素 */
_initRenderer() {
// Avoid setup to be called twice.
/* 假如已经初始化,则返回 */
if (this._rendererInitialized) return;
/* 声明几个变量 id-画布对应的cavas的id; eg:id: 'GameCanvas',
宽高 */
let el = this.config.id,
width, height,
localCanvas, localContainer;
/* 假如是模拟器native环境 */
if (CC_JSB || CC_RUNTIME) {
/* 创建html-node节点 名称为DIV */
this.container = localContainer = document.createElement("DIV");
this.frame = localContainer.parentNode === document.body ? document.documentElement : localContainer.parentNode;
localCanvas = window.__canvas;
this.canvas = localCanvas;
}
else {
/* 查找名称为GameCanvas 的元素 */
var element = (el instanceof HTMLElement) ? el : (document.querySelector(el) || document.querySelector('#' + el));
if (element.tagName === "CANVAS") {
width = element.width;
height = element.height;
//it is already a canvas, we wrap it around with a div
/* 它已经是一个画布,我们用一个 div 将它包裹起来 */
this.canvas = localCanvas = element;
this.container = localContainer = document.createElement("DIV");
if (localCanvas.parentNode)
localCanvas.parentNode.insertBefore(localContainer, localCanvas);
} else {
//we must make a new canvas and place into this element
/* //我们必须制作一个新画布并将其放入此元素中 */
if (element.tagName !== "DIV") {
/* Warning: target element is not a DIV or CANVAS */
cc.warnID(3819);
}
width = element.clientWidth;
height = element.clientHeight;
this.canvas = localCanvas = document.createElement("CANVAS");
this.container = localContainer = document.createElement("DIV");
element.appendChild(localContainer);
}
/* 设置html标签的id名称为Cocos2dGameContainer */
localContainer.setAttribute('id', 'Cocos2dGameContainer');
localContainer.appendChild(localCanvas);
this.frame = (localContainer.parentNode === document.body) ? document.documentElement : localContainer.parentNode;
function addClass(element, name) {
var hasClass = (' ' + element.className + ' ').indexOf(' ' + name + ' ') > -1;
if (!hasClass) {
if (element.className) {
element.className += " ";
}
element.className += name;
}
}
/* Web 页面上的 Canvas Element ID,仅适用于 web 端。 */
addClass(localCanvas, "gameCanvas");
localCanvas.setAttribute("width", width || 480);
localCanvas.setAttribute("height", height || 320);
localCanvas.setAttribute("tabindex", 99);
}
// 获取可用的渲染模式
this._determineRenderType();
// WebGL context created successfully
/* WebGL 上下文创建成功 */
if (this.renderType === this.RENDER_TYPE_WEBGL) {
var opts = {
'stencil': true,
// MSAA is causing serious performance dropdown on some browsers.
/* MSAA 导致某些浏览器的性能严重下降。 */
'antialias': cc.macro.ENABLE_WEBGL_ANTIALIAS,
'alpha': cc.macro.ENABLE_TRANSPARENT_CANVAS
};
renderer.initWebGL(localCanvas, opts);
this._renderContext = renderer.device._gl;
// Enable dynamic atlas manager by default
/* 默认启用动态图集管理器 */
if (!cc.macro.CLEANUP_IMAGE_CACHE && dynamicAtlasManager) {
dynamicAtlasManager.enabled = true;
}
}
if (!this._renderContext) {
this.renderType = this.RENDER_TYPE_CANVAS;
// Could be ignored by module settings
/* // 可以被模块设置忽略 */
renderer.initCanvas(localCanvas);
this._renderContext = renderer.device._ctx;
}
this.canvas.oncontextmenu = function () {
if (!cc._isContextMenuEnable) return false;
};
this._rendererInitialized = true;
},
_initEvents: function () {
var win = window, hiddenPropName;
// register system events
if (this.config.registerSystemEvent)
cc.internal.inputManager.registerSystemEvent(this.canvas);
/* Document.hidden (只读属性)返回布尔值,表示页面是(true)否(false)隐藏。
表示页面是否处于隐藏状态。页面隐藏包括页面在后台标签页或者浏览器最小化。
*/
/* // 兼容不同浏览器 */
if (typeof document.hidden !== 'undefined') {
hiddenPropName = "hidden";
} else if (typeof document.mozHidden !== 'undefined') {
hiddenPropName = "mozHidden";
} else if (typeof document.msHidden !== 'undefined') {
hiddenPropName = "msHidden";
} else if (typeof document.webkitHidden !== 'undefined') {
hiddenPropName = "webkitHidden";
}
var hidden = false;
/* 页面隐藏的监听 */
function onHidden() {
if (!hidden) {
hidden = true;
game.emit(game.EVENT_HIDE);
}
}
// In order to adapt the most of platforms the onshow API.
/* 页面显示的监听 */
function onShown(arg0, arg1, arg2, arg3, arg4) {
if (hidden) {
/* 设置hidden为false */
hidden = false;
game.emit(game.EVENT_SHOW, arg0, arg1, arg2, arg3, arg4);
}
}
if (hiddenPropName) {
/* 监控view变化 */
var changeList = [
"visibilitychange",
"mozvisibilitychange",
"msvisibilitychange",
"webkitvisibilitychange",
"qbrowserVisibilityChange"
];
for (var i = 0; i < changeList.length; i++) {
document.addEventListener(changeList[i], function (event) {
var visible = document[hiddenPropName];
// QQ App
visible = visible || event["hidden"];
if (visible)
onHidden();
else
onShown();
});
}
} else {
/* 添加监听,页面隐藏 和 页面显示 */
win.addEventListener("blur", onHidden);
win.addEventListener("focus", onShown);
}
if (navigator.userAgent.indexOf("MicroMessenger") > -1) {
win.onfocus = onShown;
}
if ("onpageshow" in window && "onpagehide" in window) {
win.addEventListener("pagehide", onHidden);
win.addEventListener("pageshow", onShown);
// Taobao UIWebKit
document.addEventListener("pagehide", onHidden);
document.addEventListener("pageshow", onShown);
}
this.on(game.EVENT_HIDE, function () {
/* 页面隐藏 游戏暂停 */
game.pause();
});
this.on(game.EVENT_SHOW, function () {
/* 页面显示 游戏恢复 */
game.resume();
});
}
};
/* EventTarget 是一个function, */
EventTarget.call(game);
/* 把EventTarget的属性赋值到 game 上; 核心是添加 on off 等事件注册 */
cc.js.addon(game, EventTarget.prototype);
/**
* @module cc
*/
/**
* !#en This is a Game instance.
* !#zh 这是一个 Game 类的实例,包含游戏主体信息并负责驱动游戏的游戏对象。。
* @property game
* @type Game
*/
cc.game = module.exports = game;