TimeManager 进阶优化
TimeManager 进阶优化
问题回顾
目前的计时器管理器会受到 Time.timeScale 的影响,我们应该如何让计时器管理器可以自己决定是否受到它的影响呢?
必备知识点
Unity中 在协同程序中使用 yield return new WaitForSeconds() 会受到Time.timeScale
影响
但是Unity也提供了不受其影响的 yield return new WaitForSecondsRealtime()
我们将利用它制作不受timeScale
影响的计时器
计时器模块 进阶优化
主要制作思路:
在计时器模块中,保留之前的受 Time.timeScale
影响的计时器
并添加一种不受其影响的计时器,让开发者可以根据需求选择使用
主要修改处:
-
添加一个字典容器专门记录不受其影响的计时器
1
2
3
4/// <summary>
/// 用于管理所有不受Time.timeScale影响的字典容器
/// </summary>
private Dictionary<int, TimerItem> realTimerDic = new Dictionary<int, TimerItem>(); -
多开一个协同程序专门用于处理不受其影响的计时器
至于协同程序,我们可以在原来的基础上复用,添加不受
Time.timeScale
影响的挂起和遍历realTimerDic
的分支,
因此,需要添加 是否是不受Time.timeScale
影响的参数 和 要遍历哪个字典容器的参数1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71/// <summary>
/// 用于管理所有计时器的字典容器
/// </summary>
private Dictionary<int, TimerItem> timerDic = new Dictionary<int, TimerItem>();
/// <summary>
/// 用于管理所有不受Time.timeScale影响的字典容器
/// </summary>
private Dictionary<int, TimerItem> realTimerDic = new Dictionary<int, TimerItem>();
/// <summary>
/// 待移除的列表
/// </summary>
private List<TimerItem> delList = new List<TimerItem>();
/// <summary>
/// 计时器管理器中唯一计时用的协程的间隔时间
/// </summary>
private const float intervalTimeSec = 0.1f;
//为了避免内存的浪费,我们可以直接提前声明成员变量
private readonly WaitForSecondsRealtime waitForSecondsRealtime = new WaitForSecondsRealtime(intervalTimeSec);
private readonly WaitForSeconds waitForSeconds = new WaitForSeconds(intervalTimeSec);
//计时用的协程
private IEnumerator StartTiming(bool isRealTime, Dictionary<int, TimerItem> timerDic)
{
while (true)
{
//每隔一段时间进行一次计时
if (isRealTime)
yield return waitForSecondsRealtime;
else
yield return waitForSeconds;
//遍历所有的计时器,进行数据更新
foreach (TimerItem item in timerDic.Values)
{
//如果计时器需要计时,才执行后边的逻辑
if (!item.isRunning)
continue;
//判断计时对象是否有间隔时间执行的需求
if (item.callBack != null)
{
//减去经过的间隔时间
item.intervalTimeMiniSec -= (int)(intervalTimeSec * 1000);
if (item.intervalTimeMiniSec <= 0)
{
item.callBack.Invoke();
item.intervalTimeMiniSec = item.maxIntervalTimeMiniSec;
}
}
//总的时间更新
item.allTimeMiniSec -= (int)(intervalTimeSec * 1000);
//计时时间到,需要执行完成回调函数
if (item.allTimeMiniSec <= 0)
{
item.overCallBack.Invoke();
delList.Add(item);
}
}
//移除待移除列表中的数据
for (int i = 0; i < delList.Count; i++)
{
//从字典中移除
timerDic.Remove(delList[i].keyID);
//放入到缓存池中
PoolManager.Instance.PushObj(delList[i]);
}
//移除结束后清空列表
delList.Clear();
}
} -
修改相关方法
-
开启计时和关闭计时
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16private Coroutine timer;
private Coroutine realtimer;
//开启计时器管理器的方法
public void Start()
{
timer = MonoManager.Instance.StartCoroutine(StartTiming(false, timerDic));
realtimer = MonoManager.Instance.StartCoroutine(StartTiming(true, realTimerDic));
}
//关闭计时器管理器的方法
public void Stop()
{
MonoManager.Instance.StopCoroutine(timer);
MonoManager.Instance.StopCoroutine(realtimer);
} -
其他相关方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97/// <summary>
/// 用于管理所有计时器的字典容器
/// </summary>
private Dictionary<int, TimerItem> timerDic = new Dictionary<int, TimerItem>();
/// <summary>
/// 用于管理所有不受Time.timeScale影响的字典容器
/// </summary>
private Dictionary<int, TimerItem> realTimerDic = new Dictionary<int, TimerItem>();
/// <summary>
/// 创建单个计时器
/// </summary>
/// <param name="isRealTime">是否是不受TimeScale的计时</param>
/// <param name="allTimeMiniSec">总延迟时间(单位:毫秒)</param>
/// <param name="overCallBack">总时间结束回调</param>
/// <param name="intervalTimeMiniSec">间隔计时时间(单位:毫秒)</param>
/// <param name="callBack">间隔计时时间结束回调</param>
/// <returns>创建出来的计时器ID,用于外部控制对应计时器</returns>
public int CreateTimer(bool isRealTime, int allTimeMiniSec, UnityAction overCallBack, int intervalTimeMiniSec = 0, UnityAction callBack = null)
{
int keyID = ++TIMER_KEY;
//从缓存池取出对应的计时器,并初始化数据,然后记录到字典内
TimerItem timerItem = PoolManager.Instance.GetObj<TimerItem>();
timerItem.InitInfo(keyID, allTimeMiniSec, overCallBack, intervalTimeMiniSec, callBack);
if (isRealTime)
realTimerDic.Add(keyID, timerItem);
else
timerDic.Add(keyID, timerItem);
return keyID;
}
/// <summary>
/// 移除单个计时器
/// </summary>
/// <param name="keyID">要移除的计时器对象ID</param>
public void RemoveTimer(int keyID)
{
if (timerDic.ContainsKey(keyID))
{
PoolManager.Instance.PushObj(timerDic[keyID]);
timerDic.Remove(keyID);
}
else if (realTimerDic.ContainsKey(keyID))
{
PoolManager.Instance.PushObj(realTimerDic[keyID]);
realTimerDic.Remove(keyID);
}
}
/// <summary>
/// 重置单个计时器
/// </summary>
/// <param name="keyID">要重置计时的计时器对象ID</param>
public void ResetTimer(int keyID)
{
if (timerDic.ContainsKey(keyID))
{
timerDic[keyID].ResetTimer();
}
else if (realTimerDic.ContainsKey(keyID))
{
realTimerDic[keyID].ResetTimer();
}
}
/// <summary>
/// 开启单个计时器
/// </summary>
/// <param name="keyID">要开始计时的计时器对象ID</param>
public void StartTimer(int keyID)
{
if (timerDic.ContainsKey(keyID))
{
timerDic[keyID].isRunning = true;
}
else if (realTimerDic.ContainsKey(keyID))
{
realTimerDic[keyID].isRunning = true;
}
}
/// <summary>
/// 停止单个计时器的计时
/// </summary>
/// <param name="keyID">要停止计时的计时器对象ID</param>
public void StopTimer(int keyID)
{
if (timerDic.ContainsKey(keyID))
{
timerDic[keyID].isRunning = false;
}
else if (realTimerDic.ContainsKey(keyID))
{
realTimerDic[keyID].isRunning = false;
}
}
-
使用示例
1 | void Update() |
同时按下T和Y,在第二秒时令Time.timeScale = 0
,在第五秒时令Time.timeScale = 1
,观察两种消息输出的变化
输出:
具体代码
1 | using System.Collections; |
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 文KRIFE齐的博客!