一 自定义计时器
因为游戏中经常用到计时器,比如每1秒发射一枚子弹啊,每2秒怪物AI自动转向啊
每次去new Timer 然后addEventListener(egret.TimerEvent... 之类的太麻烦了。
所以自定义一个计时器
二 决战沙城的自定义计时器
来看看决战沙城的自定义计时器
原理就是egret.Ticker每帧执行,让列表里TimerHandler加上时间或帧,检查当到了计时时间结束时,就执行回调。
/** * Created by yangsong on 2014/11/23. * Timer管理器 */ class TimerManager extends SingtonClass { private _handlers: Array<TimerHandler>; private _delHandlers: Array<TimerHandler>; private _currTime: number; private _currFrame: number; private _count: number; private _timeScale: number; private _isPause: boolean; private _pauseTime: number; /** * 构造函数 */ public constructor() { super(); this._handlers = new Array<TimerHandler>(); this._delHandlers = new Array<TimerHandler>(); this._currTime = egret.getTimer(); this._currFrame = 0; this._count = 0; this._timeScale = 1; egret.Ticker.getInstance().register(this.onEnterFrame, this); } /** * 设置时间参数 * @param timeScale */ public setTimeScale(timeScale: number): void { this._timeScale = timeScale; } /** * 每帧执行函数 * @param frameTime */ private onEnterFrame(): void { if (this._isPause) { return; } this._currFrame++; this._currTime = egret.getTimer(); App.DebugUtils.start("TimerManager:"); while (this._delHandlers.length) { this.removeHandle(this._delHandlers.pop()); } for (var i: number = 0; i < this._count; i++) { var handler: TimerHandler = this._handlers[i]; if (this._delHandlers.indexOf(handler) != -1) { continue; } var t: number = handler.userFrame ? this._currFrame : this._currTime; if (t >= handler.exeTime) { App.DebugUtils.start(handler.method.toString()); handler.method.call(handler.methodObj, (this._currTime - handler.dealTime) * this._timeScale); App.DebugUtils.stop(handler.method.toString()); handler.dealTime = this._currTime; handler.exeTime += handler.delay; if (!handler.repeat) { if (handler.repeatCount > 1) { handler.repeatCount--; } else { if (handler.complateMethod) { handler.complateMethod.apply(handler.complateMethodObj); } if (this._delHandlers.indexOf(handler) == -1) { this._delHandlers.push(handler); } } } } } App.DebugUtils.stop("TimerManager:"); } private removeHandle(handler: TimerHandler): void { var i = this._handlers.indexOf(handler); if (i == -1) { Log.warn("what????"); return; } this._handlers.splice(i, 1); ObjectPool.push(handler); this._count--; } private create(useFrame: boolean, delay: number, repeatCount: number, method: Function, methodObj: any, complateMethod: Function, complateMethodObj: any): void { //参数监测 if (delay < 0 || repeatCount < 0 || method == null) { return; } //先删除相同函数的计时 this.remove(method, methodObj); //创建 var handler: TimerHandler = ObjectPool.pop("TimerHandler"); handler.userFrame = useFrame; handler.repeat = repeatCount == 0; handler.repeatCount = repeatCount; handler.delay = delay; handler.method = method; handler.methodObj = methodObj; handler.complateMethod = complateMethod; handler.complateMethodObj = complateMethodObj; handler.exeTime = delay + (useFrame ? this._currFrame : this._currTime); handler.dealTime = this._currTime; this._handlers.push(handler); this._count++; } /** * 在指定的延迟(以毫秒为单位)后运行指定的函数。 * @param delay 执行间隔:毫秒 * @param method 执行函数 * @param methodObj 执行函数所属对象 */ public setTimeOut(delay: number, method: Function, methodObj: any): void { this.doTimer(delay, 1, method, methodObj); } /** * 在指定的帧后运行指定的函数。 * @param delay 执行间隔:帧频 * @param method 执行函数 * @param methodObj 执行函数所属对象 */ public setFrameOut(delay: number, method: Function, methodObj: any): void { this.doFrame(delay, 1, method, methodObj); } /** * * 定时执行 * @param delay 执行间隔:毫秒 * @param repeatCount 执行次数, 0为无限次 * @param method 执行函数 * @param methodObj 执行函数所属对象 * @param complateMethod 完成执行函数 * @param complateMethodObj 完成执行函数所属对象 * */ public doTimer(delay: number, repeatCount: number, method: Function, methodObj: any, complateMethod: Function = null, complateMethodObj: any = null): void { this.create(false, delay, repeatCount, method, methodObj, complateMethod, complateMethodObj); } /** * * 定时执行 * @param delay 执行间隔:帧频 * @param repeatCount 执行次数, 0为无限次 * @param method 执行函数 * @param methodObj 执行函数所属对象 * @param complateMethod 完成执行函数 * @param complateMethodObj 完成执行函数所属对象 * */ public doFrame(delay: number, repeatCount: number, method: Function, methodObj: any, complateMethod: Function = null, complateMethodObj: any = null): void { this.create(true, delay, repeatCount, method, methodObj, complateMethod, complateMethodObj); } /** * 定时器执行数量 * @return * */ public get count(): number { return this._count; } /** * 清理 * @param method 要移除的函数 * @param methodObj 要移除的函数对应的对象 */ public remove(method: Function, methodObj: any): void { for (var i: number = 0; i < this._count; i++) { var handler: TimerHandler = this._handlers[i]; if (handler.method == method && handler.methodObj == methodObj && this._delHandlers.indexOf(handler) == -1) { this._delHandlers.push(handler); break; } } } /** * 清理 * @param methodObj 要移除的函数对应的对象 */ public removeAll(methodObj: any): void { for (var i: number = 0; i < this._count; i++) { var handler: TimerHandler = this._handlers[i]; if (handler.methodObj == methodObj && this._delHandlers.indexOf(handler) == -1) { this._delHandlers.push(handler); } } } /** * 检测是否已经存在 * @param method * @param methodObj * */ public isExists(method: Function, methodObj: any): boolean { for (var i: number = 0; i < this._count; i++) { var handler: TimerHandler = this._handlers[i]; if (handler.method == method && handler.methodObj == methodObj && this._delHandlers.indexOf(handler) == -1) { return true; } } return false; } /** * 暂停 */ public pause(): void { if (this._isPause) { return; } this._isPause = true; this._pauseTime = egret.getTimer(); } /** * 从暂停中恢复 */ public resume(): void { if (!this._isPause) { return; } this._isPause = false; this._currTime = egret.getTimer(); var gap = this._currTime - this._pauseTime; for (var i: number = 0; i < this._count; i++) { var handler: TimerHandler = this._handlers[i]; handler.dealTime += gap; if (!handler.userFrame) { handler.exeTime += gap; } } } } class TimerHandler { /**执行间隔*/ public delay: number = 0; /**是否重复执行*/ public repeat: boolean; /**重复执行次数*/ public repeatCount: number = 0; /**是否用帧率*/ public userFrame: boolean; /**执行时间*/ public exeTime: number = 0; /**处理函数*/ public method: Function; /**处理函数所属对象*/ public methodObj: any; /**完成处理函数*/ public complateMethod: Function; /**完成处理函数所属对象*/ public complateMethodObj: any; /**上次的执行时间*/ public dealTime: number = 0; /**清理*/ public clear(): void { this.method = null; this.methodObj = null; this.complateMethod = null; this.complateMethodObj = null; } }
三 Laya的timer
laya已经提供了一个timer给开发者使用,功能和决战沙城的差不多
由于Laya用的Date.now,那么在游戏置于后台过久,再返回前台时,会导致时间相差巨大,执行很多次回调。所以Laya做了额外处理。
但是egret使用的egret.ticker,置于后台时,egret.ticker是停跳了的,所以不用处理时间相差巨大的问题。
class Timer { constructor(autoActive = true) { this.scale = 1; this.currTimer = Date.now(); this.currFrame = 0; this._delta = 0; this._lastTimer = Date.now(); this._map = []; this._handlers = []; this._temp = []; this._count = 0; autoActive && Timer.gSysTimer && Timer.gSysTimer.frameLoop(1, this, this._update); } get delta() { return this._delta; } _update() { if (this.scale <= 0) { this._lastTimer = Date.now(); this._delta = 0; return; } var frame = this.currFrame = this.currFrame + this.scale; var now = Date.now(); var awake = (now - this._lastTimer) > 30000; this._delta = (now - this._lastTimer) * this.scale; var timer = this.currTimer = this.currTimer + this._delta; this._lastTimer = now; var handlers = this._handlers; this._count = 0; for (var i = 0, n = handlers.length; i < n; i++) { var handler = handlers[i]; if (handler.method !== null) { var t = handler.userFrame ? frame : timer; if (t >= handler.exeTime) { if (handler.repeat) { if (!handler.jumpFrame || awake) { handler.exeTime += handler.delay; handler.run(false); if (t > handler.exeTime) { 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 { this._count++; } } if (this._count > 30 || frame % 200 === 0) this._clearHandlers(); } _clearHandlers() { var handlers = this._handlers; for (var i = 0, n = handlers.length; i < n; i++) { var handler = handlers[i]; if (handler.method !== null) this._temp.push(handler); else this._recoverHandler(handler); } this._handlers = this._temp; handlers.length = 0; this._temp = handlers; } _recoverHandler(handler) { if (this._map[handler.key] == handler) this._map[handler.key] = null; handler.clear(); Timer._pool.push(handler); } _create(useFrame, repeat, delay, caller, method, args, coverBefore) { if (!delay) { method.apply(caller, args); return null; } if (coverBefore) { var handler = this._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 + Date.now() - this._lastTimer); return handler; } } handler = Timer._pool.length > 0 ? Timer._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 + Date.now() - this._lastTimer); this._indexHandler(handler); this._handlers.push(handler); return handler; } _indexHandler(handler) { var caller = handler.caller; var method = handler.method; var cid = caller ? caller.$_GID || (caller.$_GID = ILaya.Utils.getGID()) : 0; var mid = method.$_TID || (method.$_TID = (Timer._mid++) * 100000); handler.key = cid + mid; this._map[handler.key] = handler; } once(delay, caller, method, args = null, coverBefore = true) { this._create(false, false, delay, caller, method, args, coverBefore); } loop(delay, caller, method, args = null, coverBefore = true, jumpFrame = false) { var handler = this._create(false, true, delay, caller, method, args, coverBefore); if (handler) handler.jumpFrame = jumpFrame; } frameOnce(delay, caller, method, args = null, coverBefore = true) { this._create(true, false, delay, caller, method, args, coverBefore); } frameLoop(delay, caller, method, args = null, coverBefore = true) { this._create(true, true, delay, caller, method, args, coverBefore); } toString() { return " handlers:" + this._handlers.length + " pool:" + Timer._pool.length; } clear(caller, method) { var handler = this._getHandler(caller, method); if (handler) { this._map[handler.key] = null; handler.key = 0; handler.clear(); } } clearAll(caller) { if (!caller) return; for (var i = 0, n = this._handlers.length; i < n; i++) { var handler = this._handlers[i]; if (handler.caller === caller) { this._map[handler.key] = null; handler.key = 0; handler.clear(); } } } _getHandler(caller, method) { var cid = caller ? caller.$_GID || (caller.$_GID = ILaya.Utils.getGID()) : 0; var mid = method.$_TID || (method.$_TID = (Timer._mid++) * 100000); return this._map[cid + mid]; } callLater(caller, method, args = null) { CallLater.I.callLater(caller, method, args); } runCallLater(caller, method) { CallLater.I.runCallLater(caller, method); } runTimer(caller, method) { var handler = this._getHandler(caller, method); if (handler && handler.method != null) { this._map[handler.key] = null; handler.run(true); } } pause() { this.scale = 0; } resume() { this.scale = 1; } } Timer.gSysTimer = null; Timer._pool = []; Timer._mid = 1; class TimerHandler { clear() { this.caller = null; this.method = null; this.args = null; } run(withClear) { var caller = this.caller; if (caller && caller.destroyed) return this.clear(); var method = this.method; var args = this.args; withClear && this.clear(); if (method == null) return; args ? method.apply(caller, args) : method.call(caller); } }