1.介绍
在游戏逻辑编写中定时器是必不可少的,接下来我就来解析ET6框架的定时器的原理和使用。
2.原理(TimerComponent.cs)
1.awake
将所有TimerAttribute的类型的类存入timerActions数组
public void Awake()
{
this.timerActions = new ITimer[TimeTypeMax];
//获取所有TimerAttribute的类型
HashSet<Type> types = Game.EventSystem.GetTypes(typeof (TimerAttribute));
foreach (Type type in types)
{
ITimer iTimer = Activator.CreateInstance(type) as ITimer;
if (iTimer == null)
{
Log.Error($"Timer Action {type.Name} 需要继承 ITimer");
continue;
}
object[] attrs = type.GetCustomAttributes(typeof(TimerAttribute), false);
if (attrs.Length == 0)
{
continue;
}
//将TimerAttribute的类保存在timerActions数组
foreach (object attr in attrs)
{
TimerAttribute timerAttribute = attr as TimerAttribute;
this.timerActions[timerAttribute.Type] = iTimer;
}
}
}
2.WaitTillAsync/WaitFrameAsync/WaitAsync/NewOnceTimer/NewFrameTimer/NewRepeatedTimer(以WaitAsync为例子)
创建TimerAction到子节点,并添加timer到TimeId上
public async ETTask<bool> WaitAsync(long time, ETCancellationToken cancellationToken = null)
{
if (time == 0)
{
return true;
}
long tillTime = TimeHelper.ServerNow() + time;
ETTask<bool> tcs = ETTask<bool>.Create(true);
//在子节点创建TimerAction
TimerAction timer = this.AddChild<TimerAction, TimerClass, long, int, object>(TimerClass.OnceWaitTimer, time, 0, tcs, true);
//添加到TimeId上
this.AddTimer(tillTime, timer);
long timerId = timer.Id;
//移除timer并且返回false
void CancelAction()
{
if (this.Remove(timerId))
{
tcs.SetResult(false);
}
}
bool ret;
//cancellationToken不为null就添加CancelAction方法
try
{
cancellationToken?.Add(CancelAction);
ret = await tcs;
}
finally
{
cancellationToken?.Remove(CancelAction);
}
return ret;
}
3.Update
每帧刷新,将超时的timer执行run方法并从TimeId中移除
public void Update()
{
if (this.TimeId.Count == 0)
{
return;
}
long timeNow = TimeHelper.ServerNow();
if (timeNow < this.minTime)
{
return;
}
//到时间的timer压入timeOutTime队列
foreach (KeyValuePair<long, List<long>> kv in this.TimeId)
{
long k = kv.Key;
if (k > timeNow)
{
minTime = k;
break;
}
this.timeOutTime.Enqueue(k);
}
//将到时的的timerId压入timeOutTimerIds队列并从TimeId移除该timer
while (this.timeOutTime.Count > 0)
{
long time = this.timeOutTime.Dequeue();
foreach (long timerId in this.TimeId[time])
{
this.timeOutTimerIds.Enqueue(timerId);
}
this.TimeId.Remove(time);
}
//执行所有到时的计时器的timerAction
while (this.timeOutTimerIds.Count > 0)
{
long timerId = this.timeOutTimerIds.Dequeue();
TimerAction timerAction = this.GetChild<TimerAction>(timerId);
if (timerAction == null)
{
continue;
}
//执行timer标签类的run方法
Run(timerAction);
}
}
4.Run
执行timer的run方法
private void Run(TimerAction timerAction)
{
switch (timerAction.TimerClass)
{
case TimerClass.OnceTimer://回调等待,需要自己移除timer(单次)
{
int type = timerAction.Type;
ITimer timer = this.timerActions[type];
if (timer == null)
{
Log.Error($"not found timer action: {type}");
return;
}
timer.Handle(timerAction.Object);
break;
}
case TimerClass.OnceWaitTimer://携程等待,自动移除timer并返回true(单次)
{
ETTask<bool> tcs = timerAction.Object as ETTask<bool>;
this.Remove(timerAction.Id);
tcs.SetResult(true);
break;
}
case TimerClass.RepeatedTimer://回调等待,需要自己移除timer(多次)
{
int type = timerAction.Type;
long tillTime = TimeHelper.ServerNow() + timerAction.Time;
this.AddTimer(tillTime, timerAction);
ITimer timer = this.timerActions[type];
if (timer == null)
{
Log.Error($"not found timer action: {type}");
return;
}
timer.Handle(timerAction.Object);
break;
}
}
}
5.Remove
移除定时器
public bool Remove(ref long id)
{
long i = id;
id = 0;
return this.Remove(i);
}
private bool Remove(long id)
{
if (id == 0)
{
return false;
}
//从子节点移除TimerAction
TimerAction timerAction = this.GetChild<TimerAction>(id);
if (timerAction == null)
{
return false;
}
timerAction.Dispose();
return true;
}
3.使用
1.打上timer标签,继承ATimer,并重写Run方法
[Timer(TimerType.AITimer)]
public class AITimer: ATimer<AIComponent>
{
public override void Run(AIComponent self)
{
//方法
}
}
2.注册定时器
self.Timer = TimerComponent.Instance.NewRepeatedTimer(1000, TimerType.AITimer, self);
//中途可取消
bool timeRet = await TimerComponent.Instance.WaitAsync(1000, cancellationToken);
3.销毁定时器
TimerComponent.Instance.Remove(ref self.Timer);
self.CancellationToken?.Cancel();
self.CancellationToken = null;