Unity自定义定时器,模拟协程,脱离MonoBehavior控制

  1 using System;
  2 using System.Collections.Generic;
  3 using System.Timers;
  4 
  5 public class PETimer {
  6     private Action<string> taskLog;
  7 
  8     private static readonly string lockTid = "lockTid";
  9     private DateTime startDateTime = new DateTime(1970, 1, 1, 0, 0, 0, 0);
 10     private double nowTime;
 11 
 12     private int tid;
 13     private List<int> tidLst = new List<int>();
 14     private List<int> recTidLst = new List<int>();
 15 
 16     private static readonly string lockTime = "lockTime";
 17     private List<PETimeTask> tmpTimeLst = new List<PETimeTask>();
 18     private List<PETimeTask> taskTimeLst = new List<PETimeTask>();
 19     private List<int> tmpDelTimeLst = new List<int>();
 20 
 21     private int frameCounter;
 22     private static readonly string lockFrame = "lockFrame";
 23     private List<PEFrameTask> tmpFrameLst = new List<PEFrameTask>();
 24     private List<PEFrameTask> taskFrameLst = new List<PEFrameTask>();
 25     private List<int> tmpDelFrameLst = new List<int>();
 26 
 27     public PETimer(int interval = 0) {
 28         tidLst.Clear();
 29         recTidLst.Clear();
 30 
 31         tmpTimeLst.Clear();
 32         taskTimeLst.Clear();
 33 
 34         tmpFrameLst.Clear();
 35         taskFrameLst.Clear();
 36     }
 37     
 38     public void Update() {
 39         CheckTimeTask();
 40         CheckFrameTask();
 41 
 42         DelTimeTask();
 43         DelFrameTask();
 44 
 45         if (recTidLst.Count > 0) {
 46             lock (lockTid) {
 47                 RecycleTid();
 48             }
 49         }
 50     }
 51     private void DelTimeTask() {
 52         if (tmpDelTimeLst.Count > 0) {
 53             lock (lockTime) {
 54                 for (int i = 0; i < tmpDelTimeLst.Count; i++) {
 55                     bool isDel = false;
 56                     int delTid = tmpDelTimeLst[i];
 57                     for (int j = 0; j < taskTimeLst.Count; j++) {
 58                         PETimeTask task = taskTimeLst[j];
 59                         if (task.tid == delTid) {
 60                             isDel = true;
 61                             taskTimeLst.RemoveAt(j);
 62                             recTidLst.Add(delTid);
 63                             //LogInfo("Del taskTimeLst ID:" + System.Threading.Thread.CurrentThread.ManagedThreadId.ToString());
 64                             break;
 65                         }
 66                     }
 67 
 68                     if (isDel)
 69                         continue;
 70 
 71                     for (int j = 0; j < tmpTimeLst.Count; j++) {
 72                         PETimeTask task = tmpTimeLst[j];
 73                         if (task.tid == delTid) {
 74                             tmpTimeLst.RemoveAt(j);
 75                             recTidLst.Add(delTid);
 76                             //LogInfo("Del tmpTimeLst ID:" + System.Threading.Thread.CurrentThread.ManagedThreadId.ToString());
 77                             break;
 78                         }
 79                     }
 80                 }
 81             }
 82         }
 83     }
 84     private void DelFrameTask() {
 85         if (tmpDelFrameLst.Count > 0) {
 86             lock (lockFrame) {
 87                 for (int i = 0; i < tmpDelFrameLst.Count; i++) {
 88                     bool isDel = false;
 89                     int delTid = tmpDelFrameLst[i];
 90                     for (int j = 0; j < taskFrameLst.Count; j++) {
 91                         PEFrameTask task = taskFrameLst[j];
 92                         if (task.tid == delTid) {
 93                             isDel = true;
 94                             taskFrameLst.RemoveAt(j);
 95                             recTidLst.Add(delTid);
 96                             break;
 97                         }
 98                     }
 99 
100                     if (isDel)
101                         continue;
102 
103                     for (int j = 0; j < tmpFrameLst.Count; j++) {
104                         PEFrameTask task = tmpFrameLst[j];
105                         if (task.tid == delTid) {
106                             tmpFrameLst.RemoveAt(j);
107                             recTidLst.Add(delTid);
108                             break;
109                         }
110                     }
111                 }
112             }
113         }
114     }
115     private void CheckTimeTask() {
116         if (tmpTimeLst.Count > 0) {
117             lock (lockTime) {
118                 //加入缓存区中的定时任务
119                 for (int tmpIndex = 0; tmpIndex < tmpTimeLst.Count; tmpIndex++) {
120                     taskTimeLst.Add(tmpTimeLst[tmpIndex]);
121                 }
122                 tmpTimeLst.Clear();
123             }
124         }
125 
126         //遍历检测任务是否达到条件
127         nowTime = GetUTCMilliseconds();
128         for (int index = 0; index < taskTimeLst.Count; index++) {
129             PETimeTask task = taskTimeLst[index];
130             if (nowTime.CompareTo(task.destTime) < 0) {
131                 continue;
132             }
133             else {
134                 Action cb = task.callback;
135                 try {
136                     if (cb != null) {
137                         cb();
138                     }
139                     
140                 }
141                 catch (Exception e) {
142                     LogInfo(e.ToString());
143                 }
144 
145                 //移除已经完成的任务
146                 if (task.count == 1) {
147                     taskTimeLst.RemoveAt(index);
148                     index--;
149                     recTidLst.Add(task.tid);
150                 }
151                 else {
152                     if (task.count != 0) {
153                         task.count -= 1;
154                     }
155                     task.destTime += task.delay;
156                 }
157             }
158         }
159     }
160     private void CheckFrameTask() {
161         if (tmpFrameLst.Count > 0) {
162             lock (lockFrame) {
163                 //加入缓存区中的定时任务
164                 for (int tmpIndex = 0; tmpIndex < tmpFrameLst.Count; tmpIndex++) {
165                     taskFrameLst.Add(tmpFrameLst[tmpIndex]);
166                 }
167                 tmpFrameLst.Clear();
168             }
169         }
170 
171         frameCounter += 1;
172         //遍历检测任务是否达到条件
173         for (int index = 0; index < taskFrameLst.Count; index++) {
174             PEFrameTask task = taskFrameLst[index];
175             if (frameCounter < task.destFrame) {
176                 continue;
177             }
178             else {
179                 Action cb = task.callback;
180                 try {
181                     if (cb != null) {
182                         cb();
183                     }
184                 }
185                 catch (Exception e) {
186                     LogInfo(e.ToString());
187                 }
188 
189                 //移除已经完成的任务
190                 if (task.count == 1) {
191                     taskFrameLst.RemoveAt(index);
192                     index--;
193                     recTidLst.Add(task.tid);
194                 }
195                 else {
196                     if (task.count != 0) {
197                         task.count -= 1;
198                     }
199                     task.destFrame += task.delay;
200                 }
201             }
202         }
203     }
204 
205     #region TimeTask
206     public int AddTimeTask(Action callback, double delay, PETimeUnit timeUnit = PETimeUnit.Millisecond, int count = 1) {
207         if (timeUnit != PETimeUnit.Millisecond) {
208             switch (timeUnit) {
209                 case PETimeUnit.Second:
210                     delay = delay * 1000;
211                     break;
212                 case PETimeUnit.Minute:
213                     delay = delay * 1000 * 60;
214                     break;
215                 case PETimeUnit.Hour:
216                     delay = delay * 1000 * 60 * 60;
217                     break;
218                 case PETimeUnit.Day:
219                     delay = delay * 1000 * 60 * 60 * 24;
220                     break;
221                 default:
222                     LogInfo("Add Task TimeUnit Type Error...");
223                     break;
224             }
225         }
226         int tid = GetTid(); ;
227         nowTime = GetUTCMilliseconds();
228         lock (lockTime) {
229             tmpTimeLst.Add(new PETimeTask(tid, callback, nowTime + delay, delay, count));
230         }
231         return tid;
232     }
233     public void DeleteTimeTask(int tid) {
234         lock (lockTime) {
235             tmpDelTimeLst.Add(tid);
236             //LogInfo("TmpDel ID:" + System.Threading.Thread.CurrentThread.ManagedThreadId.ToString());
237         }
238         
239     }
240     public bool ReplaceTimeTask(int tid, Action callback, float delay, PETimeUnit timeUnit = PETimeUnit.Millisecond, int count = 1) {
241         if (timeUnit != PETimeUnit.Millisecond) {
242             switch (timeUnit) {
243                 case PETimeUnit.Second:
244                     delay = delay * 1000;
245                     break;
246                 case PETimeUnit.Minute:
247                     delay = delay * 1000 * 60;
248                     break;
249                 case PETimeUnit.Hour:
250                     delay = delay * 1000 * 60 * 60;
251                     break;
252                 case PETimeUnit.Day:
253                     delay = delay * 1000 * 60 * 60 * 24;
254                     break;
255                 default:
256                     LogInfo("Replace Task TimeUnit Type Error...");
257                     break;
258             }
259         }
260         nowTime = GetUTCMilliseconds();
261         PETimeTask newTask = new PETimeTask(tid, callback, nowTime + delay, delay, count);
262 
263         bool isRep = false;
264         for (int i = 0; i < taskTimeLst.Count; i++) {
265             if (taskTimeLst[i].tid == tid) {
266                 taskTimeLst[i] = newTask;
267                 isRep = true;
268                 break;
269             }
270         }
271 
272         if (!isRep) {
273             for (int i = 0; i < tmpTimeLst.Count; i++) {
274                 if (tmpTimeLst[i].tid == tid) {
275                     tmpTimeLst[i] = newTask;
276                     isRep = true;
277                     break;
278                 }
279             }
280         }
281 
282         return isRep;
283     }
284     #endregion
285 
286     #region FrameTask
287     public int AddFrameTask(Action callback, int delay, int count = 1) {
288         int tid = GetTid();
289         lock (lockTime) {
290             tmpFrameLst.Add(new PEFrameTask(tid, callback, frameCounter + delay, delay, count));
291         }
292         return tid;
293     }
294     public void DeleteFrameTask(int tid) {
295         lock (lockFrame) {
296             tmpDelFrameLst.Add(tid);
297         }
298     }
299     public bool ReplaceFrameTask(int tid, Action callback, int delay, int count = 1) {
300         PEFrameTask newTask = new PEFrameTask(tid, callback, frameCounter + delay, delay, count);
301 
302         bool isRep = false;
303         for (int i = 0; i < taskFrameLst.Count; i++) {
304             if (taskFrameLst[i].tid == tid) {
305                 taskFrameLst[i] = newTask;
306                 isRep = true;
307                 break;
308             }
309         }
310 
311         if (!isRep) {
312             for (int i = 0; i < tmpFrameLst.Count; i++) {
313                 if (tmpFrameLst[i].tid == tid) {
314                     tmpFrameLst[i] = newTask;
315                     isRep = true;
316                     break;
317                 }
318             }
319         }
320 
321         return isRep;
322     }
323     #endregion
324     public void SetLog(Action<string> handle)
325     {
326         taskLog = handle;
327     }
328 
329     public void Reset() {
330         tid = 0;
331         tidLst.Clear();
332         recTidLst.Clear();
333 
334         tmpTimeLst.Clear();
335         taskTimeLst.Clear();
336 
337         tmpFrameLst.Clear();
338         taskFrameLst.Clear();
339 
340         taskLog = null;
341     }
342 
343     #region Tool Methonds
344     private int GetTid() {
345         lock (lockTid) {
346             tid += 1;
347 
348             //安全代码,以防万一
349             while (true) {
350                 if (tid == int.MaxValue) {
351                     tid = 0;
352                 }
353 
354                 bool used = false;
355                 for (int i = 0; i < tidLst.Count; i++) {
356                     if (tid == tidLst[i]) {
357                         used = true;
358                         break;
359                     }
360                 }
361                 if (!used) {
362                     tidLst.Add(tid);
363                     break;
364                 }
365                 else {
366                     tid += 1;
367                 }
368             }
369         }
370 
371         return tid;
372     }
373     private void RecycleTid() {
374         for (int i = 0; i < recTidLst.Count; i++) {
375             int tid = recTidLst[i];
376 
377             for (int j = 0; j < tidLst.Count; j++) {
378                 if (tidLst[j] == tid) {
379                     tidLst.RemoveAt(j);
380                     break;
381                 }
382             }
383         }
384         recTidLst.Clear();
385     }
386     private void LogInfo(string info) {
387         if (taskLog != null) {
388             taskLog(info);
389         }
390     }
391     private double GetUTCMilliseconds() {
392         TimeSpan ts = DateTime.UtcNow - startDateTime;
393         return ts.TotalMilliseconds;
394     }
395     #endregion
396 
397     
398 }
399 
400 class PETimeTask
401 {
402     public int tid;
403     public Action callback;
404     public double destTime;//单位:毫秒
405     public double delay;
406     public int count;
407 
408     public PETimeTask(int tid, Action callback, double destTime, double delay, int count)
409     {
410         this.tid = tid;
411         this.callback = callback;
412         this.destTime = destTime;
413         this.delay = delay;
414         this.count = count;
415     }
416 }
417 
418 class PEFrameTask
419 {
420     public int tid;
421     public Action callback;
422     public int destFrame;
423     public int delay;
424     public int count;
425 
426     public PEFrameTask(int tid, Action callback, int destFrame, int delay, int count)
427     {
428         this.tid = tid;
429         this.callback = callback;
430         this.destFrame = destFrame;
431         this.delay = delay;
432         this.count = count;
433     }
434 }
435 
436 public enum PETimeUnit {
437     Millisecond,
438     Second,
439     Minute,
440     Hour,
441     Day
442 }

使用方法:

 1 //实例化计时类
 2 PETimer pt = new PETimer();
 3 //时间定时任务
 4 pt.AddTimeTask(TimerTask, 500, PETimeUnit.Millisecond, 3);
 5 //帧数定时任务
 6 pt.AddFrameTask(FrameTask, 100, 3);
 7 
 8 int tempID = pt.AddTimeTask(() => {
 9     Debug.Log("定时等待替换......");
10 }, 1, PETimeUnit.Second, 0);
11 
12 //定时任务替换
13 pt.ReplaceTimeTask(tempID, () => {
14     Debug.Log("定时任务替换完成......");
15 }, 2, PETimeUnit.Second, 0);
16 
17 //定时任务删除
18 pt.DeleteTimeTask(tempID);
19 
20 //定时检测与处理由MonoBehaviour中的Update()函数来驱动
21 void Update() {
22     pt.Update();
23 }

转自https://github.com/PlaneZhong/PETimer

原文地址:https://www.cnblogs.com/huangzongyi/p/10579734.html