creator源码分析(生命周期函数回调相关)

从官方对creator引擎的产品定位中我们不难得知,cocos Creator是基于组件化脚本化开发,更具体点是基于js的脚本化,组件化开发

在这里插入图片描述

我们在组件脚本开发的时候首先注意的必定就是其生命周期了,目前creator提供给我们的生命周期回调函数主要有onLoad,start,update,lateUpdate,onDestroy,onEnable,
onDisable,我
们可以简单描述一下这些回调触发的时机

onLoad回调函数会在节点首次激活时触发,在任何start函数调用前执行

start回调函数会在组件第一次激活前,也就是第一次执行 update 之前触发

update在组件进行更新时执行, 每一帧渲染前更新物体的行为

lateUpdate在所有组件的 update 都执行完之后

onDestroy当组件或者所在节点调用了 destroy()

onEnable当组件的 enabled 属性从 false 变为 true 时,或者所在节点的 active 属性从 false 变为 true 时

onDisable当组件的 enabled 属性从 true 变为 false 时,或者所在节点的 active 属性从 true 变为 false 时

需要注意的是我们的初始化通常在start函数而不再onload函数中的原因:onload是场景加载完成执行,start是当前组件开始运行时执行。假设每个组件的初始化都在onload完成,当一个节点的初始化依赖于另一个节点的属性时,就有可能出现异常,因为有可能另一个节点还没有执行onload。而start是所有组件的onload都执行完了才会执行,这时无论获取哪个节点的属性都不会有问题,因为那个节点已经初始化完成了。所以节点的UI初始化最好放在start中。
知道了这些,我们在做开发的时候就会有了一个大概的流程概念,在合适的时机处理自己想要的操作,晓得这些回调函数是在什么时候触发的了,天生丽质的宝宝们都会追根到底,那为啥你说他那个时候触发他就那个时候触发啊,ok,接下来,我们就详细的解析一

从cocos官方文档中我们可得知 Cocos Creator 的引擎部分包括 JavaScript、Cocos2d-x-lite 和 adapter 三个部分,引擎代码大致分为js和原生的c++ 两种类型,分别在引擎安装目录resources/engine 和 resources/cocos2d-x下。
在这里插入图片描述

咱们就从web平台来分析吧,主要看的就是engine\cocos2d 目录下的源码。

在这里插入图片描述我们常说打出来的空包就要1-2兆,其实一部分原因就是需要把这些模块依赖给打包进去…em,有点跑题了

creator对应打出来的web包,对应的cocos2d-js-min.js文件所有模块的源码。整个文件的执行周期,main.js是creator引擎的入口,他会在cocos2d-js-min.js加载完之后开始运行游戏
如果简化main.js 的内容大概是这样的

//main.js 
window.boot =function() {
	//loading页面相关
	 function setLoadingDisplay (){...};
	 //主要是对Web和native游戏场景尺寸或者朝向的处理以及加载scene相关
	 var onStart = function(){
		//以及调用上方的setLoadingDisplay 函数设置loading相关
     };
	 //当前的游戏配置
	 var option = function(){...};
	 //s获取_CCSettings变量进行处理
	  cc.AssetLibrary.init({...});
	 //cc.game包含游戏主体信息并负责驱动游戏的游戏对象,传入配置参数
	 //运行游戏,并且指定引擎配置和 onStart 的回调
	 cc.game.run(option, onStart);
}
//调用
window.boot();

这里我们可以看到,如果简化来看,boot中就是声明和设置一些配置信息或者一些出来,以传参的形式传到run函数中
在run方法中,会根据配置进行引擎的准备工作,简单流程如下

//game
var game = {
	//运行游戏,并且指定引擎配置和 onStart 的回调。
	run(config, onStart){
		// 注册配置数据      
		this._initConfig(config);
		this.onStart = onStart;
		// 开始准备      
		this.prepare(game.onStart && game.onStart.bind(game));
	},
	//准备引擎
	prepare(cb){
	   //如果已准备则执行回调返回
	        if (this._prepared){          
	            if (cb) cb();       
	            return;    
	         }
		let jsList = this.config.jsList;
		 if (jsList && jsList.length > 0){
			var self = this;  
			// 加载准备脚本之后调用         
			cc.loader.load(jsList, function (err) {                
				if (err) throw new Error(JSON.stringify(err)); 
				self._prepareFinished(cb);            
			});
		}else{
			this._prepareFinished(cb);
		}
	},
	prepareFinished(cb){
		/*
		//初始化引擎,设置帧循环事件等等方法       
		this._initEngine();                
		this._setAnimFrame();
		.....


			↑↑↑↑↑↑↑↑
		至此已经完成了引擎的准备工作
		*/
		
		//开始运行主循环
		this._runMainLoop();
		//派发EVENT_GAME_INITED事件
		this.emit(this.EVENT_GAME_INITED);
	},
}

由上可知,运行的顺序在cocos2d-js-min.js加载完之后执行main.js的windows.boot,传入配置调cc.game.run函数,然后注册配置信息 开始准备prepare,加载脚本初始化引擎,设置帧事件,调用_runMainLoop开始运行主循环。
ok,到运行主循环这里就开始接触到我们要探究的生命周期回调函数了,

//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) {
        		//requestAnimationFrame是浏览器用于定时循环操作的一个接口,类似于setTimeout,主要用途是按帧对网页进行重绘                
		       self._intervalId = window.requestAnimFrame(callback);                
		        if (!CC_JSB && !CC_RUNTIME && frameRate === 30) {    
	        		// 每2帧跳1帧执行                
		       		if (skip = !skip) {                       
		       		 	return;                    
		       		 }                
		        }  
		     	// 执行下一帧循环             
	        	director.mainLoop(now);            
	         }        
	};
        self._intervalId = window.requestAnimFrame(callback);        
        self._paused = false;   
  },

可以看出,利用 window.requestAnimationFrame 及其回调函数来实现循环,循环里调用的是 director.mainLoop(),

//CCDirector.js
//在初始化时 有
  sharedInit: function () {        
	this._compScheduler = new ComponentScheduler();        
	this._nodeActivator = new NodeActivator();
        cc.loader.init(this);    
   },


   mainLoop (now) {       
	  if (this._purgeDirectorInNextLoop) {        
		    this._purgeDirectorInNextLoop = false;           
		    this.purgeDirector();        
	    } else {            
	  	// 计算全局的时间增量,即 dt  
            	this.calculateDeltaTime(now);          
       		if (!this._paused) {
      			//每个帧的开始时所触发的事件                
             		this.emit(cc.Director.EVENT_BEFORE_UPDATE);               
             		// 对最新加入的组件调用 `start` 方法            
	    		this._compScheduler.startPhase();               
	     		// 调用组件的 `update` 方法	                 
	    		this._compScheduler.updatePhase(this._deltaTime);               
	    		//调用调度器的 `update` 方法  
			this._scheduler.update(this._deltaTime);                
			// 调用组件的 `lateUpdate` 方法
			this._compScheduler.lateUpdatePhase(this._deltaTime)// 将在引擎和组件 “update” 逻辑之后所触发的事件。              
			this.emit(cc.Director.EVENT_AFTER_UPDATE);               
			// 回收内存             
			Obj._deferredDestroy();           
		}
               // 访问渲染场景树之前所触发的事件。            
                this.emit(cc.Director.EVENT_BEFORE_DRAW);           
 		renderer.render(this._scene, this._deltaTime);
                // 渲染过程之后所触发的事件            
                this.emit(cc.Director.EVENT_AFTER_DRAW);
                eventManager.frameUpdateListeners();            
                this._totalFrames++;       
              }   
  }, 

ok,看到这里我们已经大概很明确这些回调了,接下来我们在深入的看一下ComponentScheduler类和NodeActivator类具体函数的实现,我们以start简化源码为例来看一下

//component-scheduler.js	
 function ctor(){
 	//开始
	this.startInvoker = new OneOffInvoker(invokeStart);
	this.updateInvoker = new ReusableInvoker(invokeUpdate);
	this.lateUpdateInvoker = new ReusableInvoker(invokeLateUpdate);
	//下一帧组件方法数组
	this.scheduleInNextFrame = [];
	// 一次循环中
	this._updating = false;
 },

 var OneOffInvoker = cc.Class({
	extends: LifeCycleInvoker,
	//....
	//....
 	invoke () {...}
 )};
 
 var invokeStar =  function (iterator) {
	 var array = iterator.array;
	 for (iterator.i = 0; iterator.i < array.length; ++iterator.i) {
	 	let comp = array[iterator.i];
	 	//调用组件的 start 方法
		comp.start();
	 	comp._objFlags |= IsStartCalled;
	 }
 },

 startPhase () { 
	// 当前帧开始       
        this._updating = true;
        if (this.scheduleInNextFrame.length > 0) {            
        	this._deferredSchedule();        
        }
        //调用 startInvoker   
        this.startInvoker.invoke();
 }

我们可以看到在外部调用startPhase 方法后,他会去判断上个循环是否结束,然后调用this.startInvoke,而他在构造函数中已经实现。update,onenable等生命周期回调方法都在这个脚本中实现,而在NodeActivator类中的activateComp方法的工作就是调用并只调用一次组件的 onLoad 方法。这里就不在描述

好了,至此,已经分析了creator提供给我们的这些生命周期回调方法的实现。如有遗漏或不足,还请多多指教

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值