基于Unity的定时器封装
一.封装原因:
在unity中有多种方法能实现定时器的效果,比如在Update中累加Time.deltaTime,或者是通过Time.time,亦或者通过协程yield return new WaitForSeconds()来实现,这些方法十分简单,在代码简洁时还能应付过来,当代码数量多了时,这些方法就会显得过于冗杂,这时就需要一个定时器系统来管理这些定时的任务。
二.代码实现:
1.整体类结构
(1.图中的TimerTask类是定时的任务
(2.图中的TimeSys类是管理Time的系统
(3.图中的TimeTest类用来测试
2.TimerTask具体内容
TimerTask是关于时间定时的任务
FrameTask是关于帧定时的任务
TimeType是时间定时的时间单位
using UnityEngine;
using System;
public class TimerTask
{
public Action callback;
public float bsetTime;
public int currNum;
public float delayTime;
public int tID;
}
public class FrameTask
{
public Action callback;
public int bsetTime;
public int currNum;
public int delayTime;
public int tID;
}
public enum TimeType
{
Millisecond,
Second,
Minute,
Hour,
Day
}
3.TimeSys具体实现
用两个列表来存储各自的任务
还有一个tIDs列表来存储任务对应的ID
tID用来存储当前的任务ID,以便每次添加定时任务时给定时任务唯一的ID
lock是为了防止在多线程中对tID进行改变
frame是用来记录游戏开始到现在的帧数
-----------------------------------------方法说明-----------------------------------------------
TimeFunc和FrameFunc都是在Update中判断当前的时间或帧数是否满足任务执行需要的帧数(类似于Time.time计时器方法)
Add,Del,RepTimeTask分别为添加定时任务,删除定时任务,替换定时任务(下面的帧相关的3个方法同理)
GetTID是用来生成任务唯一的ID号
下面为TimeSys完整代码
using System.Collections.Generic;
using UnityEngine;
using System;
public class TimeSys : MonoBehaviour
{
private static TimeSys instance;
public static TimeSys Instance
{
get
{
if (instance == null)
{
instance = GameObject.FindObjectOfType<TimeSys>();
}
return instance;
}
set
{
if (instance == null)
{
instance = value;
}
}
}
public void Start()
{
Instance = this;
}
private List<TimerTask> timerTasks = new List<TimerTask>();
private List<FrameTask> frameTasks = new List<FrameTask>();
private List<int> tIDs = new List<int>();
private int tID;
private static readonly string obj = "lock";
private int frame;
private void Update()
{
frame++;
TimeFunc();
FrameFunc();
}
private void TimeFunc()
{
for (int i = 0; i < timerTasks.Count; i++)
{
if (timerTasks[i].bsetTime < Time.realtimeSinceStartup * 1000)
{
if (timerTasks[i].callback != null)
{
timerTasks[i].callback();
timerTasks[i].bsetTime = Time.realtimeSinceStartup * 1000f + timerTasks[i].delayTime;
if (timerTasks[i].currNum == -1)
{
continue;
}
else
{
timerTasks[i].currNum--;
if (timerTasks[i].currNum == 0)
{
timerTasks.RemoveAt(i);
i--;
}
}
}
}
}
}
private void FrameFunc()
{
for (int i = 0; i < frameTasks.Count; i++)
{
if (frameTasks[i].bsetTime < frame)
{
if (frameTasks[i].callback != null)
{
frameTasks[i].callback();
frameTasks[i].bsetTime = frame + frameTasks[i].delayTime;
if (frameTasks[i].currNum == -1)
{
continue;
}
else
{
frameTasks[i].currNum--;
if (frameTasks[i].currNum == 0)
{
frameTasks.RemoveAt(i);
i--;
}
}
}
}
}
}
#region 时间相关
/// <summary>
///
/// </summary>
/// <param name="delayTime"></param>
/// <param name="action"></param>
/// <param name="timeType"></param>
/// <param name="num">-1为无限循环,其余整数为循环次数</param>
public int AddTimerTask(float delayTime, Action action, TimeType timeType = TimeType.Millisecond, int num = 1)
{
if (timeType != TimeType.Millisecond)
{
switch (timeType)
{
case TimeType.Second:
delayTime *= 1000;
break;
case TimeType.Minute:
delayTime *= 60;
break;
case TimeType.Hour:
delayTime *= 3600;
break;
case TimeType.Day:
delayTime *= (3600 * 24);
break;
default:
break;
}
}
int id = GetTID();
TimerTask timerTask = new TimerTask { bsetTime = Time.realtimeSinceStartup * 1000f + delayTime, callback = action, currNum = num, delayTime = delayTime };
timerTask.tID = id;
timerTasks.Add(timerTask);
tIDs.Add(id);
return timerTask.tID;
}
public bool DelTimerTask(int tID)
{
bool isSucced = false;
for (int i = 0; i < timerTasks.Count; i++)
{
if (timerTasks[i].tID == tID)
{
timerTasks.RemoveAt(i);
isSucced = true;
tIDs.Remove(tID);
break;
}
}
return isSucced;
}
public bool RepTimerTask(int tID, float delayTime, Action action, TimeType timeType = TimeType.Millisecond, int num = 1)
{
bool isSucced = false;
for (int i = 0; i < timerTasks.Count; i++)
{
if (timerTasks[i].tID == tID)
{
if (timeType != TimeType.Millisecond)
{
switch (timeType)
{
case TimeType.Second:
delayTime *= 1000;
break;
case TimeType.Minute:
delayTime *= 60;
break;
case TimeType.Hour:
delayTime *= 3600;
break;
case TimeType.Day:
delayTime *= (3600 * 24);
break;
default:
break;
}
}
TimerTask timerTask = new TimerTask { bsetTime = Time.realtimeSinceStartup * 1000f + delayTime, callback = action, currNum = num, delayTime = delayTime };
timerTask.tID = GetTID();
timerTasks[i] = timerTask;
isSucced = true;
}
}
return isSucced;
}
#endregion
#region 帧相关
public int AddFrameTimerTask(int delayTime, Action action, int num = 1)
{
int id = GetTID();
FrameTask timerTask = new FrameTask { bsetTime = frame + delayTime, callback = action, currNum = num, delayTime = delayTime };
timerTask.tID = id;
frameTasks.Add(timerTask);
tIDs.Add(id);
return timerTask.tID;
}
public bool DelFrameTimerTask(int tID)
{
bool isSucced = false;
for (int i = 0; i < frameTasks.Count; i++)
{
if (frameTasks[i].tID == tID)
{
frameTasks.RemoveAt(i);
isSucced = true;
tIDs.Remove(tID);
break;
}
}
return isSucced;
}
public bool RepFrameTimerTask(int tID, int delayTime, Action action, int num = 1)
{
bool isSucced = false;
for (int i = 0; i < frameTasks.Count; i++)
{
FrameTask timerTask = new FrameTask { bsetTime = frame + delayTime, callback = action, currNum = num, delayTime = delayTime };
timerTask.tID = GetTID();
frameTasks[i] = timerTask;
isSucced = true;
}
return isSucced;
}
#endregion
#region 通用生成ID
private int GetTID()
{
lock (obj)
{
tID++;
while (true)
{
if (tID == int.MaxValue)
{
tID = 0;
}
bool used = false;
for (int i = 0; i < tIDs.Count; i++)
{
if (tIDs[i] == tID)
{
used = true;
break;
}
}
if (!used)
{
break;
}
tID++;
}
}
return tID;
}
#endregion
}
最后测试一下定时器
public class TimeTest : MonoBehaviour
{
private int tID;
private void Start()
{
tID = TimeSys.Instance.AddFrameTimerTask(1, SayHello,-1);
}
private void SayHello()
{
Debug.Log("Hello");
}
private void Update()
{
if (Input.GetKeyDown(KeyCode.Space))
{
TimeSys.Instance.RepFrameTimerTask(tID, 200, () => Debug.Log("Over..."), 5);
}
}
}
运行后unity每1帧打印一条“Hello”,当我按下空格时,每过200帧打印一句Over…,一共打印了5次。
这样一个计时器的封装就完成了!