Cocos creator 源码解析(二)

序言

上一篇写了关于CCGame.js的解读,当中用到了需要cc.director的函数,想必cc.director在引擎中也是举足轻重的,那么这一篇我们就来看看CCDirector.js的源码吧。
话不多说,地址附上。

https://github.com/cocos-creator/engine/blob/v2.4.5/cocos2d/core/CCDirector.js

源码解析

// cc.EventTarget对象引入
const EventTarget = require('./event/event-target');
// 组件管理类引入
const ComponentScheduler = require('./component-scheduler');
// 节点激活/反激活管理类
const NodeActivator = require('./node-activator');
// cc.obj
const Obj = require('./platform/CCObject');
// cc.game对象引入
const game = require('./CCGame');
// cc.renderer 对象(提供渲染层接口等)导入
const renderer = require('./renderer');
// 事件管理器
const eventManager = require('./event-manager');
// 负责触发回调函数,定时器的类
const Scheduler = require('./CCScheduler');

//----------------------------------------------------------------------------------------------------------------------

/**
 * !#en
 * <p>
 *    ATTENTION: USE cc.director INSTEAD OF cc.Director.<br/>
 *    cc.director is a singleton object which manage your game's logic flow.<br/>
 *    Since the cc.director is a singleton, you don't need to call any constructor or create functions,<br/>
 *    the standard way to use it is by calling:<br/>
 *      - cc.director.methodName(); <br/>
 *
 *    It creates and handle the main Window and manages how and when to execute the Scenes.<br/>
 *    <br/>
 *    The cc.director is also responsible for:<br/>
 *      - initializing the OpenGL context<br/>
 *      - setting the OpenGL pixel format (default on is RGB565)<br/>
 *      - setting the OpenGL buffer depth (default on is 0-bit)<br/>
 *      - setting the color for clear screen (default one is BLACK)<br/>
 *      - setting the projection (default one is 3D)<br/>
 *      - setting the orientation (default one is Portrait)<br/>
 *      <br/>
 *    <br/>
 *    The cc.director also sets the default OpenGL context:<br/>
 *      - GL_TEXTURE_2D is enabled<br/>
 *      - GL_VERTEX_ARRAY is enabled<br/>
 *      - GL_COLOR_ARRAY is enabled<br/>
 *      - GL_TEXTURE_COORD_ARRAY is enabled<br/>
 * </p>
 * <p>
 *   cc.director also synchronizes timers with the refresh rate of the display.<br/>
 *   Features and Limitations:<br/>
 *      - Scheduled timers & drawing are synchronizes with the refresh rate of the display<br/>
 *      - Only supports animation intervals of 1/60 1/30 & 1/15<br/>
 * </p>
 *
 * !#zh
 * <p>
 *     注意:用 cc.director 代替 cc.Director。<br/>
 *     cc.director 一个管理你的游戏的逻辑流程的单例对象。<br/>
 *     由于 cc.director 是一个单例,你不需要调用任何构造函数或创建函数,<br/>
 *     使用它的标准方法是通过调用:<br/>
 *       - cc.director.methodName();
 *     <br/>
 *     它创建和处理主窗口并且管理什么时候执行场景。<br/>
 *     <br/>
 *     cc.director 还负责:<br/>
 *      - 初始化 OpenGL 环境。<br/>
 *      - 设置OpenGL像素格式。(默认是 RGB565)<br/>
 *      - 设置OpenGL缓冲区深度 (默认是 0-bit)<br/>
 *      - 设置空白场景的颜色 (默认是 黑色)<br/>
 *      - 设置投影 (默认是 3D)<br/>
 *      - 设置方向 (默认是 Portrait)<br/>
 *    <br/>
 *    cc.director 设置了 OpenGL 默认环境 <br/>
 *      - GL_TEXTURE_2D   启用。<br/>
 *      - GL_VERTEX_ARRAY 启用。<br/>
 *      - GL_COLOR_ARRAY  启用。<br/>
 *      - GL_TEXTURE_COORD_ARRAY 启用。<br/>
 * </p>
 * <p>
 *   cc.director 也同步定时器与显示器的刷新速率。
 *   <br/>
 *   特点和局限性: <br/>
 *      - 将计时器 & 渲染与显示器的刷新频率同步。<br/>
 *      - 只支持动画的间隔 1/60 1/30 & 1/15。<br/>
 * </p>
 *
 * @class Director
 * @extends EventTarget
 */
cc.Director = function () {
	// 将EventTarget的原型链复制生成的单例中
    EventTarget.call(this);

    // paused?
    this._paused = false;
    // purge?
    this._purgeDirectorInNextLoop = false;

	// 默认为cc.Sie(0, 0),暂无任何作用
    this._winSizeInPoints = null;

    // scenes
    this._scene = null;
    // 当前loading的场景名:string类型
    this._loadingScene = '';

    // FPS
    // director 启动以来游戏运行的总帧数
    this._totalFrames = 0;
    // 最后更新时间
    this._lastUpdate = 0;
    // 上一帧的增量时间
    this._deltaTime = 0.0;
    // 开始时间,默认为本类实例化时
    this._startTime = 0.0;

    // ParticleSystem max step delta time
    this._maxParticleDeltaTime = 0.0;

    // Scheduler for user registration update
    this._scheduler = null;
    // Scheduler for life-cycle methods in component
    this._compScheduler = null;
    // Node activator
    this._nodeActivator = null;
    // Action manager
    this._actionManager = null;

    var self = this;
    // 在触发 游戏进入前台运行时的事件 时,更新 最后更新时间
    game.on(game.EVENT_SHOW, function () {
        self._lastUpdate = performance.now();
    });

	// 监听一次 引擎初始化完成事件 时,调用 cc.director.init 事件
    game.once(game.EVENT_ENGINE_INITED, this.init, this);
};

// 原型链
cc.Director.prototype = {
    constructor: cc.Director,
    init: function () {
    	// 设置各个初始值
        this._totalFrames = 0;
        this._lastUpdate = performance.now();
        this._startTime = this._lastUpdate;
        this._paused = false;
        this._purgeDirectorInNextLoop = false;
        this._winSizeInPoints = cc.size(0, 0);
        this._scheduler = new Scheduler();

		// 是否存cc.ActionManager类
        if (cc.ActionManager) {
            this._actionManager = new cc.ActionManager();
            // 使用指定的优先级为指定的对象设置 update 定时器
            this._scheduler.scheduleUpdate(this._actionManager, Scheduler.PRIORITY_SYSTEM, false);
        } else {
            this._actionManager = null;
        }

		// 初始化其他模块
        this.sharedInit();
        return true;
    },

    /*
     * Manage all init process shared between the web engine and jsb engine.
     * All platform independent init process should be occupied here.
     */
    sharedInit: function () {
    	// 生成单例
        this._compScheduler = new ComponentScheduler();
        // 生成单例
        this._nodeActivator = new NodeActivator();

        // Event manager
        if (eventManager) {
        	// 激活事件管理器
            eventManager.setEnabled(true);
        }

        // Animation manager
        if (cc.AnimationManager) {
            this._animationManager = new cc.AnimationManager();
            // 使用指定的优先级为指定的对象设置 update 定时器
            this._scheduler.scheduleUpdate(this._animationManager, Scheduler.PRIORITY_SYSTEM, false);
        }
        else {
            this._animationManager = null;
        }

        // collision manager
        if (cc.CollisionManager) {
            this._collisionManager = new cc.CollisionManager();
            // 使用指定的优先级为指定的对象设置 update 定时器
            this._scheduler.scheduleUpdate(this._collisionManager, Scheduler.PRIORITY_SYSTEM, false);
        }
        else {
            this._collisionManager = null;
        }

        // physics manager
        if (cc.PhysicsManager) {
            this._physicsManager = new cc.PhysicsManager();
            // 使用指定的优先级为指定的对象设置 update 定时器
            this._scheduler.scheduleUpdate(this._physicsManager, Scheduler.PRIORITY_SYSTEM, false);
        }
        else {
            this._physicsManager = null;
        }

        // physics 3d manager
        if (cc.Physics3DManager && (CC_PHYSICS_BUILTIN || CC_PHYSICS_CANNON)) {
            this._physics3DManager = new cc.Physics3DManager();
            // 使用指定的优先级为指定的对象设置 update 定时器
            this._scheduler.scheduleUpdate(this._physics3DManager, Scheduler.PRIORITY_SYSTEM, false);
        } else {
            this._physics3DManager = null;
        }

        // WidgetManager
        if (cc._widgetManager) {
        	// 初始化对齐组件
            cc._widgetManager.init(this);
        }
    },

    /**
     * calculates delta time since last time it was called
     */
    calculateDeltaTime: function (now) {
        if (!now) now = performance.now();

        // avoid delta time from being negative
        // negative deltaTime would be caused by the precision of now's value, for details please see: https://developer.mozilla.org/zh-CN/docs/Web/API/window/requestAnimationFrame
        this._deltaTime = now > this._lastUpdate ? (now - this._lastUpdate) / 1000 : 0;
        // 如果是调试模式下,增量时间大于1秒,则重置帧数为 60 帧,增量时间为 1/60秒
        if (CC_DEBUG && (this._deltaTime > 1))
            this._deltaTime = 1 / 60.0;

        this._lastUpdate = now;
    },

    /**
     * !#en
     * Converts a view coordinate to an WebGL coordinate<br/>
     * Useful to convert (multi) touches coordinates to the current layout (portrait or landscape)<br/>
     * Implementation can be found in CCDirectorWebGL.
     * !#zh 将触摸点的屏幕坐标转换为 WebGL View 下的坐标。
     * @method convertToGL
     * @param {Vec2} uiPoint
     * @return {Vec2}
     * @deprecated since v2.0
     */
    convertToGL: function (uiPoint) {
    	// id 为 Cocos2dGameContainer 的div
        var container = game.container;
        var view = cc.view;
        // 获取 container 的各项属性
        var box = container.getBoundingClientRect();
        var left = box.left + window.pageXOffset - container.clientLeft;
        var top = box.top + window.pageYOffset - container.clientTop;
        var x = view._devicePixelRatio * (uiPoint.x - left);
        var y = view._devicePixelRatio * (top + box.height - uiPoint.y);
        return view._isRotated ? cc.v2(view._viewportRect.width - y, x) : cc.v2(x, y);
    },

    /**
     * !#en
     * Converts an OpenGL coordinate to a view coordinate<br/>
     * Useful to convert node points to window points for calls such as glScissor<br/>
     * Implementation can be found in CCDirectorWebGL.
     * !#zh 将触摸点的 WebGL View 坐标转换为屏幕坐标。
     * @method convertToUI
     * @param {Vec2} glPoint
     * @return {Vec2}
     * @deprecated since v2.0
     */
    convertToUI: function (glPoint) {
    	// id 为 Cocos2dGameContainer 的div
        var container = game.container;
        var view = cc.view;
        var box = container.getBoundingClientRect();
        var left = box.left + window.pageXOffset - container.clientLeft;
        var top = box.top + window.pageYOffset - container.clientTop;
        var uiPoint = cc.v2(0, 0);
        if (view._isRotated) {
            uiPoint.x = left + glPoint.y / view._devicePixelRatio;
            uiPoint.y = top + box.height - (view._viewportRect.width - glPoint.x) / view._devicePixelRatio;
        }
        else {
            uiPoint.x = left + glPoint.x * view._devicePixelRatio;
            uiPoint.y = top + box.height - glPoint.y * view._devicePixelRatio;
        }
        return uiPoint;
    },

    /**
     * End the life of director in the next frame
     * @method end
     */
    end: function () {
    	// 下一帧purge cc.director
        this._purgeDirectorInNextLoop = true;
    },

    /**
     * !#en
     * Returns the size of the WebGL view in points.<br/>
     * It takes into account any possible rotation (device orientation) of the window.
     * !#zh 获取视图的大小,以点为单位。
     * @method getWinSize
     * @return {Size}
     * @deprecated since v2.0
     */
    getWinSize: function () {
        return cc.size(cc.winSize);
    },

    /**
     * !#en
     * Returns the size of the OpenGL view in pixels.<br/>
     * It takes into account any possible rotation (device orientation) of the window.<br/>
     * On Mac winSize and winSizeInPixels return the same value.
     * (The pixel here refers to the resource resolution. If you want to get the physics resolution of device, you need to use cc.view.getFrameSize())
     * !#zh
     * 获取视图大小,以像素为单位(这里的像素指的是资源分辨率。
     * 如果要获取屏幕物理分辨率,需要用 cc.view.getFrameSize())
     * @method getWinSizeInPixels
     * @return {Size}
     * @deprecated since v2.0
     */
    getWinSizeInPixels: function () {
        return cc.size(cc.winSize);
    },

    /**
     * !#en Pause the director's ticker, only involve the game logic execution.
     * It won't pause the rendering process nor the event manager.
     * If you want to pause the entier game including rendering, audio and event, 
     * please use {{#crossLink "Game.pause"}}cc.game.pause{{/crossLink}}
     * !#zh 暂停正在运行的场景,该暂停只会停止游戏逻辑执行,但是不会停止渲染和 UI 响应。
     * 如果想要更彻底得暂停游戏,包含渲染,音频和事件,请使用 {{#crossLink "Game.pause"}}cc.game.pause{{/crossLink}}。
     * @method pause
     */
    pause: function () {
        if (this._paused)
            return;
        this._paused = true;
    },

    /**
     * Removes cached all cocos2d cached data.
     * @deprecated since v2.0
     */
    purgeCachedData: function () {
    	// 释放所有资源
        cc.assetManager.releaseAll();
    },

    /**
     * Purge the cc.director itself, including unschedule all schedule, remove all event listeners, clean up and exit the running scene, stops all animations, clear cached data.
     */
    purgeDirector: function () {
        //cleanup scheduler
        this._scheduler.unscheduleAll();
        this._compScheduler.unscheduleAll();

        this._nodeActivator.reset();

        // Disable event dispatching
        if (eventManager)
            eventManager.setEnabled(false);

		// 编辑器环境,清理场景和渲染器和内置资源
        if (!CC_EDITOR) {
            if (cc.isValid(this._scene)) {
                this._scene.destroy();
            }
            this._scene = null;

            cc.renderer.clear();
            cc.assetManager.builtins.clear();
        }

		// 暂停主逻辑
        cc.game.pause();

        // Clear all caches
        cc.assetManager.releaseAll();
    },

    /**
     * Reset the cc.director, can be used to restart the director after purge
     */
    reset: function () {
    	// 强制清理一遍先
        this.purgeDirector();

        if (eventManager)
            eventManager.setEnabled(true);

        // Action manager
        if (this._actionManager){
        	// 使用指定的优先级为指定的对象设置 update 定时器
            this._scheduler.scheduleUpdate(this._actionManager, cc.Scheduler.PRIORITY_SYSTEM, false);
        }

        // Animation manager
        if (this._animationManager) {
        	// 使用指定的优先级为指定的对象设置 update 定时器
            this._scheduler.scheduleUpdate(this._animationManager, cc.Scheduler.PRIORITY_SYSTEM, false);
        }

        // Collider manager
        if (this._collisionManager) {
        	// 使用指定的优先级为指定的对象设置 update 定时器
            this._scheduler.scheduleUpdate(this._collisionManager, cc.Scheduler.PRIORITY_SYSTEM, false);
        }

        // Physics manager
        if (this._physicsManager) {
        	// 使用指定的优先级为指定的对象设置 update 定时器
            this._scheduler.scheduleUpdate(this._physicsManager, cc.Scheduler.PRIORITY_SYSTEM, false);
        }

		// 恢复主逻辑
        cc.game.resume();
    },

    /**
     * !#en
     * Run a scene. Replaces the running scene with a new one or enter the first scene.<br/>
     * The new scene will be launched immediately.
     * !#zh 立刻切换指定场景。
     * @method runSceneImmediate
     * @param {Scene|SceneAsset} scene - The need run scene.
     * @param {Function} [onBeforeLoadScene] - The function invoked at the scene before loading.
     * @param {Function} [onLaunched] - The function invoked at the scene after launch.
     */
    runSceneImmediate: function (scene, onBeforeLoadScene, onLaunched) {
    	// 如果scene不是场景类或场景资源,则报1216的错误
        cc.assertID(scene instanceof cc.Scene || scene instanceof cc.SceneAsset, 1216);

        if (scene instanceof cc.SceneAsset) scene = scene.scene;

        CC_BUILD && CC_DEBUG && console.time('InitScene');
        scene._load();  // ensure scene initialized
        CC_BUILD && CC_DEBUG && console.timeEnd('InitScene');

        // Re-attach or replace persist nodes
        CC_BUILD && CC_DEBUG && console.time('AttachPersist');
        // 常驻节点列表
        var persistNodeList = Object.keys(game._persistRootNodes).map(function (x) {
            return game._persistRootNodes[x];
        });
        for (let i = 0; i < persistNodeList.length; i++) {
            let node = persistNodeList[i];
            var existNode = scene.getChildByUuid(node.uuid);
            // 如果场景存在 node ,则先销毁场景里的节点
            if (existNode) {
                // scene also contains the persist node, select the old one
                var index = existNode.getSiblingIndex();
                existNode._destroyImmediate();
                scene.insertChild(node, index);
            }
            else {
                node.parent = scene;
            }
        }
        CC_BUILD && CC_DEBUG && console.timeEnd('AttachPersist');

        var oldScene = this._scene;
        // 非编辑器环境下 自动释放资源
        if (!CC_EDITOR) {
            // auto release assets
            CC_BUILD && CC_DEBUG && console.time('AutoRelease');
            cc.assetManager._releaseManager._autoRelease(oldScene, scene, persistNodeList);
            CC_BUILD && CC_DEBUG && console.timeEnd('AutoRelease');
        }

        // unload scene
        CC_BUILD && CC_DEBUG && console.time('Destroy');
        if (cc.isValid(oldScene)) {
            oldScene.destroy();
        }

        this._scene = null;

        // purge destroyed nodes belongs to old scene
        Obj._deferredDestroy();
        CC_BUILD && CC_DEBUG && console.timeEnd('Destroy');

		// 生成场景前事件回调
        if (onBeforeLoadScene) {
            onBeforeLoadScene();
        }
        // 派发事件
        this.emit(cc.Director.EVENT_BEFORE_SCENE_LAUNCH, scene);

        // Run an Entity Scene
        this._scene = scene;

        CC_BUILD && CC_DEBUG && console.time('Activate');
        scene._activate();
        CC_BUILD && CC_DEBUG && console.timeEnd('Activate');

        //start scene
        cc.game.resume();

		// 加载完场景事件回调
        if (onLaunched) {
            onLaunched(null, scene);
        }
        // 派发事件
        this.emit(cc.Director.EVENT_AFTER_SCENE_LAUNCH, scene);
    },

    /**
     * !#en
     * Run a scene. Replaces the running scene with a new one or enter the first scene.
     * The new scene will be launched at the end of the current frame.
     * !#zh 运行指定场景。
     * @method runScene
     * @param {Scene|SceneAsset} scene - The need run scene.
     * @param {Function} [onBeforeLoadScene] - The function invoked at the scene before loading.
     * @param {Function} [onLaunched] - The function invoked at the scene after launch.
     */
    runScene: function (scene, onBeforeLoadScene, onLaunched) {
        cc.assertID(scene, 1205);
        cc.assertID(scene instanceof cc.Scene || scene instanceof cc.SceneAsset, 1216);

        if (scene instanceof cc.SceneAsset) scene = scene.scene;
        // ensure scene initialized
        scene._load();

        // Delay run / replace scene to the end of the frame
        this.once(cc.Director.EVENT_AFTER_DRAW, function () {
            this.runSceneImmediate(scene, onBeforeLoadScene, onLaunched);
        }, this);
    },

    /**
     * !#en Loads the scene by its name.
     * !#zh 通过场景名称进行加载场景。
     *
     * @method loadScene
     * @param {String} sceneName - The name of the scene to load.
     * @param {Function} [onLaunched] - callback, will be called after scene launched.
     * @return {Boolean} if error, return false
     */
    loadScene: function (sceneName, onLaunched, _onUnloaded) {
    	// 如果存在正在加载的场景,报错
        if (this._loadingScene) {
            cc.warnID(1208, sceneName, this._loadingScene);
            return false;
        }
        // 查找 bundles 中存在 sceneName 的场景信息的 bundle
        var bundle = cc.assetManager.bundles.find(function (bundle) {
            return bundle.getSceneInfo(sceneName);
        });
        // 如果 bundle 不存在,即场景数据资源不存在,报错
        if (bundle) {
        	// 派发场景加载前事件
            this.emit(cc.Director.EVENT_BEFORE_SCENE_LOADING, sceneName);
            this._loadingScene = sceneName;
            var self = this;
            console.time('LoadScene ' + sceneName);
            // 加载场景
            bundle.loadScene(sceneName, function (err, scene) {
                console.timeEnd('LoadScene ' + sceneName);
                self._loadingScene = '';
                if (err) {
                    err = 'Failed to load scene: ' + err;
                    cc.error(err);
                    onLaunched && onLaunched(err);
                }
                else {
                	// 立即运行场景
                    self.runSceneImmediate(scene, _onUnloaded, onLaunched);
                }
            });
            return true;
        }
        else {
            cc.errorID(1209, sceneName);
            return false;
        }
    },

	/**
     * !#en
     * Preloads the scene to reduces loading time. You can call this method at any time you want.
     * After calling this method, you still need to launch the scene by `cc.director.loadScene`.
     * It will be totally fine to call `cc.director.loadScene` at any time even if the preloading is not
     * yet finished, the scene will be launched after loaded automatically.
     * !#zh 预加载场景,你可以在任何时候调用这个方法。
     * 调用完后,你仍然需要通过 `cc.director.loadScene` 来启动场景,因为这个方法不会执行场景加载操作。
     * 就算预加载还没完成,你也可以直接调用 `cc.director.loadScene`,加载完成后场景就会启动。
     *
     * @method preloadScene
     * @param {String} sceneName - The name of the scene to preload.
     * @param {Function} [onProgress] - callback, will be called when the load progression change.
     * @param {Number} onProgress.completedCount - The number of the items that are already completed
     * @param {Number} onProgress.totalCount - The total number of the items
     * @param {Object} onProgress.item - The latest item which flow out the pipeline
     * @param {Function} [onLoaded] - callback, will be called after scene loaded.
     * @param {Error} onLoaded.error - null or the error object.
     */
    preloadScene (sceneName, onProgress, onLoaded) {
    	// 查找 bundles 中存在 sceneName 的场景信息的 bundle
        var bundle = cc.assetManager.bundles.find(function (bundle) {
            return bundle.getSceneInfo(sceneName);
        });
        // 如果 bundle 不存在,即场景数据资源不存在,报错
        if (bundle) {
            bundle.preloadScene(sceneName, null, onProgress, onLoaded);
        }
        else {
            cc.errorID(1209, sceneName);
            return null;
        }
    },

    /**
     * !#en Resume game logic execution after pause, if the current scene is not paused, nothing will happen.
     * !#zh 恢复暂停场景的游戏逻辑,如果当前场景没有暂停将没任何事情发生。
     * @method resume
     */
    resume: function () {
        if (!this._paused) {
            return;
        }

        this._lastUpdate = performance.now();
        if (!this._lastUpdate) {
            cc.logID(1200);
        }

        this._paused = false;
        this._deltaTime = 0;
    },

    /**
     * !#en
     * Enables or disables WebGL depth test.<br/>
     * Implementation can be found in CCDirectorCanvas.js/CCDirectorWebGL.js
     * !#zh 启用/禁用深度测试(在 Canvas 渲染模式下不会生效)。
     * @method setDepthTest
     * @param {Boolean} on
     * @deprecated since v2.0
     */
    setDepthTest: function (value) {
   		// 无主摄像机,则不执行
        if (!cc.Camera.main) {
            return;
        }
        cc.Camera.main.depth = !!value;
    },

    /**
     * !#en
     * Set color for clear screen.<br/>
     * (Implementation can be found in CCDirectorCanvas.js/CCDirectorWebGL.js)
     * !#zh
     * 设置场景的默认擦除颜色。<br/>
     * 支持全透明,但不支持透明度为中间值。要支持全透明需手工开启 cc.macro.ENABLE_TRANSPARENT_CANVAS。
     * @method setClearColor
     * @param {Color} clearColor
     * @deprecated since v2.0
     */
    setClearColor: function (clearColor) {
        if (!cc.Camera.main) {
            return;
        }
        cc.Camera.main.backgroundColor = clearColor;
    },

    /**
     * !#en Returns current logic Scene.
     * !#zh 获取当前逻辑场景。
     * @method getRunningScene
     * @private
     * @return {Scene}
     * @deprecated since v2.0
     */
    getRunningScene: function () {
        return this._scene;
    },

    /**
     * !#en Returns current logic Scene.
     * !#zh 获取当前逻辑场景。
     * @method getScene
     * @return {Scene}
     * @example
     *  // This will help you to get the Canvas node in scene
     *  cc.director.getScene().getChildByName('Canvas');
     */
    getScene: function () {
        return this._scene;
    },

    /**
     * !#en Returns the FPS value. Please use {{#crossLink "Game.setFrameRate"}}cc.game.setFrameRate{{/crossLink}} to control animation interval.
     * !#zh 获取单位帧执行时间。请使用 {{#crossLink "Game.setFrameRate"}}cc.game.setFrameRate{{/crossLink}} 来控制游戏帧率。
     * @method getAnimationInterval
     * @deprecated since v2.0
     * @return {Number}
     */
    getAnimationInterval: function () {
        return 1000 / game.getFrameRate();
    },

    /**
     * Sets animation interval, this doesn't control the main loop.
     * To control the game's frame rate overall, please use {{#crossLink "Game.setFrameRate"}}cc.game.setFrameRate{{/crossLink}}
     * @method setAnimationInterval
     * @deprecated since v2.0
     * @param {Number} value - The animation interval desired.
     */
    setAnimationInterval: function (value) {
        game.setFrameRate(Math.round(1000 / value));
    },

    /**
     * !#en Returns the delta time since last frame.
     * !#zh 获取上一帧的增量时间。
     * @method getDeltaTime
     * @return {Number}
     */
    getDeltaTime: function () {
        return this._deltaTime;
    },

    /**
     * !#en Returns the total passed time since game start, unit: ms
     * !#zh 获取从游戏开始到现在总共经过的时间,单位为 ms
     * @method getTotalTime
     * @return {Number}
     */
    getTotalTime: function () {
        return performance.now() - this._startTime;
    },

    /**
     * !#en Returns how many frames were called since the director started.
     * !#zh 获取 director 启动以来游戏运行的总帧数。
     * @method getTotalFrames
     * @return {Number}
     */
    getTotalFrames: function () {
        return this._totalFrames;
    },

    /**
     * !#en Returns whether or not the Director is paused.
     * !#zh 是否处于暂停状态。
     * @method isPaused
     * @return {Boolean}
     */
    isPaused: function () {
        return this._paused;
    },

    /**
     * !#en Returns the cc.Scheduler associated with this director.
     * !#zh 获取和 director 相关联的 cc.Scheduler。
     * @method getScheduler
     * @return {Scheduler}
     */
    getScheduler: function () {
        return this._scheduler;
    },

    /**
     * !#en Sets the cc.Scheduler associated with this director.
     * !#zh 设置和 director 相关联的 cc.Scheduler。
     * @method setScheduler
     * @param {Scheduler} scheduler
     */
    setScheduler: function (scheduler) {
        if (this._scheduler !== scheduler) {
            this._scheduler = scheduler;
        }
    },

    /**
     * !#en Returns the cc.ActionManager associated with this director.
     * !#zh 获取和 director 相关联的 cc.ActionManager(动作管理器)。
     * @method getActionManager
     * @return {ActionManager}
     */
    getActionManager: function () {
        return this._actionManager;
    },
    /**
     * !#en Sets the cc.ActionManager associated with this director.
     * !#zh 设置和 director 相关联的 cc.ActionManager(动作管理器)。
     * @method setActionManager
     * @param {ActionManager} actionManager
     */
    setActionManager: function (actionManager) {
        if (this._actionManager !== actionManager) {
        	// 取消之前的 actionManager 的update回调
            if (this._actionManager) {
                this._scheduler.unscheduleUpdate(this._actionManager);
            }
            this._actionManager = actionManager;
            // 使用指定的优先级为指定的对象设置 update 定时器
            this._scheduler.scheduleUpdate(this._actionManager, cc.Scheduler.PRIORITY_SYSTEM, false);
        }
    },

    /* 
     * !#en Returns the cc.AnimationManager associated with this director.
     * !#zh 获取和 director 相关联的 cc.AnimationManager(动画管理器)。
     * @method getAnimationManager
     * @return {AnimationManager}
     */
    getAnimationManager: function () {
        return this._animationManager;
    },

    /**
     * !#en Returns the cc.CollisionManager associated with this director.
     * !#zh 获取和 director 相关联的 cc.CollisionManager (碰撞管理器)。
     * @method getCollisionManager
     * @return {CollisionManager}
     */
    getCollisionManager: function () {
        return this._collisionManager;
    },

    /**
     * !#en Returns the cc.PhysicsManager associated with this director.
     * !#zh 返回与 director 相关联的 cc.PhysicsManager (物理管理器)。
     * @method getPhysicsManager
     * @return {PhysicsManager}
     */
    getPhysicsManager: function () {
        return this._physicsManager;
    },

    /**
     * !#en Returns the cc.Physics3DManager associated with this director.
     * !#zh 返回与 director 相关联的 cc.Physics3DManager (物理管理器)。
     * @method getPhysics3DManager
     * @return {Physics3DManager}
     */
    getPhysics3DManager: function () {
        return this._physics3DManager;
    },

    // Loop management
    /*
     * Starts Animation
     * @deprecated since v2.1.2
     */
    startAnimation: function () {
        cc.game.resume();
    },

    /*
     * Stops animation
     * @deprecated since v2.1.2
     */
    stopAnimation: function () {
        cc.game.pause();
    },

	// 在 cc.game.resume 里 重置下一帧增量时间
    _resetDeltaTime () {
        this._lastUpdate = performance.now();
        this._deltaTime = 0;
    },

    /*
     * Run main loop of director
     */
    mainLoop: CC_EDITOR ? function (deltaTime, updateAnimate) {
        this._deltaTime = deltaTime;

        // Update
        if (!this._paused) {
        	// 派发 更新前 事件
            this.emit(cc.Director.EVENT_BEFORE_UPDATE);

			// 为新添加的组件调用 start
            this._compScheduler.startPhase();
            // 为新添加的组件调用 update
            this._compScheduler.updatePhase(deltaTime);

			// updateAnimate 存在且为 true 时,调用 scheduler 的 update 调度
            if (updateAnimate) {
                this._scheduler.update(deltaTime);
            }

			// 为新添加的组件调用 lateUpdate
            this._compScheduler.lateUpdatePhase(deltaTime);

			// 派发 更新后 事件
            this.emit(cc.Director.EVENT_AFTER_UPDATE);
        }

        // Render
        // 派发 渲染前 事件
        this.emit(cc.Director.EVENT_BEFORE_DRAW);
        renderer.render(this._scene, deltaTime);
        
        // After draw
        // 派发 渲染后 事件
        this.emit(cc.Director.EVENT_AFTER_DRAW);

		// 总帧数增加
        this._totalFrames++;

    } : function (now) {
    	// 是否需要 purge
        if (this._purgeDirectorInNextLoop) {
            this._purgeDirectorInNextLoop = false;
            this.purgeDirector();
        }
        else {
            // calculate "global" dt
            this.calculateDeltaTime(now);

            // Update
            if (!this._paused) {
                // before update
                // 派发 更新前 事件
                this.emit(cc.Director.EVENT_BEFORE_UPDATE);

                // Call start for new added components
                this._compScheduler.startPhase();

                // Update for components
                this._compScheduler.updatePhase(this._deltaTime);
                // Engine update with scheduler
                this._scheduler.update(this._deltaTime);

                // Late update for components
                this._compScheduler.lateUpdatePhase(this._deltaTime);

                // User can use this event to do things after update
                // 派发 更新后 事件
                this.emit(cc.Director.EVENT_AFTER_UPDATE);
                
                // Destroy entities that have been removed recently
                Obj._deferredDestroy();
            }

            // Render
            // 派发 渲染前 事件
            this.emit(cc.Director.EVENT_BEFORE_DRAW);
            renderer.render(this._scene, this._deltaTime);

            // After draw
            // 派发 渲染后 事件
            this.emit(cc.Director.EVENT_AFTER_DRAW);

			// 事件管理器 检测各个属性和是否存在需要添加的事件,是否存在需要注销的事件等操作
            eventManager.frameUpdateListeners();
            // 总帧数增加
            this._totalFrames++;
        }
    },

    __fastOn: function (type, callback, target) {
        this.on(type, callback, target);
    },

    __fastOff: function (type, callback, target) {
        this.off(type, callback, target);
    },
};

// Event target
// 将EventTarget的原型链复制到cc.Director.prototype里
cc.js.addon(cc.Director.prototype, EventTarget.prototype);

/**
 * !#en The event projection changed of cc.Director. This event will not get triggered since v2.0
 * !#zh cc.Director 投影变化的事件。从 v2.0 开始这个事件不会再被触发
 * @property {String} EVENT_PROJECTION_CHANGED
 * @readonly
 * @static
 * @deprecated since v2.0
 */
cc.Director.EVENT_PROJECTION_CHANGED = "director_projection_changed";

/**
 * !#en The event which will be triggered before loading a new scene.
 * !#zh 加载新场景之前所触发的事件。
 * @event cc.Director.EVENT_BEFORE_SCENE_LOADING
 * @param {String} sceneName - The loading scene name
 */
/**
 * !#en The event which will be triggered before loading a new scene.
 * !#zh 加载新场景之前所触发的事件。
 * @property {String} EVENT_BEFORE_SCENE_LOADING
 * @readonly
 * @static
 */
cc.Director.EVENT_BEFORE_SCENE_LOADING = "director_before_scene_loading";

/*
 * !#en The event which will be triggered before launching a new scene.
 * !#zh 运行新场景之前所触发的事件。
 * @event cc.Director.EVENT_BEFORE_SCENE_LAUNCH
 * @param {String} sceneName - New scene which will be launched
 */
/**
 * !#en The event which will be triggered before launching a new scene.
 * !#zh 运行新场景之前所触发的事件。
 * @property {String} EVENT_BEFORE_SCENE_LAUNCH
 * @readonly
 * @static
 */
cc.Director.EVENT_BEFORE_SCENE_LAUNCH = "director_before_scene_launch";

/**
 * !#en The event which will be triggered after launching a new scene.
 * !#zh 运行新场景之后所触发的事件。
 * @event cc.Director.EVENT_AFTER_SCENE_LAUNCH
 * @param {String} sceneName - New scene which is launched
 */
/**
 * !#en The event which will be triggered after launching a new scene.
 * !#zh 运行新场景之后所触发的事件。
 * @property {String} EVENT_AFTER_SCENE_LAUNCH
 * @readonly
 * @static
 */
cc.Director.EVENT_AFTER_SCENE_LAUNCH = "director_after_scene_launch";

/**
 * !#en The event which will be triggered at the beginning of every frame.
 * !#zh 每个帧的开始时所触发的事件。
 * @event cc.Director.EVENT_BEFORE_UPDATE
 */
/**
 * !#en The event which will be triggered at the beginning of every frame.
 * !#zh 每个帧的开始时所触发的事件。
 * @property {String} EVENT_BEFORE_UPDATE
 * @readonly
 * @static
 */
cc.Director.EVENT_BEFORE_UPDATE = "director_before_update";

/**
 * !#en The event which will be triggered after engine and components update logic.
 * !#zh 将在引擎和组件 “update” 逻辑之后所触发的事件。
 * @event cc.Director.EVENT_AFTER_UPDATE
 */
/**
 * !#en The event which will be triggered after engine and components update logic.
 * !#zh 将在引擎和组件 “update” 逻辑之后所触发的事件。
 * @property {String} EVENT_AFTER_UPDATE
 * @readonly
 * @static
 */
cc.Director.EVENT_AFTER_UPDATE = "director_after_update";

/**
 * !#en The event is deprecated since v2.0, please use cc.Director.EVENT_BEFORE_DRAW instead
 * !#zh 这个事件从 v2.0 开始被废弃,请直接使用 cc.Director.EVENT_BEFORE_DRAW
 * @property {String} EVENT_BEFORE_VISIT
 * @readonly
 * @deprecated since v2.0
 * @static
 */
cc.Director.EVENT_BEFORE_VISIT = "director_before_draw";

/**
 * !#en The event is deprecated since v2.0, please use cc.Director.EVENT_BEFORE_DRAW instead
 * !#zh 这个事件从 v2.0 开始被废弃,请直接使用 cc.Director.EVENT_BEFORE_DRAW
 * @property {String} EVENT_AFTER_VISIT
 * @readonly
 * @deprecated since v2.0
 * @static
 */
cc.Director.EVENT_AFTER_VISIT = "director_before_draw";

/**
 * !#en The event which will be triggered before the rendering process.
 * !#zh 渲染过程之前所触发的事件。
 * @event cc.Director.EVENT_BEFORE_DRAW
 */
/**
 * !#en The event which will be triggered before the rendering process.
 * !#zh 渲染过程之前所触发的事件。
 * @property {String} EVENT_BEFORE_DRAW
 * @readonly
 * @static
 */
cc.Director.EVENT_BEFORE_DRAW = "director_before_draw";

/**
 * !#en The event which will be triggered after the rendering process.
 * !#zh 渲染过程之后所触发的事件。
 * @event cc.Director.EVENT_AFTER_DRAW
 */
/**
 * !#en The event which will be triggered after the rendering process.
 * !#zh 渲染过程之后所触发的事件。
 * @property {String} EVENT_AFTER_DRAW
 * @readonly
 * @static
 */
cc.Director.EVENT_AFTER_DRAW = "director_after_draw";

//Possible OpenGL projections used by director

/**
 * Constant for 2D projection (orthogonal projection)
 * @property {Number} PROJECTION_2D
 * @default 0
 * @readonly
 * @static
 * @deprecated since v2.0
 */
cc.Director.PROJECTION_2D = 0;

/**
 * Constant for 3D projection with a fovy=60, znear=0.5f and zfar=1500.
 * @property {Number} PROJECTION_3D
 * @default 1
 * @readonly
 * @static
 * @deprecated since v2.0
 */
cc.Director.PROJECTION_3D = 1;

/**
 * Constant for custom projection, if cc.Director's projection set to it, it calls "updateProjection" on the projection delegate.
 * @property {Number} PROJECTION_CUSTOM
 * @default 3
 * @readonly
 * @static
 * @deprecated since v2.0
 */
cc.Director.PROJECTION_CUSTOM = 3;

/**
 * Constant for default projection of cc.Director, default projection is 2D projection
 * @property {Number} PROJECTION_DEFAULT
 * @default cc.Director.PROJECTION_2D
 * @readonly
 * @static
 * @deprecated since v2.0
 */
cc.Director.PROJECTION_DEFAULT = cc.Director.PROJECTION_2D;

/**
 * The event which will be triggered before the physics process.<br/>
 * 物理过程之前所触发的事件。
 * @event Director.EVENT_BEFORE_PHYSICS
 * @readonly
 */
cc.Director.EVENT_BEFORE_PHYSICS = 'director_before_physics';

/**
 * The event which will be triggered after the physics process.<br/>
 * 物理过程之后所触发的事件。
 * @event Director.EVENT_AFTER_PHYSICS
 * @readonly
 */
cc.Director.EVENT_AFTER_PHYSICS = 'director_after_physics';

/**
 * @module cc
 */

/**
 * !#en Director
 * !#zh 导演类。
 * @property director
 * @type {Director}
 */
cc.director = new cc.Director();

module.exports = cc.director;

结论

一:

  1. cc.director在cc.game.run执行前就已经实例化了,cc.director里提供的方法大多是配合其他模块使用。
  2. cc.director提供了一些比较关键的函数,如:mainLoop, pause, resume, reset
    其中mainLoop用于CCCgame初始化游戏逻辑后 被 帧渲染函数所调用,主要执行逻辑顺序如下:
1.设置增量时间
2.派发cc.Director.EVENT_BEFORE_UPDATE
3._compScheduler(组件管理器)调用start回调
4._compScheduler调用update回调
5.使用_scheduler更新引擎,定时器等
6._compScheduler调用lateUpdate回调
7.派发cc.Director.EVENT_AFTER_UPDATE
8.销毁之前生成的CCObject
9.派发cc.Director.EVENT_BEFORE_DRAW
10.renderer更新渲染逻辑
11.派发cc.Director.EVENT_AFTER_DRAW
12.eventManager检测各个属性和是否存在需要添加的事件,是否存在需要注销的事件等操作
13.总帧数增加

从这顺序,我们可以清晰的知道一些关键的信息,如:
为什么获取当前视图,不在EVENT_AFTER_UPDATE中获取,而是需要在EVENT_AFTER_DRAW中获取,因为在EVENT_AFTER_DRAW事件后才是更新后的视图。
设置在lateUpdate中,会在全部组件update后才调用,一般用于部分场景内的节点计算完属性后,特殊节点根据其他节点的属性反馈操作。
设置在定时器的回调,会在组件的update之后才调用。

  1. cc.director还提供了场景加载切换等函数,如:runSceneImmediaterunSceneloadScenepreloadScene

    其中preloadScene这个函数是在V2.0版本之后才有的,在v2.0之前的版本,如需获取加载场景时的进度,可以设置cc.loader.onProgress获取进度。也可模仿asset-manager文件夹下的bundle.js里的preloadScene函数,直接加载场景资源,但不将sence传入回调。

    当然v2.0之后大改了资源管理和加载类,可能跟之前的版本有些区别,需要根据架构来编写对应的功能。接下来我会单独写一章来分析下v2.0之后的资源管理类。

  2. 了解了主逻辑外,其他模块的生成和注入。我们可以自行根据业务来编写一些额外的模块,加入到sharedInit中。还可调整其update优先级。

总之,CCDirector是跟CCGame相辅相成的一个类,一个控制主逻辑和帧渲染。一个提供除音频动画等以外的暂停恢复函数,更是提供mainLoop函数,连接组件和节点和渲染器之间。

这一章的编写拖了很久,之前写了一部分停下了。之后版本更新太快,只能按照新版本来解析,有错误或者不对的地方,还请大家提出或私信我。
最后感谢大家的观看,祝各位的编程能力越来越好,工资越来越高!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值