zoukankan      html  css  js  c++  java
  • Laya1.x Timer小记

    Timer是时钟管理类,在Laya初始化的时候会创建一个实例,通过Laya.timer访问。

    TimerHandler
    • TimerHandler是对每一个定时任务的封装,每次调用frameOnce、frameLoop、once、loop或者callLayer都会产生一个TimerHandler实例。
    • TimerHandler会缓存传入的参数(args),当定时器触发时,会将对应的args传递给对应回调。
    public var key:int;   			//唯一key,用于快速找到对应handler
    public var repeat:Boolean;		//是否是重复调用   分别对应 once/loop
    public var delay:int;			//延时
    public var userFrame:Boolean;	// true表示使用帧计时  false表示使用时间计时
    public var exeTime:int;			//下次执行的时间
    public var caller:*				//调用者
    public var method:Function;		//调用方法
    public var args:Array;			//触发时的参数
    public var jumpFrame:Boolean;	//时钟是否跳帧。基于时间的循环回调,单位时间间隔内,如能执行多次回调,出于性能考虑,引擎默认只执行一次,设置jumpFrame=true后,则回调会连续执行多次
    
    public function clear():void {
    	caller = null;
    	method = null;
    	args = null;
    }
    
    public function run(withClear:Boolean):void {
    	var caller:* = this.caller;
    	/*[IF-FLASH]*/
    	if ((caller is Node) && caller.destroyed)
    		/*[IF-FLASH]*/
    		return clear();
    	//[IF-SCRIPT] if (caller && caller.destroyed) return clear();
    	var method:Function = this.method;
    	var args:Array = this.args;
    	withClear && clear();
    	if (method == null) return;
    	args ? method.apply(caller, args) : method.call(caller);
    }
    
    Timer数据类型定义
    /**@private */
    private static var _pool:Array = [];   //TimerHandler池
    
    /*[DISABLE-ADD-VARIABLE-DEFAULT-VALUE]*/
    /** 两帧之间的时间间隔,单位毫秒。*/
    private var _delta:int = 0;		
    /** 时针缩放。*/
    public var scale:Number = 1;
    /** 当前帧开始的时间。*/
    public var currTimer:Number = _now();
    /** 当前的帧数。*/
    public var currFrame:int = 0;
    /**@private */
    private var _lastTimer:Number = _now();	//上一帧的时间
    /**@private */
    private var _mid:int = 1;				//用于生成唯一key
    /**@private */
    /*[IF-FLASH]*/							//TimerHandler的map  用于查找Handler(包括Later的handler)
    private var _map:flash.utils.Dictionary = new flash.utils.Dictionary(true);
    //[IF-JS] private var _map:Array = [];
    /**@private */
    private var _laters:Array = [];			//存放callLater创建的handler
    /**@private */
    private var _handlers:Array = [];		//存放非callLater创建的handler,包括frameOnce、frameLoop、once、loop
    /**@private */
    private var _temp:Array = [];			//临时数组,用于清理
    /**@private */
    private var _count:int = 0;				//记录handler.method为空的数量,检测是否清理
    
    初始化
    /**@private */
    protected function _init():void {
    	Laya.timer && Laya.timer.frameLoop(1, this, _update);
    }
    

    除了Laya.timer之外,其他的timer的实例都是注册到Laya.timer的frameLoop中。

    _create方法
    • 创建对应的TimerHandler。
    • 如果coverBefore并且已经注册过相同Handler,则覆盖之前的handler
    • 设置对应参数,并计算下一次执行的时间。
    • 为Handler创建唯一key,添加到_map中,用于快速查找。
    • 将handler放到_handler数组中,用于_update时遍历。

    frameOnce、frameLoop、once、loop实现上都是调用_create方法,传入对应的参数,将handler注册到timer中,等待_update执行相关计算。

    /** @private */
    public function _create(useFrame:Boolean, repeat:Boolean, delay:int, caller:*, method:Function, args:Array, coverBefore:Boolean):TimerHandler {
    	//如果延迟为0,则立即执行
    	if (!delay) {
    		method.apply(caller, args);
    		return null;
    	}
    	
    	//先覆盖相同函数的计时
    	if (coverBefore) {
    		var handler:TimerHandler = _getHandler(caller, method);
    		if (handler) {
    			handler.repeat = repeat;
    			handler.userFrame = useFrame;
    			handler.delay = delay;
    			handler.caller = caller;
    			handler.method = method;
    			handler.args = args;
    			handler.exeTime = delay + (useFrame ? this.currFrame : this.currTimer + _now() - _lastTimer);
    			return handler;
    		}
    	}
    	
    	//找到一个空闲的timerHandler
    	handler = _pool.length > 0 ? _pool.pop() : new TimerHandler();
    	handler.repeat = repeat;
    	handler.userFrame = useFrame;
    	handler.delay = delay;
    	handler.caller = caller;
    	handler.method = method;
    	handler.args = args;
    	handler.exeTime = delay + (useFrame ? this.currFrame : this.currTimer + _now() - _lastTimer) + 1;
    	
    	//索引handler
    	_indexHandler(handler);
    	
    	//插入数组
    	_handlers.push(handler);
    	
    	return handler;
    }
    
    callLater
    • callLater使用延时执行,再Timer.update执行末尾调用。
    • 如果对应的caller和method已经注册过,则不会重复注册。
    • 创建的TimerHandler会放到_laters数组中。
    _update
    • 计算当前帧和当前时间。
    • 计算delta。
    • 遍历handler数组,检查时间/帧是否超过了handler的exeTime。
    • 超过时间handler执行回调,如果是once则将handler的caller和method设置为空;如果是loop则执行回调,并计算下次执行的时间。如果下次调用的时间间隔内,可以触发多次回调,默认情况下执行执行一次回调。如果设置为jumpFrame则会调用多次回调(如delay为1毫秒,delta为30毫秒,则下个update时,会调用30次回调)。
    • 遍历数组时,会记录method为空的handler的数量,当数量为30或者200帧时,会清理一次没用的handler,将没用的handler回收到池中。
    • 执行完所有handler之后,遍历所有laters数组,执行callLater所注册的回调。执行完成后,清理所有laters数组。
    /**
     * @private
     * 帧循环处理函数。
     */
    public function _update():void {
    	if (scale <= 0) {
    		_lastTimer =_now();
    		return;
    	}
    	var frame:int = this.currFrame = this.currFrame + scale;
    	var now:Number = _now();
    	_delta = (now - _lastTimer) * scale;
    	var timer:Number = this.currTimer = this.currTimer + _delta;
    	_lastTimer = now;
    	
    	//处理handler
    	var handlers:Array = this._handlers;
    	_count = 0;
    	for (i = 0, n = handlers.length; i < n; i++) {
    		handler = handlers[i];
    		if (handler.method !== null) {
    			var t:int = handler.userFrame ? frame : timer;
    			if (t >= handler.exeTime) {
    				if (handler.repeat) {
    					if (!handler.jumpFrame) {
    						handler.exeTime += handler.delay;
    						handler.run(false);
    						if (t > handler.exeTime) {
    							//如果执行一次后还能再执行,做跳出处理,如果想用多次执行,需要设置jumpFrame=true
    							handler.exeTime += Math.ceil((t - handler.exeTime) / handler.delay) * handler.delay;
    						}
    					} else {
    						while (t >= handler.exeTime) {
    							handler.exeTime += handler.delay;
    							handler.run(false);
    						}
    					}
    				} else {
    					handler.run(true);
    				}
    			}
    		} else {
    			_count++;
    		}
    	}
    	
    	if (_count > 30 || frame % 200 === 0) _clearHandlers();
    	
    	//处理callLater
    	var laters:Array = this._laters;
    	for (var i:int = 0, n:int = laters.length - 1; i <= n; i++) {
    		var handler:TimerHandler = laters[i];
    		if (handler.method !== null) {
    			/*[IF-FLASH]*/
    			_map[handler.method] = null;
    			//[IF-SCRIPT]_map[handler.key] = null;
    			handler.run(false);
    		}
    		_recoverHandler(handler);
    		i === n && (n = laters.length - 1);
    	}
    	laters.length = 0;
    }
    
    其他
    • Laya.timer._update是在Laya.stage.loop中调用,Laya.stage.loop是在Render中调用。

    • 在Frash中,监听enterFrame事件。

    • 在js中则是注册requestAnimationFrame,来进入渲染帧。

    • 当stage不可见时(如切到后台),则timer._update每一秒触发一次。一秒钟一帧,当切回前台后,帧率恢复正常。

    • update是在渲染之前执行。

    • Laya.stage控制实际帧率,根据Laya.stage.frameRate。

  • 相关阅读:
    欧拉公式
    isap的一些想法
    错误合集
    Hello World
    PAT (Advanced Level) Practice 1068 Find More Coins
    PAT (Advanced Level) 1087 All Roads Lead to Rome
    PAT (Advanced Level) 1075 PAT Judge
    PAT (Advanced Level) 1067 Sort with Swap(0, i)
    PAT (Advanced Level) 1017 Queueing at Bank
    PAT (Advanced Level) 1025 PAT Ranking
  • 原文地址:https://www.cnblogs.com/chiguozi/p/9755808.html
Copyright © 2011-2022 走看看