Unity TimerManager

通用的计时器管理系统实现

完整代码会放在文章最后,想自己研究的可以直接Copy到工程中实践

一、脚本分析

CommonBehaviour.cs

  • Instance
  • ILoop
  • NormalLoop : ILoop
  • FixedLoop : ILoop
  • LateLoop : ILoop

TimerManager.cs

  • TimerGroup
  • Timer : CommonBehaviour.NormalLoop

二、CommonBehaviour详解

1.全局循环开关

1
public static bool isLooping = true;

2.计时器列表

1
2
3
private List<ILoop> normalLoopList = new List<ILoop>();
private List<ILoop> fixedLoopList = new List<ILoop>();
private List<ILoop> lateLoopList = new List<ILoop>();

3.退出程序时的回调

1
2
public static NoParamEvent onApplicationQuit { get; private set; }
public static BoolParamEvent onApplicationPause { get; private set; }

4.Instance类

1
2
3
4
5
6
7
8
9
10
11
class Instance
{
public readonly static CommonBehaviour CommonBehaviour;
static Instance()
{
CommonBehaviour = new GameObject("CommonBehaviour").AddComponent<CommonBehaviour>();
onApplicationQuit = new NoParamEvent();
onApplicationPause = new BoolParamEvent();
DontDestroyOnLoad(CommonBehaviour);
}
}

初始化CommonBehaviour对象

5.Update方法

因为有三种计时器(NormalLoop,FixedLoop,LateLoop),对应在MonoBehaviour中的Update,FixedUpdate,LateUpdate中调用对应的Update方法,这里以FixedLoop为例

1
2
3
4
5
6
7
8
9
10
11
12
13
void FixedUpdate()
{
if (fixedLoopList == null || fixedLoopList.Count == 0) return;
for (int i = fixedLoopList.Count - 1; i >= 0; i--)
{
if (fixedLoopList[i] == null)
{
fixedLoopList.RemoveAt(i);
continue;
}
if (isLooping && fixedLoopList[i].isLooping) fixedLoopList[i].Update();
}
}

遍历FixedLoop列表,移除为空的对象,如果全局循环开关和ILoop的循环开关均为true,则调用对应的Update方法。其他两种类型类似。

6.UnLoop方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public static void UnLoop(ILoop iLoop)
{
if (iLoop == null) return;
if (iLoop is NormalLoop)
{
Instance.CommonBehaviour.normalLoopList?.Remove(iLoop);
}
else if (iLoop is FixedLoop)
{
Instance.CommonBehaviour.fixedLoopList?.Remove(iLoop);
}
else if (iLoop is LateLoop)
{
Instance.CommonBehaviour.lateLoopList?.Remove(iLoop);
}
}

判断Loop类型并从列表移除

7.Quit&Pause

1
2
3
4
5
6
7
8
9
10
11
public class NoParamEvent : UnityEvent { }
public class BoolParamEvent : UnityEvent<bool> { }

private void OnApplicationQuit()
{
onApplicationQuit?.Invoke();
}
private void OnApplicationPause(bool pauseStatus)
{
onApplicationPause?.Invoke(pauseStatus);
}

全局退出和暂停方法,Invoke对应的Event

8.ILoop抽象类

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
public abstract class ILoop
{
public bool isLooping { get; private set; } = true;
protected void PauseLoop()
{
isLooping = false;
}
protected void ContinueLoop()
{
isLooping = true;
}
protected Coroutine StartCoroutine(IEnumerator ienumerator)
{
return Instance.CommonBehaviour.StartCoroutine(ienumerator);
}
protected void StopCoroutine(Coroutine coroutine)
{
Instance.CommonBehaviour.StopCoroutine(coroutine);
}
public void StopAllCoroutines()
{
Instance.CommonBehaviour.StopAllCoroutines();
}
public abstract void Update();
}

借助MonoBehaviour完成一些协程以及留下Update抽象方法

9.xxxLoop抽象类

1
2
3
4
5
6
7
8
9
10
11
12
public abstract class NormalLoop : ILoop
{
public NormalLoop() => Instance.CommonBehaviour.normalLoopList.Add(this);
}
public abstract class FixedLoop : ILoop
{
public FixedLoop() => Instance.CommonBehaviour.fixedLoopList.Add(this);
}
public abstract class LateLoop : ILoop
{
public LateLoop() => Instance.CommonBehaviour.lateLoopList.Add(this);
}

构造函数中将自己加入维护的列表中

三、Timer详解

实现对应的Loop接口,这里以NormalLoop为例子

1
public class Timer : CommonBehaviour.NormalLoop

1.属性及初始化

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
private TimerGroup timerGroup;
public string groupFlag { get; private set; }
private bool isAutoClose = true;
private Action<float, float> progressAct1;
private Action<float> progressAct2;
private Action completeAct;
public bool isPlaying { get; private set; } = false;
public float timer { get; private set; }
public float curTimer { get; private set; }
public Timer()
{
PauseLoop();
}
public Timer Init(float timer, bool isAutoClose, TimerGroup timerGroup)
{
if (isPlaying)
{
Debug.LogError("Timer 初始化失败!");
return null;
}
this.timerGroup = timerGroup;
groupFlag = timerGroup.groupFlag;
this.isAutoClose = isAutoClose;
this.timer = timer;
curTimer = 0;
return this;
}

构造函数中暂停Loop,初始化函数中对各个属性赋初值
两个Action下边会具体说明

2.Play方法

1
2
3
4
5
6
7
8
9
public Timer Play(bool restart = false)
{
if (restart) curTimer = 0;
progressAct1?.Invoke(curTimer, timer);
progressAct2?.Invoke(curTimer / timer);
ContinueLoop();
isPlaying = true;
return this;
}
  • 如果restart,计时器当前时间归零
  • 两个参数的action,分别传入当前时间和总时间
  • 一个参数的action,实际上是计时百分比

3.Pause/Stop方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public Timer Pause()
{
if (!isPlaying) return this;
isPlaying = false;
PauseLoop();
return this;
}
public Timer Stop()
{
PauseLoop();
isPlaying = false;
curTimer = 0;
return this;
}

区别是Stop()会导致计时器当前时间归零

4.Action

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public Timer OnComplete(Action completeAct)
{
this.completeAct = completeAct;
return this;
}
public Timer OnProgress(Action<float, float> progressAct)
{
progressAct1 = progressAct;
return this;
}
public Timer OnProgress(Action<float> progressAct)
{
progressAct2 = progressAct;
return this;
}
  • OnComplete 计时完成后调用的Action
  • OnProgress Play()解释过了

5.Update方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public override void Update()
{
curTimer += Time.deltaTime * timerGroup.timeScale;
if (curTimer > timer) curTimer = timer;
progressAct1?.Invoke(curTimer, timer);
progressAct2?.Invoke(curTimer / timer);
if (curTimer == timer)
{
isPlaying = false;
completeAct?.Invoke();
if (!isAutoClose) Stop();
else timerGroup.Remove(this);
}
}
- Unity自带的deltaTime * 设置的timeScale = 计时器当前时间
- 如果AutoClose为true,计时完成后自动移除对应计时器组

四、TimerGroup详解

1.属性和初始化

1
2
3
4
5
6
7
8
public float timeScale = 1;
public string groupFlag { get; private set; }
private List<Timer> list = new List<Timer>();
public TimerGroup Init(string groupFlag)
{
this.groupFlag = groupFlag;
return this;
}
  • timeScale 倍速
  • groupFlag 组名
  • list存同组的计时器

2.Add/Remove方法

1
2
3
4
5
6
7
8
9
10
11
12
13
public void Add(Timer timer)
{
if (list.Contains(timer)) return;
list.Add(timer);
}
public void Remove(Timer timer)
{
if (list == null) return;
if (!list.Contains(timer)) return;
timer.Dispose();
list.Remove(timer);
CloseTimered(timer, this);
}

CloseTimered 方法见下边 TimerManager详解

3.Stop/Pause/Continue方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public void Stop()
{
if (list == null || list.Count == 0) return;
for (int i = list.Count - 1; i >= 0; i--) list[i]?.Stop();
}
public void Pause()
{
if (list == null || list.Count == 0) return;
for (int i = list.Count - 1; i >= 0; i--) list[i]?.Pause();
}
public void Continue()
{
if (list == null || list.Count == 0) return;
for (int i = list.Count - 1; i >= 0; i--) list[i]?.Play();
}

遍历该组的计时器列表,执行对应的停止暂停继续方法

4.CloseGroup方法

1
2
3
4
5
6
7
8
public void CloseGroup()
{
if (list != null && list.Count > 0) for (int i = list.Count - 1; i >= 0; i--) Remove(list[i]);
list?.Clear();
CloseGrouped(this);
list = null;
groupFlag = null;
}

移除组内所有计时器并删除该组

五、TimerManager详解

1.属性

1
2
3
public const string commonGroupFlag = "COMMON_TIMER";
private static List<Timer> timers_idle = new List<Timer>();
private static Dictionary<string, TimerGroup> timerGroupDict = new Dictionary<string, TimerGroup>();
  • commonGroupFlag 如果使用计时器时没有给他分组,那就会统一放进这个标签下的计时器组
  • timers_idle 闲置计时器组
  • timerGroupDict 以组分类所有的正在运行的计时器

2.OpenTimer方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public static Timer OpenTimer(float timer, string groupFlag, bool isPlay = true, bool isAutoClose = true)
{
if (timers_idle == null) timers_idle = new List<Timer>();
Timer t;
if (timers_idle.Count > 0)
{
t = timers_idle[0];
timers_idle.RemoveAt(0);
}
else t = new Timer();
if (timerGroupDict.ContainsKey(groupFlag)) timerGroupDict[groupFlag].Add(t);
else
{
timerGroupDict.Add(groupFlag, new TimerGroup().Init(groupFlag));
timerGroupDict[groupFlag].Add(t);
}
t.Init(timer, isAutoClose, timerGroupDict[groupFlag]);
if (isPlay) t.Play(true);
return t;
}
public static Timer OpenTimer(float timer, bool isPlay = true, bool isAutoClose = true)
{
return OpenTimer(timer, commonGroupFlag, isPlay, isAutoClose);
}
  • timer 计时器时长 isPlay 是否直接开始计时 isAutoClose 是否自动关闭计时器 groupFlag 计时器组名
  • 如果闲置计时器列表内有计时器,直接拿出来用,否则新建计时器

3.GetGroups方法

1
2
3
4
5
public static TimerGroup[] GetGroups()
{
if (timerGroupDict == null || timerGroupDict.Count == 0) return null;
return timerGroupDict.Values.ToArray();
}

获取所有计时器组

4.Stop/Pause/Continue方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public static void StopGroup(string groupFlag)
{
if (!timerGroupDict.ContainsKey(groupFlag)) return;
timerGroupDict[groupFlag].Stop();
}
public static void PauseGroup(string groupFlag)
{
if (!timerGroupDict.ContainsKey(groupFlag)) return;
timerGroupDict[groupFlag].Pause();
}
public static void ContinueGroup(string groupFlag)
{
if (!timerGroupDict.ContainsKey(groupFlag)) return;
timerGroupDict[groupFlag].Continue();
}

传入计时器名,执行对应组的停止暂停继续方法

5.CloseTimered方法

1
2
3
4
5
static void CloseTimered(Timer timer, TimerGroup group)
{
if (timers_idle.Contains(timer)) return;
timers_idle.Add(timer);
}

传入的计时器添加进闲置计时器列表

6.CloseGrouped方法

1
2
3
4
5
static void CloseGrouped(TimerGroup group)
{
if (group == null) return;
if (timerGroupDict.ContainsKey(group.groupFlag)) timerGroupDict.Remove(group.groupFlag);
}

移除传入的计时器组

六、完整代码

1.CommonBehaviour.cs

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
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
public class NoParamEvent : UnityEvent { }
public class BoolParamEvent : UnityEvent<bool> { }

public class CommonBehaviour : MonoBehaviour
{
public static bool isLooping = true;
private List<ILoop> normalLoopList = new List<ILoop>();
private List<ILoop> fixedLoopList = new List<ILoop>();
private List<ILoop> lateLoopList = new List<ILoop>();
public static NoParamEvent onApplicationQuit { get; private set; }
public static BoolParamEvent onApplicationPause { get; private set; }

class Instance
{
public readonly static CommonBehaviour CommonBehaviour;

static Instance()
{
CommonBehaviour = new GameObject("CommonBehaviour").AddComponent<CommonBehaviour>();
onApplicationQuit = new NoParamEvent();
onApplicationPause = new BoolParamEvent();
DontDestroyOnLoad(CommonBehaviour);
}
}

#if UNITY_EDITOR
[UnityEditor.CustomEditor(typeof(CommonBehaviour))]
class CommonBehaviourInspector : UnityEditor.Editor
{
private int normalLoopCount => GetNormalLoopCount();
private int normalLoopUsingCount;
private int fixedLoopCount => GetFixedLoopCount();
private int fixedLoopUsingCount;
private int lateLoopCount => GetLateLoopCount();
private int lateLoopUsingCount;

private GUIStyle titleStyle;
private GUIStyle normalStyle;
private GUIStyle idleStyle;

private bool showNormalLoopInfo;
private bool showFixedLoopInfo;
private bool showLateLoopInfo;

void OnEnable()
{
titleStyle = new GUIStyle();
titleStyle.fontSize = 15;
titleStyle.fontStyle = FontStyle.Bold;
titleStyle.normal.textColor = Color.green;

normalStyle = new GUIStyle();
normalStyle.normal.textColor = Color.cyan;

idleStyle = new GUIStyle();
idleStyle.normal.textColor = Color.gray;
}

public override void OnInspectorGUI()
{
Repaint();
GUILayout.Label("NormalLoop: " + normalLoopUsingCount + "/" + normalLoopCount, titleStyle);
normalLoopUsingCount = 0;
if (normalLoopCount > 0)
{
for (int i = normalLoopCount - 1; i >= 0; i--)
{
ILoop loop = Instance.CommonBehaviour.normalLoopList[i];
if (isLooping && loop.isLooping)
{
normalLoopUsingCount++;
DrawLoopInfo(loop.GetType().ToString(), true);
}
else DrawLoopInfo(loop.GetType().ToString(), false);
}
}

GUILayout.Label("FixedLoop: " + fixedLoopUsingCount + "/" + fixedLoopCount, titleStyle);
fixedLoopUsingCount = 0;
if (fixedLoopCount > 0)
{
for (int i = fixedLoopCount - 1; i >= 0; i--)
{
ILoop loop = Instance.CommonBehaviour.fixedLoopList[i];
if (isLooping && loop.isLooping)
{
fixedLoopUsingCount++;
DrawLoopInfo(loop.GetType().ToString(), true);
}
else DrawLoopInfo(loop.GetType().ToString(), false);
}
}

GUILayout.Label("LateLoop: " + lateLoopUsingCount + "/" + lateLoopCount, titleStyle);
lateLoopUsingCount = 0;
if (lateLoopCount > 0)
{
for (int i = lateLoopCount - 1; i >= 0; i--)
{
ILoop loop = Instance.CommonBehaviour.lateLoopList[i];
if (isLooping && loop.isLooping)
{
lateLoopUsingCount++;
DrawLoopInfo(loop.GetType().ToString(), true);
}
else DrawLoopInfo(loop.GetType().ToString(), false);
}
}
}

void DrawLoopInfo(string info, bool isUsing)
{
UnityEditor.EditorGUILayout.BeginHorizontal();
GUILayout.Space(20);
GUILayout.Label(info, isUsing ? normalStyle : idleStyle);
UnityEditor.EditorGUILayout.EndHorizontal();
}

int GetNormalLoopCount()
{
if (!Application.isPlaying) return 0;
return Instance.CommonBehaviour.normalLoopList == null ? 0 : Instance.CommonBehaviour.normalLoopList.Count;
}

int GetFixedLoopCount()
{
if (!Application.isPlaying) return 0;
return Instance.CommonBehaviour.fixedLoopList == null ? 0 : Instance.CommonBehaviour.fixedLoopList.Count;
}

int GetLateLoopCount()
{
if (!Application.isPlaying) return 0;
return Instance.CommonBehaviour.lateLoopList == null ? 0 : Instance.CommonBehaviour.lateLoopList.Count;
}
}
#endif

void Update()
{
if (normalLoopList == null || normalLoopList.Count == 0) return;
for (int i = normalLoopList.Count - 1; i >= 0; i--)
{
if (normalLoopList[i] == null)
{
normalLoopList.RemoveAt(i);
continue;
}
if (isLooping && normalLoopList[i].isLooping) normalLoopList[i].Update();
}
}

void FixedUpdate()
{
if (fixedLoopList == null || fixedLoopList.Count == 0) return;
for (int i = fixedLoopList.Count - 1; i >= 0; i--)
{
if (fixedLoopList[i] == null)
{
fixedLoopList.RemoveAt(i);
continue;
}
if (isLooping && fixedLoopList[i].isLooping) fixedLoopList[i].Update();
}
}

void LateUpdate()
{
if (lateLoopList == null || lateLoopList.Count == 0) return;
for (int i = lateLoopList.Count - 1; i >= 0; i--)
{
if (lateLoopList[i] == null)
{
lateLoopList.RemoveAt(i);
continue;
}
if (isLooping && lateLoopList[i].isLooping) lateLoopList[i].Update();
}
}

public static void UnLoop(ILoop iLoop)
{
if (iLoop == null) return;
if (iLoop is NormalLoop)
{
Instance.CommonBehaviour.normalLoopList?.Remove(iLoop);
}
else if (iLoop is FixedLoop)
{
Instance.CommonBehaviour.fixedLoopList?.Remove(iLoop);
}
else if (iLoop is LateLoop)
{
Instance.CommonBehaviour.lateLoopList?.Remove(iLoop);
}
}

private void OnApplicationQuit()
{
onApplicationQuit?.Invoke();
}

private void OnApplicationPause(bool pauseStatus)
{
onApplicationPause?.Invoke(pauseStatus);
}

public abstract class ILoop
{
public bool isLooping { get; private set; } = true;

protected void PauseLoop()
{
isLooping = false;
}

protected void ContinueLoop()
{
isLooping = true;
}

protected Coroutine StartCoroutine(IEnumerator ienumerator)
{
return Instance.CommonBehaviour.StartCoroutine(ienumerator);
}

protected void StopCoroutine(Coroutine coroutine)
{
Instance.CommonBehaviour.StopCoroutine(coroutine);
}

public void StopAllCoroutines()
{
Instance.CommonBehaviour.StopAllCoroutines();
}

public abstract void Update();
}

public abstract class NormalLoop : ILoop
{
public NormalLoop() => Instance.CommonBehaviour.normalLoopList.Add(this);
}

public abstract class FixedLoop : ILoop
{
public FixedLoop() => Instance.CommonBehaviour.fixedLoopList.Add(this);
}

public abstract class LateLoop : ILoop
{
public LateLoop() => Instance.CommonBehaviour.lateLoopList.Add(this);
}
}

2.TimerManager.cs

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
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
public class TimerManager
{
public const string commonGroupFlag = "COMMON_TIMER";
private static List<Timer> timers_idle = new List<Timer>();
private static Dictionary<string, TimerGroup> timerGroupDict = new Dictionary<string, TimerGroup>();

public static Timer OpenTimer(float timer, string groupFlag, bool isPlay = true, bool isAutoClose = true)
{
if (timers_idle == null) timers_idle = new List<Timer>();
Timer t;
if (timers_idle.Count > 0)
{
t = timers_idle[0];
timers_idle.RemoveAt(0);
}
else t = new Timer();
if (timerGroupDict.ContainsKey(groupFlag)) timerGroupDict[groupFlag].Add(t);
else
{
timerGroupDict.Add(groupFlag, new TimerGroup().Init(groupFlag));
timerGroupDict[groupFlag].Add(t);
}
t.Init(timer, isAutoClose, timerGroupDict[groupFlag]);
if (isPlay) t.Play(true);
return t;
}

public static Timer OpenTimer(float timer, bool isPlay = true, bool isAutoClose = true)
{
return OpenTimer(timer, commonGroupFlag, isPlay, isAutoClose);
}

public static TimerGroup[] GetGroups()
{
if (timerGroupDict == null || timerGroupDict.Count == 0) return null;
return timerGroupDict.Values.ToArray();
}

public static void StopGroup(string groupFlag)
{
if (!timerGroupDict.ContainsKey(groupFlag)) return;
timerGroupDict[groupFlag].Stop();
}

public static void PauseGroup(string groupFlag)
{
if (!timerGroupDict.ContainsKey(groupFlag)) return;
timerGroupDict[groupFlag].Pause();
}

public static void ContinueGroup(string groupFlag)
{
if (!timerGroupDict.ContainsKey(groupFlag)) return;
timerGroupDict[groupFlag].Continue();
}

public static void CloseTimer(Timer timer, string groupFlag)
{
if (timer == null) return;
if (!timerGroupDict.ContainsKey(groupFlag)) return;
timerGroupDict[groupFlag].Remove(timer);
}

public static void CloseGroup(string groupFlag)
{
if (!timerGroupDict.ContainsKey(groupFlag)) return;
timerGroupDict[groupFlag].CloseGroup();
}

public static void SetTimerScale(string groupFlag, float timeScale)
{
if (timeScale < 0) return;
if (!timerGroupDict.ContainsKey(groupFlag)) return;
timerGroupDict[groupFlag].timeScale = timeScale;
}

public static float GetTimerScale(string groupFlag)
{
if (!timerGroupDict.ContainsKey(groupFlag)) return -1f;
return timerGroupDict[groupFlag].timeScale;
}

public static void StopAll()
{
TimerGroup[] groups = GetGroups();
if (groups == null || groups.Length == 0) return;
for (int i = groups.Length - 1; i >= 0; i--) groups[i].Stop();
}

public static void PauseAll()
{
TimerGroup[] groups = GetGroups();
if (groups == null || groups.Length == 0) return;
for (int i = groups.Length - 1; i >= 0; i--) groups[i].Pause();
}

public static void ContinueGroup()
{
TimerGroup[] groups = GetGroups();
if (groups == null || groups.Length == 0) return;
for (int i = groups.Length - 1; i >= 0; i--) groups[i].Continue();
}

public static void CloseAll()
{
TimerGroup[] groups = GetGroups();
if (groups == null || groups.Length == 0) return;
for (int i = groups.Length - 1; i >= 0; i--) groups[i].CloseGroup();
}

static void CloseTimered(Timer timer, TimerGroup group)
{
if (timers_idle.Contains(timer)) return;
timers_idle.Add(timer);
}

static void CloseGrouped(TimerGroup group)
{
if (group == null) return;
if (timerGroupDict.ContainsKey(group.groupFlag)) timerGroupDict.Remove(group.groupFlag);
}

public class TimerGroup
{
public float timeScale = 1;
public string groupFlag { get; private set; }
private List<Timer> list = new List<Timer>();

public TimerGroup Init(string groupFlag)
{
this.groupFlag = groupFlag;
return this;
}

public void Add(Timer timer)
{
if (list.Contains(timer)) return;
list.Add(timer);
}

public void Remove(Timer timer)
{
if (list == null) return;
if (!list.Contains(timer)) return;
timer.Dispose();
list.Remove(timer);
CloseTimered(timer, this);
}

public void Stop()
{
if (list == null || list.Count == 0) return;
for (int i = list.Count - 1; i >= 0; i--) list[i]?.Stop();
}

public void Pause()
{
if (list == null || list.Count == 0) return;
for (int i = list.Count - 1; i >= 0; i--) list[i]?.Pause();
}

public void Continue()
{
if (list == null || list.Count == 0) return;
for (int i = list.Count - 1; i >= 0; i--) list[i]?.Play();
}

public void CloseGroup()
{
if (list != null && list.Count > 0) for (int i = list.Count - 1; i >= 0; i--) Remove(list[i]);
list?.Clear();
CloseGrouped(this);
list = null;
groupFlag = null;
}
}

public class Timer : CommonBehaviour.NormalLoop
{
private TimerGroup timerGroup;
public string groupFlag { get; private set; }
private bool isAutoClose = true;
private Action<float, float> progressAct1;
private Action<float> progressAct2;
private Action completeAct;
public bool isPlaying { get; private set; } = false;
public float timer { get; private set; }
public float curTimer { get; private set; }

public Timer()
{
PauseLoop();
}

public Timer Init(float timer, bool isAutoClose, TimerGroup timerGroup)
{
if (isPlaying)
{
Debug.LogError("Timer 初始化失败!");
return null;
}
this.timerGroup = timerGroup;
groupFlag = timerGroup.groupFlag;
this.isAutoClose = isAutoClose;
this.timer = timer;
curTimer = 0;
return this;
}

public Timer Play(bool restart = false)
{
if (restart) curTimer = 0;
progressAct1?.Invoke(curTimer, timer);
progressAct2?.Invoke(curTimer / timer);
ContinueLoop();
isPlaying = true;
return this;
}

public Timer Pause()
{
if (!isPlaying) return this;
isPlaying = false;
PauseLoop();
return this;
}

public Timer Stop()
{
PauseLoop();
isPlaying = false;
curTimer = 0;
return this;
}

public Timer OnComplete(Action completeAct)
{
this.completeAct = completeAct;
return this;
}

public Timer OnProgress(Action<float, float> progressAct)
{
progressAct1 = progressAct;
return this;
}

public Timer OnProgress(Action<float> progressAct)
{
progressAct2 = progressAct;
return this;
}

public override void Update()
{
curTimer += Time.deltaTime * timerGroup.timeScale;
if (curTimer > timer) curTimer = timer;
progressAct1?.Invoke(curTimer, timer);
progressAct2?.Invoke(curTimer / timer);
if (curTimer == timer)
{
isPlaying = false;
completeAct?.Invoke();
if (!isAutoClose) Stop();
else timerGroup.Remove(this);
}
}

public void Dispose()
{
Stop();
timer = 0;
progressAct1 = null;
progressAct2 = null;
completeAct = null;
}
}
}
完结撒花~

pid:61251232


Unity TimerManager
https://baifabaiquan.cn/2023/03/17/UnityTimerManager/
作者
白发败犬
发布于
2023年3月17日
许可协议