cocos-enhance-kit/engine/cocos2d/core/CCGame.js
2022-06-25 00:26:34 +08:00

898 lines
32 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/****************************************************************************
Copyright (c) 2013-2016 Chukong Technologies Inc.
Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd.
https://www.cocos.com/
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated engine source code (the "Software"), a limited,
worldwide, royalty-free, non-assignable, revocable and non-exclusive license
to use Cocos Creator solely to develop games on your target platforms. You shall
not use Cocos Creator software for developing other software or tools that's
used for developing games. You are not granted to publish, distribute,
sublicense, and/or sell copies of Cocos Creator.
The software or tools in this License Agreement are licensed, not sold.
Xiamen Yaji Software Co., Ltd. reserves all rights not expressly granted to you.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
****************************************************************************/
var EventTarget = require('./event/event-target');
require('../audio/CCAudioEngine');
const debug = require('./CCDebug');
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,
/**
* WebGL API as renderer backend
* @property RENDER_TYPE_WEBGL
* @constant
* @type {Number}
*/
RENDER_TYPE_WEBGL: 1,
/**
* OpenGL API as renderer backend
* @property RENDER_TYPE_OPENGL
* @constant
* @type {Number}
*/
RENDER_TYPE_OPENGL: 2,
_persistRootNodes: {},
// states
_paused: true,//whether the game is paused
_configLoaded: false,//whether config loaded
_isCloning: false, // deserializing or instantiating
_prepared: false, //whether the engine has prepared
_rendererInitialized: false,
_renderContext: null,
_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. debugModedebug 模式,但是在浏览器中这个选项会被忽略) <br/>
* "debugMode" 各种设置选项的意义。 <br/>
* 0 - 没有消息被打印出来。 <br/>
* 1 - cc.errorcc.assertcc.warncc.log 将打印在 console 中。 <br/>
* 2 - cc.errorcc.assertcc.warn 将打印在 console 中。 <br/>
* 3 - cc.errorcc.assert 将打印在 console 中。 <br/>
* 4 - cc.errorcc.assertcc.warncc.log 将打印在 canvas 中(仅适用于 web 端)。 <br/>
* 5 - cc.errorcc.assertcc.warn 将打印在 canvas 中(仅适用于 web 端)。 <br/>
* 6 - cc.errorcc.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;
if (this._intervalId)
window.cancelAnimFrame(this._intervalId);
this._intervalId = 0;
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 () {
if (this._paused) return;
this._paused = true;
// Pause audio engine
if (cc.audioEngine) {
cc.audioEngine._break();
}
// Pause main loop
if (this._intervalId)
window.cancelAnimFrame(this._intervalId);
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 () {
if (!this._paused) return;
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
if (cc.audioEngine) {
cc.audioEngine.uncacheAll();
}
cc.director.reset();
game.pause();
cc.assetManager.builtins.init(() => {
game.onStart();
game.emit(game.EVENT_RESTART);
});
});
},
/**
* !#en End game, it will close the game window
* !#zh 退出游戏
* @method end
*/
end: function () {
close();
},
// @Game loading
_initEngine () {
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 SP 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) {
if (!cc.Node.isNode(node) || !node.uuid) {
cc.warnID(3800);
return;
}
var id = node.uuid;
if (!this._persistRootNodes[id]) {
var scene = cc.director._scene;
if (cc.isValid(scene)) {
if (!node.parent) {
node.parent = scene;
}
else if ( !(node.parent instanceof cc.Scene) ) {
cc.warnID(3801);
return;
}
else if (node.parent !== scene) {
cc.warnID(3802);
return;
}
}
this._persistRootNodes[id] = node;
node._persistNode = true;
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) {
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 () {
this._lastTime = performance.now();
var frameRate = game.config.frameRate;
this._frameTime = 1000 / frameRate;
cc.director._maxParticleDeltaTime = this._frameTime / 1000 * 2;
if (CC_JSB || CC_RUNTIME) {
jsb.setPreferredFramesPerSecond(frameRate);
window.requestAnimFrame = window.requestAnimationFrame;
window.cancelAnimFrame = window.cancelAnimationFrame;
}
else {
let rAF = window.requestAnimationFrame = window.requestAnimationFrame ||
window.webkitRequestAnimationFrame ||
window.mozRequestAnimationFrame ||
window.oRequestAnimationFrame ||
window.msRequestAnimationFrame;
if (frameRate !== 60 && frameRate !== 30) {
window.requestAnimFrame = rAF ? this._stTimeWithRAF : this._stTime;
window.cancelAnimFrame = this._ctTime;
}
else {
window.requestAnimFrame = rAF || 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;
}
}
},
_stTimeWithRAF: function(callback){
var currTime = performance.now();
var timeToCall = Math.max(0, game._frameTime - (currTime - game._lastTime));
var id = window.setTimeout(function() {
window.requestAnimationFrame(callback);
}, timeToCall);
game._lastTime = currTime + timeToCall;
return id;
},
_stTime: function(callback){
var currTime = performance.now();
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;
}
if (!this._prepared) return;
var self = this, callback, config = self.config,
director = cc.director,
skip = true, frameRate = config.frameRate;
debug.setDisplayStats(config.showFPS);
callback = function (now) {
if (!self._paused) {
self._intervalId = window.requestAnimFrame(callback);
if (!CC_JSB && !CC_RUNTIME && frameRate === 30) {
if (skip = !skip) {
return;
}
}
director.mainLoop(now);
}
};
self._intervalId = window.requestAnimFrame(callback);
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);
this.config = config;
this._configLoaded = true;
},
_determineRenderType () {
let config = this.config,
userRenderMode = parseInt(config.renderMode) || 0;
// Determine RenderType
this.renderType = this.RENDER_TYPE_CANVAS;
let supportRender = false;
if (userRenderMode === 0) {
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));
}
},
_initRenderer () {
// Avoid setup to be called twice.
if (this._rendererInitialized) return;
let el = this.config.id,
width, height,
localCanvas, localContainer;
if (CC_JSB || CC_RUNTIME) {
this.container = localContainer = document.createElement("DIV");
this.frame = localContainer.parentNode === document.body ? document.documentElement : localContainer.parentNode;
localCanvas = window.__canvas;
this.canvas = localCanvas;
}
else {
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
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") {
cc.warnID(3819);
}
width = element.clientWidth;
height = element.clientHeight;
this.canvas = localCanvas = document.createElement("CANVAS");
this.container = localContainer = document.createElement("DIV");
element.appendChild(localContainer);
}
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;
}
}
addClass(localCanvas, "gameCanvas");
localCanvas.setAttribute("width", width || 480);
localCanvas.setAttribute("height", height || 320);
localCanvas.setAttribute("tabindex", 99);
}
this._determineRenderType();
// WebGL context created successfully
if (this.renderType === this.RENDER_TYPE_WEBGL) {
var opts = {
'stencil': true,
// MSAA is causing serious performance dropdown on some browsers.
'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);
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;
game.emit(game.EVENT_SHOW, arg0, arg1, arg2, arg3, arg4);
}
}
if (hiddenPropName) {
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.call(game);
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;