unity协程Coroutine 大家并不陌生,在项目中也是经常使用。大量的使用协程,会影响性能,带来GC。
如下图 延迟调用 所带来的的GC:
因此 对于延迟调用 ,最好不要用 协程 Coroutine,最好自己用update 去实现。
自己写定时器有哪些优点:
1.减少GC
2.可以自己写回调,开始执行,执行中,执行完毕回调。
3.可以循环次数、暂定、停止、恢复、循环间隔时间、剩余循环次数。
根据以上的需求,该如何设计出一个优雅且好用的定时器呢?
定时器:
1 using System;
2
3 /// <summary>
4 /// 定时器
5 /// </summary>
6 public class TestTimeAction
7 {
8 /// <summary>
9 /// 定时器名字
10 /// </summary>
11 private string m_Name;
12
13 /// <summary>
14 /// 定时器名字
15 /// </summary>
16 internal string Name
17 {
18 get { return m_Name; }
19 }
20
21 /// <summary>
22 /// 延迟时间
23 /// </summary>
24 private float m_DelayTime;
25
26 /// <summary>
27 /// 循环次数 -1:无线循环 0:循环一次 >0:循环次数
28 /// </summary>
29 private int m_LoopTimes;
30
31 /// <summary>
32 /// 已循环次数
33 /// </summary>
34 private int m_AlreadyTimes;
35
36 /// <summary>
37 /// 间隔
38 /// </summary>
39 private float m_Interval;
40
41 /// <summary>
42 /// 是否正在运行
43 /// </summary>
44 public bool IsRuning
45 {
46 get;
47 private set;
48 }
49
50 /// <summary>
51 /// 是否暂定中
52 /// </summary>
53 private bool m_IsPause = false;
54
55 /// <summary>
56 /// 最后暂停时间
57 /// </summary>
58 private float m_LastPauseTime;
59
60 /// <summary>
61 /// 暂停了多久
62 /// </summary>
63 private float m_PauseTime;
64
65 /// <summary>
66 /// 当前时间
67 /// </summary>
68 private float m_CurrTime;
69
70 //开始、运行中、完成回调
71 public Action OnStartFun { get; private set; }
72 public Action<int> OnDoingFun { get; private set; }//剩余次数
73 public Action OnCompleteFun { get; private set; }
74
75 /// <summary>
76 /// 初始化
77 /// </summary>
78 internal TestTimeAction Init(string name, float delayTime, int loopTimes, float interval, Action startFun, Action<int> doingFun, Action completeFun)
79 {
80 m_Name = name;
81 m_DelayTime = delayTime;
82 m_LoopTimes = loopTimes;
83 m_Interval = interval;
84 OnStartFun = startFun;
85 OnDoingFun = doingFun;
86 OnCompleteFun = completeFun;
87 return this;
88 }
89
90 /// <summary>
91 /// 运行
92 /// </summary>
93 public void Run()
94 {
95 TestGameEntry.TimeMgr.RegisterTimeAction(this);
96 m_CurrTime = UnityEngine.Time.time;
97 m_IsPause = false;
98 }
99
100 /// <summary>
101 /// 暂停
102 /// </summary>
103 public void Pause()
104 {
105 if (m_IsPause) return;
106 m_LastPauseTime = UnityEngine.Time.time;
107 m_IsPause = true;
108 }
109
110 /// <summary>
111 /// 恢复
112 /// </summary>
113 public void Resume()
114 {
115 m_IsPause = false;
116 //计算暂停多久
117 m_PauseTime = UnityEngine.Time.time - m_LastPauseTime;
118 }
119
120 /// <summary>
121 /// 停止
122 /// </summary>
123 private void Stop()
124 {
125 IsRuning = false;
126 OnCompleteFun?.Invoke();
127 TestGameEntry.TimeMgr.RemoveTimeAction(this);
128 }
129
130 internal void OnUpdate()
131 {
132 if (m_IsPause) return;
133 //开始运行
134 if (UnityEngine.Time.time > m_CurrTime + m_DelayTime + m_PauseTime)
135 {
136 if (!IsRuning)
137 {
138 IsRuning = true;
139 m_CurrTime = UnityEngine.Time.time;
140 m_PauseTime = 0;
141 OnStartFun?.Invoke();
142 }
143 }
144 if (!IsRuning) return;
145 //运行中
146 if (UnityEngine.Time.time > m_CurrTime + m_PauseTime)
147 {
148 m_CurrTime = UnityEngine.Time.time + m_Interval;
149 m_PauseTime = 0;
150 OnDoingFun?.Invoke(m_LoopTimes - m_AlreadyTimes);
151 if (m_LoopTimes > -1)
152 {
153 m_AlreadyTimes++;
154 if (m_AlreadyTimes >= m_LoopTimes)
155 {
156 Stop();
157 }
158 }
159 }
160 }
161 }
时间管理器:
1 using System.Collections.Generic;
2
3 public class TestTimeManager : System.IDisposable
4 {
5 private LinkedList<TestTimeAction> testTimeActions;
6
7 internal void Init()
8 {
9 testTimeActions = new LinkedList<TestTimeAction>();
10 }
11
12 public TestTimeAction CreateTimeAction()
13 {
14 TestTimeAction timeAction = new TestTimeAction();
15 return timeAction;
16 }
17
18
19 public void RegisterTimeAction(TestTimeAction timeAction)
20 {
21 testTimeActions.AddLast(timeAction);
22 }
23
24 public void RemoveTimeAction(TestTimeAction timeAction)
25 {
26 if (testTimeActions.Contains(timeAction))
27 {
28 testTimeActions.Remove(timeAction);
29 }
30 }
31 public void RemoveTimeActionByName(string name)
32 {
33 for (var v = testTimeActions.First; v.Next != null; v = v.Next)
34 {
35 if (v.Value.Name.Equals(name, System.StringComparison.CurrentCultureIgnoreCase))
36 {
37 testTimeActions.Remove(v.Value);
38 break;
39 }
40 }
41 }
42
43 public void OnUpdate()
44 {
45 for (var v = testTimeActions.First; v != null; v = v.Next)
46 {
47 if (v.Value.OnStartFun.Target == null || v.Value.OnStartFun.Target.ToString() == "null")
48 {
49 testTimeActions.Remove(v);
50 continue;
51 }
52 if (v.Value.OnDoingFun.Target == null || v.Value.OnDoingFun.Target.ToString() == "null")
53 {
54 testTimeActions.Remove(v);
55 continue;
56 }
57 if (v.Value.OnCompleteFun.Target == null || v.Value.OnCompleteFun.Target.ToString() == "null")
58 {
59 testTimeActions.Remove(v);
60 continue;
61 }
62 v.Value.OnUpdate();
63 }
64 }
65
66 public void Dispose()
67 {
68 testTimeActions.Clear();
69 }
70 }
测试入口
1 using UnityEngine;
2
3 public class TestGameEntry : MonoBehaviour
4 {
5 public static TestGameEntry Instance;
6 public static TestTimeManager TimeMgr { get; private set; }
7 private void Awake()
8 {
9 Instance = this;
10 }
11
12 void Start()
13 {
14 InitManagers();
15 Init();
16 }
17
18 void InitManagers()
19 {
20 TimeMgr = new TestTimeManager();
21 }
22
23 void Init()
24 {
25 TimeMgr.Init();
26 }
27
28 void Update()
29 {
30 TimeMgr.OnUpdate();
31 }
32
33 private void OnDestroy()
34 {
35 TimeMgr.Dispose();
36 }
37 }
测试用例:
1 if (Input.GetKeyUp(KeyCode.A))
2 {
3 timeAction = TestGameEntry.TimeMgr.CreateTimeAction();
4 Debug.Log("创建了定时器time1");
5 if (timeAction == null) return;
6 timeAction.Init("time1", 10f, 100, 1f, () =>
7 {
8 Debug.Log("time1 开始执行");
9 }, (int times) =>
10 {
11 Debug.Log(string.Format("time1 运行中,剩余{0}次", times));
12 }, () =>
13 {
14 Debug.Log("time1 完成");
15 }).Run();
16 }
17
18 if (Input.GetKeyUp(KeyCode.P))
19 {
20 if (timeAction == null) return;
21 timeAction.Pause();
22 Debug.LogError("暂停");
23 }
24
25 if (Input.GetKeyUp(KeyCode.R))
26 {
27 if (timeAction == null) return;
28 timeAction.Resume();
29 Debug.LogError("恢复");
30 }
欢迎大家提出批评建议和指教哈~