unity之 协程Coroutine VS 定时器
unity协程Coroutine 大家并不陌生,在项目中也是经常使用。大量的使用协程,会影响性能,带来GC。
如下图 延迟调用 所带来的的GC:
因此 对于延迟调用 ,最好不要用 协程 Coroutine,最好自己用update 去实现。
自己写定时器有哪些优点:
1.减少GC
2.可以自己写回调,开始执行,执行中,执行完毕回调。
3.可以循环次数、暂定、停止、恢复、循环间隔时间、剩余循环次数。
根据以上的需求,该如何设计出一个优雅且好用的定时器呢?
定时器:
1 using System; 2 3 ///4 /// 定时器 5 /// 6 public class TestTimeAction 7 { 8 /// 9 /// 定时器名字 10 /// 11 private string m_Name; 12 13 /// 14 /// 定时器名字 15 /// 16 internal string Name 17 { 18 get { return m_Name; } 19 } 20 21 /// 22 /// 延迟时间 23 /// 24 private float m_DelayTime; 25 26 /// 27 /// 循环次数 -1:无线循环 0:循环一次 >0:循环次数 28 /// 29 private int m_LoopTimes; 30 31 /// 32 /// 已循环次数 33 /// 34 private int m_AlreadyTimes; 35 36 /// 37 /// 间隔 38 /// 39 private float m_Interval; 40 41 /// 42 /// 是否正在运行 43 /// 44 public bool IsRuning 45 { 46 get; 47 private set; 48 } 49 50 /// 51 /// 是否暂定中 52 /// 53 private bool m_IsPause = false; 54 55 /// 56 /// 最后暂停时间 57 /// 58 private float m_LastPauseTime; 59 60 /// 61 /// 暂停了多久 62 /// 63 private float m_PauseTime; 64 65 /// 66 /// 当前时间 67 /// 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 /// 76 /// 初始化 77 /// 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 /// 91 /// 运行 92 /// 93 public void Run() 94 { 95 TestGameEntry.TimeMgr.RegisterTimeAction(this); 96 m_CurrTime = UnityEngine.Time.time; 97 m_IsPause = false; 98 } 99 100 /// 101 /// 暂停 102 /// 103 public void Pause() 104 { 105 if (m_IsPause) return; 106 m_LastPauseTime = UnityEngine.Time.time; 107 m_IsPause = true; 108 } 109 110 /// 111 /// 恢复 112 /// 113 public void Resume() 114 { 115 m_IsPause = false; 116 //计算暂停多久 117 m_PauseTime = UnityEngine.Time.time - m_LastPauseTime; 118 } 119 120 /// 121 /// 停止 122 /// 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 LinkedListtestTimeActions; 6 7 internal void Init() 8 { 9 testTimeActions = new LinkedList (); 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 }
欢迎大家提出批评建议和指教哈~