CocosCreator 源码./core/CCGame详解

/****************************************************************************

  

/* 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;

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值