赞
踩
许久没写博客了,这次借着读源码来写几篇关于源码的解析吧。加上最近用cocos比较多,底层看的比较少,准备开始对全部的源码来一次深刻的解读,此篇文章也是方便我自己以后查看。源码已经添加了对应方法的解释,在此基础上加入我个人的解释。
PS: 本人是web方向的,所以基本cocos用的都是html场景,原生部分就没能力解析了,其他部分解析后续会更新
首先我们先从CCGame.js开始解读,因为场景的创建初始化都由cc.game开始,具体可以看打包html场景后的main.js。
本人不善于写作,有错误可以私信我,欢迎大家提出意见和批评,新手轻喷。
ps:之前读的老版本在github已经没有了,只能更新成新的版本,看了一下,大体类似,V2.4.5
github地址:https://github.com/cocos-creator/engine/blob/v2.4.5/cocos2d/core/CCGame.js
// cc.EventTarget 对象类引入 var EventTarget = require('./event/event-target'); // cc.audioEngine 对象(提供音频播放暂停等接口)引入 require('../audio/CCAudioEngine'); // cc.debug 对象(提供游戏信息如fps等)导入 const debug = require('./CCDebug'); // cc.renderer 对象(提供渲染层接口等)导入 const renderer = require('./renderer/index.js'); // 如果是qq玩一玩环境则导入专门的 输入管理类 否则导入通用的 输入管理类 const inputManager = CC_QQPLAY ? require('./platform/BKInputManager') : require('./platform/CCInputManager'); // cc.dynamicAtlasManager 对象(动态图集管理)导入 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(); * }); */ // 这个注释都比较清晰,平时多用于 原生web-view里音频播放 因为应用切换切入切出导致音频持续播放等业务问题 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} */ // 在cc.game.run(config, onStart)的onStart之前触发,引擎部分和业务脚本已全部加载,还未加载完成sence // 防止cc.game.on监听不到该事件,如果 _paused 为 false,将直接调用on的回调 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} */ // 在cc.game.run(config, onStart)的onStart之前触发,引擎部分已全部加载,业务脚本还未加载 // 防止cc.game.on监听不到该事件,如果 _prepared 为 true,将直接调用on的回调 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, // 是否已经加载完引擎和业务脚本初始化 // 渲染上下文(webgl或者2d) _renderContext: null, // 主循环目标(用于暂停或重启停止动画帧循环) _intervalId: null,//interval target of main // 上一次渲染时间(用于帧数设置不为30帧或60帧时使用) _lastTime: null, // 每一帧间隔时间,帧数除于1000得到(用于帧数设置不为30帧或60帧使用) _frameTime: null, // Scenes list // 场景列表信息(无则空列表) _sceneInfos: [], /** * !#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/> * 7. scenes<br/> * "scenes" include available scenes in the current bundle.<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/> * 7. scenes <br/> * “scenes” 当前包中可用场景。 <br/> * <br/> * 注意:请不要直接修改这个对象,它不会有任何效果。 * @property config * @type {Object} */ config: null, /** * !#en Callback when the scripts of engine have been load. * !#zh 当引擎完成启动后的回调函数。 * @method onStart * @type {Function} */ // restart也会调用 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 animation cc.director.stopAnimation(); // 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) { this._prepared = true; // 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) { // 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); } }, /** * !#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) { // 确保是节点且存在uuid if (!cc.Node.isNode(node) || !node.uuid) { cc.warnID(3800); return; } var id = node.uuid; // 防止出现同个uuid的节点(一般不会) if (!this._persistRootNodes[id]) { var scene = cc.director._scene; // 是否存在场景 if (cc.isValid(scene)) { if (!node.parent) { node.parent = scene; } // 节点的parent不属于cc.Scene类 else if ( !(node.parent instanceof cc.Scene) ) { cc.warnID(3801); return; } // 节点的parent不等于当前节点 else if (node.parent !== scene) { cc.warnID(3802); return; } } this._persistRootNodes[id] = node; // 节点的是否常驻节点属性 node._persistNode = true; // 通过资源管理添加常驻节点资源(具体以后详细讲) cc.assetManager.finalizer._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.finalizer._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; // 2倍的帧数存储 cc.director._maxParticleDeltaTime = this._frameTime / 1000 * 2; // 是否原生环境(这个不是很清楚) if (CC_JSB || CC_RUNTIME) { jsb.setPreferredFramesPerSecond(frameRate); window.requestAnimFrame = window.requestAnimationFrame; window.cancelAnimFrame = window.cancelAnimationFrame; } else { // 如用户设置的帧数不为60或30 则使用自定义的循环函数 if (frameRate !== 60 && frameRate !== 30) { window.requestAnimFrame = this._stTime; window.cancelAnimFrame = this._ctTime; } // 使用动画帧循环事件 else { 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(); // 获取当前至下一帧时间 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; // 设置在左下角是否显示fps信息 debug.setDisplayStats(config.showFPS); callback = function (now) { // 非暂停状态 if (!self._paused) { self._intervalId = window.requestAnimFrame(callback); // 不为原生(这里不明确)且 用户帧数设置为30 if (!CC_JSB && !CC_RUNTIME && frameRate === 30) { // 每2帧跳1帧执行 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设置 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) { // 是否可使用webgl功能 if (cc.sys.capabilities['opengl']) { this.renderType = this.RENDER_TYPE_WEBGL; supportRender = true; } // 是否可使用canvas else if (cc.sys.capabilities['canvas']) { this.renderType = this.RENDER_TYPE_CANVAS; supportRender = true; } } // 是否可使用canvas else if (userRenderMode === 1 && cc.sys.capabilities['canvas']) { this.renderType = this.RENDER_TYPE_CANVAS; supportRender = true; } // 是否可使用webgl功能 else if (userRenderMode === 2 && cc.sys.capabilities['opengl']) { this.renderType = this.RENDER_TYPE_WEBGL; supportRender = true; } // 都使用不了canvas和webgl功能,无法使用引擎 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)); // 已经存在canvas,只需添加一层容器包裹 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); } // 不存在指定canvas,添加一个新的canvas else { //we must make a new canvas and place into this element // 配置提前写好的的id必须是个div 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 = { // 使用 8 位模板缓冲区 'stencil': true, // MSAA is causing serious performance dropdown on some browsers. // 在创建 WebGL Context 时是否开启抗锯齿,如需要修改则在cc.game.run之前赋值为true,会影响性能 // 少部分使用软件级别抗锯齿算法的设备或浏览器上,这个选项会对性能产生比较大的影响 'antialias': cc.macro.ENABLE_WEBGL_ANTIALIAS, // 是否开启canvas背景 alpha通道,如需要背景透明则在cc.game.run之前赋值为true,仅web环境,会影响性能 'alpha': cc.macro.ENABLE_TRANSPARENT_CANVAS }; // 是否微信小游戏或者qq玩一玩环境,则开启在绘图完成后保留绘图缓冲区 if (CC_WECHATGAME || CC_QQPLAY) { opts['preserveDrawingBuffer'] = true; } // 注册webgl renderer.initWebGL(localCanvas, opts); this._renderContext = renderer.device._gl; // Enable dynamic atlas manager by default // CLEANUP_IMAGE_CACHE // 是否在将贴图上传至 GPU 之后删除原始图片缓存,删除之后图片将无法进行 动态合图。 // 在 Web 平台,你通常不需要开启这个选项,因为在 Web 平台 Image 对象所占用的内存很小。 // 但是在微信小游戏平台的当前版本,Image 对象会缓存解码后的图片数据,它所占用的内存空间很大。 // 所以我们在微信平台默认开启了这个选项,这样我们就可以在上传 GL 贴图之后立即释放 Image 对象的内存,避免过高的内存占用 if (!cc.macro.CLEANUP_IMAGE_CACHE && dynamicAtlasManager) { dynamicAtlasManager.enabled = true; } } // 如果不存在渲染上下文,则默认使用canvas模式 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 // 注册pc端事件 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 (CC_WECHATGAME && cc.sys.browserType !== cc.sys.BROWSER_TYPE_WECHAT_GAME_SUB) { wx.onShow && wx.onShow(onShown); wx.onHide && wx.onHide(onHidden); } // 设置其他兼容 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); // 将EventTarget的原型链复制到game里 cc.js.addon(game, EventTarget.prototype); /** * @module cc */ /** 1. !#en This is a Game instance. 2. !#zh 这是一个 Game 类的实例,包含游戏主体信息并负责驱动游戏的游戏对象。。 3. @property game 4. @type Game */ cc.game = module.exports = game;
一:
我们来整理一下整个文件看完能得出的生命周期:
至此我们已经了解了从引擎文件加载后调用window.boot后,大概是怎样的流程。至于加载cocos-2d引擎时具体执行的顺序,还需要进一步的解读其他的代码。
二:
我们了解了渲染模式的生成,底层是用的什么方法实现帧循环的。还了解了游戏的开始,暂停,和重新开始等功能,此处的功能都包括游戏逻辑和音频、动画等部分。
这些有助于我们接下来更加深入的了解cocos的底层架构。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。