谈到链式编程,大家都会想到方法扩展,具象一些,就是想到DoTween,但是DoTween链式思想里面还有个很有意思的分支,那就是
委托+方法+返回This
完成的链式编程。本文重点来了,那就是学习一下这个思路实现一个小巧实用的定时器。
代码
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace TimeTrigger
{
public class Timer
{
static List timers = new List();
Action UpdateEvent;
Action EndEvent;
private float _time = -1; // 用户设定的定时时长
private bool _loop; // 是否循环执行
private bool _ignorTimescale; // 是否忽略Timescale
private string _flag;// 用户指定的定时器标志,便于手动清除、暂停、恢复
private static TimerDriver driver = null;//拿驱动器的引用只是为了初始化驱动器
private float CurrentTime { get { return _ignorTimescale ? Time.realtimeSinceStartup : Time.time; } }// 获得当前时间
private float cachedTime;//缓存时间
float timePassed; //已经过去的时间
private bool _isFinish = false; //计时器是否结束
private bool _isPause = false; //计时器是否暂停
private static bool showLog = true; //确认是否输出Debug信息
public static bool ShowLog { set { showLog = value; } }
public bool IsPause // 暂停计时器
{
get { return _isPause; }
set
{
if (value)
{
Pause();
}
else
{
Resum();
}
}
}
///
/// 构造定时器
///
/// 定时时长
/// 定时器标识符
/// 是否循环
/// 是否忽略TimeScale
private Timer(float time, string flag, bool loop = false, bool ignorTimescale = true)
{
if (null == driver) driver = TimerDriver.Get; //初始化Time驱动
_time = time;
_loop = loop;
_ignorTimescale = ignorTimescale;
cachedTime = CurrentTime;
if (timers.Exists((v) => { return v._flag == flag; }))
{
if (showLog) Debug.LogWarningFormat("【TimerTrigger(容错)】:存在相同的标识符【{0}】!", flag);
}
_flag = string.IsNullOrEmpty(flag) ? GetHashCode().ToString() : flag;//设置辨识标志符
}
private void Pause() // 暂停计时
{
if (!_isFinish)
{
_isPause = true;
}
}
private void Resum() // 继续计时
{
if (!_isFinish&&_isPause)
{
cachedTime = CurrentTime-timePassed;
_isPause = false;
}
}
private void Update() // 刷新定时器
{
if (!_isFinish && !_isPause) //运行中
{
timePassed = CurrentTime - cachedTime;
if (null != UpdateEvent) UpdateEvent(Mathf.Clamp01(timePassed / _time));
if (timePassed >= _time)
{
if (null != EndEvent) EndEvent();
if (_loop)
{
cachedTime = CurrentTime;
}
else
{
Stop();
}
}
}
}
private void Stop() // 回收定时器
{
if (timers.Contains(this))
{
timers.Remove(this);
}
_time = -1;
_isFinish = true;
_isPause = false;
UpdateEvent = null;
EndEvent = null;
}
#region--------------------------静态方法扩展-------------------------------------
#region-------------添加定时器---------------
///
/// 添加定时触发器
///
/// 定时时长
/// 定时器标识符
/// 是否循环
/// 是否忽略TimeScale
public static Timer AddTimer(float time, string flag = "", bool loop = false, bool ignorTimescale = true)
{
Timer timer = new Timer(time, flag, loop, ignorTimescale);
timers.Add(timer);
return timer;
}
#endregion
#region-------------刷新所有定时器---------------
public static void UpdateAllTimer()
{
for (int i = 0; i < timers.Count; i++)
{
if (null != timers[i])
{
timers[i].Update();
}
}
}
#endregion
#region-------------暂停和恢复定时器---------------
///
/// 暂停用户指定的计时触发器
///
/// 指定的标识符
public static void PauseTimer(string flag)
{
Timer timer = timers.Find((v) => { return v._flag == flag; });
if (null != timer)
{
timer.Pause();
}
}
///
/// 恢复用户指定的计时触发器
///
/// 指定的标识符
public static void ResumTimer(string flag)
{
Timer timer = timers.Find((v) => { return v._flag == flag; });
if (null != timer)
{
timer.Resum();
}
}
#endregion
#region-------------删除定时器---------------
///
/// 删除用户指定的计时触发器
///
/// 指定的标识符
public static void DelTimer(string flag)
{
Timer timer = timers.Find((v) => { return v._flag == flag; });
if (null != timer)
{
timer.Stop();
}
}
///
/// 删除用户指定的计时触发器
///
/// 指定的定时器
public static void DelTimer(Timer timer)
{
if (timers.Contains(timer))
{
timer.Stop();
}
}
///
/// 删除用户指定的计时触发器
///
/// 指定的完成事件(直接赋值匿名函数无效)
public static void DelTimer(Action completedEvent)
{
Timer timer = timers.Find((v) => { return v.EndEvent == completedEvent; });
if (null != timer)
{
timer.Stop();
}
}
///
/// 删除用户指定的计时触发器
///
/// 指定的Update事件(直接赋值匿名函数无效)
public static void DelTimer(Action updateEvent)
{
Timer timer = timers.Find((v) => { return v.UpdateEvent == updateEvent; });
if (null != timer)
{
timer.Stop();
}
}
#endregion
#endregion
#region-------------添加事件-------------------
public Timer OnCompleted(Action completedEvent) //添加完成事件
{
if (null == EndEvent)
{
EndEvent = completedEvent;
}
return this;
}
public Timer OnUpdated(Action updateEvent) //添加update更新事件
{
if (null == UpdateEvent)
{
UpdateEvent = updateEvent;
}
return this;
}
#endregion
#region ---------------运行中的定时器参数修改-----------
public void Setloop(bool loop) // 设置运行中的定时器的loop状态
{
if (!_isFinish)
{
_loop = loop;
}
}
public void SetIgnoreTimeScale(bool ignoreTimescale)// 设置运行中的定时器的ignoreTimescale状态
{
if (!_isFinish)
{
_ignorTimescale = ignoreTimescale;
}
}
#endregion
}
public class TimerDriver : MonoBehaviour
{
#region 单例
private static TimerDriver _instance;
public static TimerDriver Get
{
get
{
if (null == _instance)
{
_instance = FindObjectOfType() ?? new GameObject("TimerEntity").AddComponent();
}
return _instance;
}
}
private void Awake()
{
_instance = this;
}
#endregion
private void Update()
{
Timer.UpdateAllTimer();
}
}
}
Tips:上面脚本为了节省篇幅,删除了很多注释和输出Log信息,可以下载下面的Demo体验更好,Demo含示例。
使用示例:
Tips:使用
Timer.DelTimer("12545");
即可随时停止计时器,当然,DelTimer也提供了多个重载。
动画示例:
Tips: 演示了延时+是否循环+是否忽略TimeScale+回调,当然也包含Lambda表达式支持下的多参数回调。
Tips:由于所有定时器的驱动是同一个,并非Itween的那种对谁起作用就绑定在谁身上驱动。所以,如果回调中用到了游戏实体对象且对象总是显示隐藏不停切换的话,建议定时器也随之暂停和恢复.避免游戏对象不可见了,定时器依旧触发的尴尬局面(Itween是随着游戏对象的显示隐藏自动暂停和恢复的,毕竟就在那个游戏对象上驱动嘛)
写到最后:
这个定时器使用了静态的管理器,所以请养成销毁游戏对象时主动移除事件的好习惯哈~
这个计时核心算法(假装是算法哈)除了上面这个方案,还有-=(+=)Time.deltatime,还有 协程
说道协程,这里笔者也不是不想封装,有,只是后来发现协程的频繁开启停止发现有安全问题。所以摒弃了。
再说协程怎么点对点关闭呢,刚刚好,关于协程管理的笔者也恰写了点文字可以挪步瞧一瞧的;
Unity3D 协程管理
Unity3D IEnumerator 做一个通用的定时器
笔者编程实数小菜级别,互相学习咯。
Demo下载:https://pan.baidu.com/s/1pHSX6-6cTfI6KEKy2xqKmA
扩展阅读
【大话链式编程之定时器Timer(四)】-开发者专栏-【游戏蛮牛】,有木有很熟悉的感觉?嗯哼,就是本文的详尽版啦~
标签: 定时器,定时触发器, 链式编程,Timer ,TimeTrigger