因为协程开启后比较难控制管理
生命周期
然后我们想去写要先去了解unity的生命周期
有我们常用的Awake Start Update OnDestory
Update 帧率不固定 看硬件性能和逻辑复杂度
FixedUpdate 固定帧率 调用时间间隔不确定 用于物理计算准确(刚体)
在这里设置
比如1s50次 但是我们计算要花费时间所以有个最大计算时间,如果计算时间超过最大计算时间就不算了,不然CPU可能会当机,效果合算满有偏差,但是肉眼看不出来
LateUpdate 相机用比较好会没那么抖动
OnEnable 在Awake 和 Start之间 激活调用
OnDisable失活调用 对象池这两个用的比较多
脚本的生命周期
比如挂在同一个物体上的A脚本要运行在B之前运行
在这里添加脚本可以改脚本运行时间
那同一个脚本挂不同物体呢
那么就没办法从上面改变去控制先后
我们可以不用Awake 和Start 去调用
可以public void AwakeA public void AwakeB 两个方法
然后手动调用控制谁先谁后初始化
上面在Script Execution Order改的话代码多了不好管理 不建议用
全局的话不要用Unity自带的初始化 只留一个主类,然后在里面调用个各类的初始化 好管理
如果有三个物体 都有一个脚本 运行他们三个调用顺序我们不能控制
在主线程,并不是一起运行的
Unity每一帧 会先遍历所有物体的Start启动 然后遍历所有Update启动
所以优化的话自动生成的Start Update不用的话就去掉
一个帧循环 :就是比如从unity主线程开始遍历所有Start开始到下一次遍历所有Start开始叫一个帧循环
遍历所有UpDate开始到下一次遍历所有UpDate 也算 也就是生命周期的一个函数从他开始遍历到下一次开始遍历就算一次帧循环
当有大量物理运算在FixedUpdate时 就会在Update里多次执行FixedUpdate 比如FixedUpdate是0.02一次 Update如果是到0.03一次
那么FixedUpdate会被调用两次 因为FixedUpdate比较快
主线程概念
Unity主线程是单线程 但是可以使用多线程 但是一些关键数据只能再主线程访问 为了数据安全,还有降低编程难度 比如有三个线程同时要调用一个物体的transform都去改值到底算谁的呢 所以Unity只能再主线程访问 C#的线程要加上一个线程锁,保护线程只能一个时间只有一个去修改 但是我们做Unity项目中却很少用到
那为什么 我们unity做一些东西时候还是很快的
因为unity自带一个线程池 还有封装的异步方法 可以让耗时间的任务扔给底层运行
协程
Unity有自带的协程 (不是线程)
协程主要用异步加载多一点
可以让主线程不卡死等待一秒执行
可以等待停止几秒执行 还是并行的 下面是等2s执行下一个
字符串传参只能传一个参数 而且性能开销比直接传类大一点 其实可以忽略不计
然后停止协程
但是这个只能停止传参是字符串启动的 就下面这个
这种这样才能停止
或者
这样就很麻烦 要保留引用 协程多了就很难受
还有个粗暴的方法
直接干掉所有协程
还有一种萌新最容易以为可以停止的方式
没错没报错 停不下来 因为要的是引用不是重新传参
协程套协程呢 然后是
然后我们在3s之后出了124之后把这个脚本失活 再激活
之后的就没有了 之后会解释为什么
运行没报错 这样的话就是在主线程调用的
yield都是在这里调用的
然后他们都在OnEnable OnDisable之间所以激活失活相当于把之间的一段帧就给去除掉了 然后资源在OnDisable释放 所以不会运行
yield返回的值
null 或者数字 字符串 bool 函数 嵌套协程
null 数字 字符串 会在下一帧Update()函数运行之后继续运行
就像这样异步加载资源
WaitForSeconds:延迟指定的时间之后,在所有的Update()函数运行之后继续运行
WaitForFixedUpdate:在所有FixedUpdate() 函数运行之后继续运行。(注意这里不是指下一帧,因为FixedUpdate()的帧率和Update()是不一样的)
WWWW:当WWW资源加载成功以后继续运行。(其它的异步资源加载函数也是一样)
所以Unity把一个多线程的游戏封装成一个单线程的游戏已经很强大了,易上手。
为什么要定时回调
当有50个定时 技能啊 buff啊 什么的也不能开协程50个吧 而且有时候要取消 而且也是高度依赖MonoBehaviour 在服务器没Unity怎么运行
服务器可能有上千个 比较不方便不通用 不过适合用于异步加载资源
我们要实现一个帧的定时 和一个定时器
定时类
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System;
public class PETimeTask
{
public float destTime;
public Action callBack;
}
/// <summary>
/// 支持时间定时,帧定时
/// 定时任务可循环 取消 替换
/// </summary>
public class TimerSys :MonoBehaviour
{
//单例
public static TimerSys Instance;
List<PETimeTask> taskTimes = new List<PETimeTask>();
public void Init()
{
Instance = this;
}
void Update()
{
//遍历检测任务是否到达条件
for (int i = 0; i < taskTimes.Count; i++)
{
PETimeTask task = taskTimes[i];
if (Time.realtimeSinceStartup < task.destTime)
{
continue;
}
else
{
//时间到 callBack不为空调用
task.callBack?.Invoke();
taskTimes.RemoveAt(i);
i--;
}
}
}
public void AddTimeTask(Action callBack, float delay)
{
//从游戏开始到现在的时间
float destTime = Time.realtimeSinceStartup + delay;
PETimeTask timeTask = new PETimeTask
{
destTime = destTime,
callBack = callBack
};
taskTimes.Add(timeTask);
}
}
调用类
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class GameRoot : MonoBehaviour
{
public Button btn;
// Start is called before the first frame update
void Start()
{
//初始化定时类
TimerSys timerSys = GetComponent<TimerSys>();
timerSys.Init();
btn.onClick.AddListener(ClickAddBtn);
}
public void ClickAddBtn()
{
TimerSys.Instance.AddTimeTask(FunA,2);
}
void FunA()
{
Debug.Log("到了");
}
// Update is called once per frame
void Update()
{
}
}
挂在一起 创一个Btn拖进去 然后点击
简单的定时器就好了
然后加上时间转换 循环功能 和移除、
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System;
public class PETimeTask
{
public int tid;
public float destTime;//单位:毫秒
public Action callBack;
public float delay;
public int count;//次数
public PETimeTask(int tid, float destTime, Action callBack, float delay, int count)
{
this.tid = tid;
this.destTime = destTime;
this.callBack = callBack;
this.count = count;
this.delay = delay;
}
}
public enum EPETimeUnit
{
Millisecond = 0,
Second,
Minute,
Hour,
Day
}
/// <summary>
/// 支持时间定时,帧定时
/// 定时任务可循环 取消 替换
/// </summary>
public class TimerSys : MonoBehaviour
{
//单例
public static TimerSys Instance;
//声明锁
static readonly string obj = "lock";
int tid;
List<int> tids = new List<int>();
/// <summary>
/// 临时列表 支持多线程操作 错开时间操作 避免使用锁 提升操作效率
/// </summary>
List<PETimeTask> tmpTimes = new List<PETimeTask>();
List<PETimeTask> taskTimes = new List<PETimeTask>();
public void Init()
{
Instance = this;
}
void Update()
{
//加入缓存区中的定时任务
for (int i = 0; i < tmpTimes.Count; i++)
{
taskTimes.Add(tmpTimes[i]);
}
tmpTimes.Clear();
//遍历检测任务是否到达条件
for (int i = 0; i < taskTimes.Count; i++)
{
PETimeTask task = taskTimes[i];
if (Time.realtimeSinceStartup * 1000 < task.destTime)
{
continue;
}
else
{
//时间到 callBack不为空调用
task.callBack?.Invoke();
if (task.count == 1)
{
taskTimes.RemoveAt(i);
i--;
}
else
{
if (task.count != 0)
{
task.count -= 1;
}
//重新赋值时间
task.destTime += task.delay;
}
}
}
}
/// <summary>
/// 添加一个计时器
/// </summary>
/// <param name="callBack"></param>
/// <param name="delay"></param>
/// <param name="count"></param>
/// <param name="timeUnit"></param>
/// <returns></returns>
public int AddTimeTask(Action callBack, float delay, int count = 1, EPETimeUnit timeUnit = EPETimeUnit.Millisecond)
{
//时间单位换算 最小毫秒
if (timeUnit != EPETimeUnit.Millisecond)
{
switch (timeUnit)
{
case EPETimeUnit.Second:
delay = delay * 1000;
break;
case EPETimeUnit.Minute:
delay = delay * 1000 * 60;
break;
case EPETimeUnit.Hour:
delay = delay * 1000 * 60 * 60;
break;
case EPETimeUnit.Day:
delay = delay * 1000 * 60 * 60 * 24;
break;
default:
Debug.Log("Add Task TimeUnit Type error");
break;
}
}
int tid = GetTid();
//从游戏开始到现在的时间
float destTime = Time.realtimeSinceStartup * 1000 + delay;
tmpTimes.Add(new PETimeTask(tid, destTime, callBack, delay, count));
tids.Add(tid);
return tid;
}
/// <summary>
/// 移除一个计时器
/// </summary>
/// <param name="tid"></param>
/// <returns></returns>
public bool DeleteTimeTask(int tid)
{
bool exist = false;
for (int i = 0; i < taskTimes.Count; i++)
{
PETimeTask task = taskTimes[i];
if (task.tid == tid)
{
taskTimes.RemoveAt(i);
for (int j = 0; j < tids.Count; j++)
{
if (tids[j] == tid)
{
tids.RemoveAt(j);
break;
}
}
exist = true;
break;
}
}
if (!exist)
{
for (int i = 0; i < tmpTimes.Count; i++)
{
PETimeTask task = tmpTimes[i];
if (task.tid==tid)
{
tmpTimes.RemoveAt(i);
for (int j = 0; j < tids.Count; j++)
{
if (tids[j] == tid)
{
tids.RemoveAt(j);
break;
}
}
exist = true;
break;
}
}
}
return exist;
}
public int GetTid()
{
lock (obj)
{
tid += 1;
//安全代码,以防万一(服务器)
while (true)
{
if (tid == int.MaxValue)
{
tid = 0;
}
//最后一个归0后从新赋值唯一id
bool used = false;
for (int i = 0; i < tids.Count; i++)
{
if (tid == tids[i])
{
used = true;
break;
}
}
if (!used)
{
break;
}
else
{
tid += 1;
}
}
}
return tid;
}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class GameRoot : MonoBehaviour
{
public Button btn;
public Button btnD;
int tid;
// Start is called before the first frame update
void Start()
{
//初始化定时类
TimerSys timerSys = GetComponent<TimerSys>();
timerSys.Init();
btn.onClick.AddListener(ClickAddBtn);
btnD.onClick.AddListener(ClickDelBtn);
}
public void ClickAddBtn()
{
tid = TimerSys.Instance.AddTimeTask(FunA, 2000, 3);
}
public void ClickDelBtn()
{
bool ret = TimerSys.Instance.DeleteTimeTask(tid);
Debug.Log("移除"+ret);
}
void FunA()
{
Debug.Log("到了");
}
// Update is called once per frame
void Update()
{
}
}
然后在他一次之后移除他
没有就是个false
然后添加替换功能还有 加上帧计时
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System;
public class PETimeTask
{
public int tid;
public float destTime;//单位:毫秒
public Action callBack;
public float delay;
public int count;//次数
public PETimeTask(int tid, float destTime, Action callBack, float delay, int count)
{
this.tid = tid;
this.destTime = destTime;
this.callBack = callBack;
this.count = count;
this.delay = delay;
}
}
public class PEFrameTask
{
public int tid;
public int destFrame;//单位:毫秒
public Action callBack;
public int delay;
public int count;//次数
public PEFrameTask(int tid, int destFrame, Action callBack, int delay, int count)
{
this.tid = tid;
this.destFrame = destFrame;
this.callBack = callBack;
this.count = count;
this.delay = delay;
}
}
public enum EPETimeUnit
{
Millisecond = 0,
Second,
Minute,
Hour,
Day
}
/// <summary>
/// 支持时间定时,帧定时
/// 定时任务可循环 取消 替换
/// </summary>
public class TimerSys : MonoBehaviour
{
//单例
public static TimerSys Instance;
//声明锁
static readonly string obj = "lock";
int tid;
List<int> tids = new List<int>();
/// <summary>
/// tid缓存回收
/// </summary>
List<int> recTids = new List<int>();
/// <summary>
/// 临时列表 支持多线程操作 错开时间操作 避免使用锁 提升操作效率
/// </summary>
List<PETimeTask> tmpTimes = new List<PETimeTask>();
List<PETimeTask> taskTimes = new List<PETimeTask>();
int frameCounter;
List<PEFrameTask> tmpFrames = new List<PEFrameTask>();
List<PEFrameTask> taskFrames = new List<PEFrameTask>();
public void Init()
{
Instance = this;
}
void Update()
{
CheckTimeTask();
CheckFrameTask();
if (recTids.Count > 0)
{
RecycleTid();
}
}
void CheckTimeTask()
{
//加入缓存区中的定时任务
for (int i = 0; i < tmpTimes.Count; i++)
{
taskTimes.Add(tmpTimes[i]);
}
tmpTimes.Clear();
//遍历检测任务是否到达条件
for (int i = 0; i < taskTimes.Count; i++)
{
PETimeTask task = taskTimes[i];
if (Time.realtimeSinceStartup * 1000 < task.destTime)
{
continue;
}
else
{
try
{
//时间到 callBack不为空调用
task.callBack?.Invoke();
}
catch (Exception e)
{
Debug.Log(e.ToString());
}
if (task.count == 1)
{
taskTimes.RemoveAt(i);
i--;
recTids.Add(task.tid);
}
else
{
if (task.count != 0)
{
task.count -= 1;
}
//重新赋值时间
task.destTime += task.delay;
}
}
}
}
void CheckFrameTask()
{
//加入缓存区中的定时任务
for (int i = 0; i < tmpFrames.Count; i++)
{
taskFrames.Add(tmpFrames[i]);
}
tmpFrames.Clear();
frameCounter += 1;
//遍历检测任务是否到达条件
for (int i = 0; i < taskFrames.Count; i++)
{
PEFrameTask task = taskFrames[i];
if (frameCounter < task.destFrame)
{
continue;
}
else
{
try
{
//时间到 callBack不为空调用
task.callBack?.Invoke();
}
catch (Exception e)
{
Debug.Log(e.ToString());
}
if (task.count == 1)
{
taskFrames.RemoveAt(i);
i--;
recTids.Add(task.tid);
}
else
{
if (task.count != 0)
{
task.count -= 1;
}
//重新赋值时间
task.destFrame += task.delay;
}
}
}
}
#region TimeTask
/// <summary>
/// 添加一个计时器
/// </summary>
/// <param name="callBack"></param>
/// <param name="delay"></param>
/// <param name="count"></param>
/// <param name="timeUnit"></param>
/// <returns></returns>
public int AddTimeTask(Action callBack, float delay, int count = 1, EPETimeUnit timeUnit = EPETimeUnit.Millisecond)
{
//时间单位换算 最小毫秒
if (timeUnit != EPETimeUnit.Millisecond)
{
switch (timeUnit)
{
case EPETimeUnit.Second:
delay = delay * 1000;
break;
case EPETimeUnit.Minute:
delay = delay * 1000 * 60;
break;
case EPETimeUnit.Hour:
delay = delay * 1000 * 60 * 60;
break;
case EPETimeUnit.Day:
delay = delay * 1000 * 60 * 60 * 24;
break;
default:
Debug.Log("Add Task TimeUnit Type error");
break;
}
}
int tid = GetTid();
//从游戏开始到现在的时间
float destTime = Time.realtimeSinceStartup * 1000 + delay;
tmpTimes.Add(new PETimeTask(tid, destTime, callBack, delay, count));
tids.Add(tid);
return tid;
}
/// <summary>
/// 移除一个计时器
/// </summary>
/// <param name="tid"></param>
/// <returns></returns>
public bool DeleteTimeTask(int tid)
{
bool exist = false;
for (int i = 0; i < taskTimes.Count; i++)
{
PETimeTask task = taskTimes[i];
if (task.tid == tid)
{
taskTimes.RemoveAt(i);
for (int j = 0; j < tids.Count; j++)
{
if (tids[j] == tid)
{
tids.RemoveAt(j);
break;
}
}
exist = true;
break;
}
}
if (!exist)
{
for (int i = 0; i < tmpTimes.Count; i++)
{
PETimeTask task = tmpTimes[i];
if (task.tid == tid)
{
tmpTimes.RemoveAt(i);
for (int j = 0; j < tids.Count; j++)
{
if (tids[j] == tid)
{
tids.RemoveAt(j);
break;
}
}
exist = true;
break;
}
}
}
return exist;
}
/// <summary>
/// 替换
/// </summary>
/// <param name="tid"></param>
/// <param name="callBack"></param>
/// <param name="delay"></param>
/// <param name="count"></param>
/// <param name="timeUnit"></param>
/// <returns></returns>
public bool ReplaceTimeTask(int tid, Action callBack, float delay, int count = 1, EPETimeUnit timeUnit = EPETimeUnit.Millisecond)
{
//时间单位换算 最小毫秒
if (timeUnit != EPETimeUnit.Millisecond)
{
switch (timeUnit)
{
case EPETimeUnit.Second:
delay = delay * 1000;
break;
case EPETimeUnit.Minute:
delay = delay * 1000 * 60;
break;
case EPETimeUnit.Hour:
delay = delay * 1000 * 60 * 60;
break;
case EPETimeUnit.Day:
delay = delay * 1000 * 60 * 60 * 24;
break;
default:
Debug.Log("Add Task TimeUnit Type error");
break;
}
}
//从游戏开始到现在的时间
float destTime = Time.realtimeSinceStartup * 1000 + delay;
PETimeTask newTask = new PETimeTask(tid, destTime, callBack, delay, count);
bool isRep = false;
for (int i = 0; i < taskTimes.Count; i++)
{
if (taskTimes[i].tid == tid)
{
taskTimes[i] = newTask;
isRep = true;
break;
}
}
if (!isRep)
{
for (int i = 0; i < tmpTimes.Count; i++)
{
if (tmpTimes[i].tid == tid)
{
tmpTimes[i] = newTask;
isRep = true;
break;
}
}
}
return isRep;
}
#endregion
#region FrameTask
/// <summary>
/// 添加一个帧计时器
/// </summary>
/// <param name="callBack"></param>
/// <param name="delay"></param>
/// <param name="count"></param>
/// <param name="timeUnit"></param>
/// <returns></returns>
public int AddFrameTask(Action callBack, int delay, int count = 1)
{
int tid = GetTid();
taskFrames.Add(new PEFrameTask(tid, frameCounter+delay, callBack, delay, count));
tids.Add(tid);
return tid;
}
/// <summary>
/// 移除一个帧计时器
/// </summary>
/// <param name="tid"></param>
/// <returns></returns>
public bool DeleteFrameTask(int tid)
{
bool exist = false;
for (int i = 0; i < taskFrames.Count; i++)
{
PEFrameTask task = taskFrames[i];
if (task.tid == tid)
{
taskFrames.RemoveAt(i);
for (int j = 0; j < tids.Count; j++)
{
if (tids[j] == tid)
{
tids.RemoveAt(j);
break;
}
}
exist = true;
break;
}
}
if (!exist)
{
for (int i = 0; i < tmpFrames.Count; i++)
{
PEFrameTask task = tmpFrames[i];
if (task.tid == tid)
{
tmpFrames.RemoveAt(i);
for (int j = 0; j < tids.Count; j++)
{
if (tids[j] == tid)
{
tids.RemoveAt(j);
break;
}
}
exist = true;
break;
}
}
}
return exist;
}
/// <summary>
/// 替换帧计时器
/// </summary>
/// <param name="tid"></param>
/// <param name="callBack"></param>
/// <param name="delay"></param>
/// <param name="count"></param>
/// <param name="timeUnit"></param>
/// <returns></returns>
public bool ReplaceFrameTask(int tid, Action callBack, int delay, int count = 1)
{
PEFrameTask newTask = new PEFrameTask(tid, frameCounter+delay, callBack, delay, count);
bool isRep = false;
for (int i = 0; i < taskFrames.Count; i++)
{
if (taskFrames[i].tid == tid)
{
taskFrames[i] = newTask;
isRep = true;
break;
}
}
if (!isRep)
{
for (int i = 0; i < tmpFrames.Count; i++)
{
if (tmpFrames[i].tid == tid)
{
tmpFrames[i] = newTask;
isRep = true;
break;
}
}
}
return isRep;
}
#endregion
#region Tool Methonds
public int GetTid()
{
lock (obj)
{
tid += 1;
//安全代码,以防万一(服务器)
while (true)
{
if (tid == int.MaxValue)
{
tid = 0;
}
//最后一个归0后从新赋值唯一id
bool used = false;
for (int i = 0; i < tids.Count; i++)
{
if (tid == tids[i])
{
used = true;
break;
}
}
if (!used)
{
break;
}
else
{
tid += 1;
}
}
}
return tid;
}
/// <summary>
/// tid回收
/// </summary>
void RecycleTid()
{
for (int i = 0; i < recTids.Count; i++)
{
int tid = recTids[i];
for (int j = 0; j < tids.Count; j++)
{
if (tids[j] == tid)
{
tids.RemoveAt(j);
break;
}
}
}
recTids.Clear();
}
#endregion
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class GameRoot : MonoBehaviour
{
public Button btn;
public Button btnD;
public Button btnR;
public Button btnF;
public Button btnFD;
public Button btnFR;
int tid;
// Start is called before the first frame update
void Start()
{
//初始化定时类
TimerSys timerSys = GetComponent<TimerSys>();
timerSys.Init();
btn.onClick.AddListener(ClickAddBtn);
btnD.onClick.AddListener(ClickDelBtn);
btnR.onClick.AddListener(ClickRepBtn);
btnF.onClick.AddListener(ClickAddFBtn);
btnFD.onClick.AddListener(ClickDelFBtn);
btnFR.onClick.AddListener(ClickRepFBtn);
}
public void ClickAddBtn()
{
tid = TimerSys.Instance.AddTimeTask(() => { Debug.Log("到了"); }, 2000, 3);
}
public void ClickDelBtn()
{
bool ret = TimerSys.Instance.DeleteTimeTask(tid);
Debug.Log("移除" + ret);
}
public void ClickRepBtn()
{
bool ret = TimerSys.Instance.ReplaceTimeTask(tid, ()=> { Debug.Log("替换了"); }, 200);
Debug.Log("Rep" + ret);
}
public void ClickAddFBtn()
{
tid = TimerSys.Instance.AddFrameTask(() => { Debug.Log("帧到了"+System.DateTime.Now); }, 50, 0);
}
public void ClickDelFBtn()
{
bool ret = TimerSys.Instance.DeleteFrameTask(tid);
Debug.Log("移除" + ret);
}
public void ClickRepFBtn()
{
bool ret = TimerSys.Instance.ReplaceFrameTask(tid, () => { Debug.Log("替换了帧"); }, 100);
Debug.Log("Rep" + ret);
}
}
然后添加3个新Btn运行
替换移除都OK
然后我优化了下代码 不然有点长 用到了反射所以性能没之前的高
using System.Collections.Generic;
using System;
using System.Reflection;
public class BasePETask
{
public int tid;
public Action callBack;
public int count;//次数
public BasePETask(int tid, Action callBack, int count)
{
this.tid = tid;
this.callBack = callBack;
this.count = count;
}
}
public class PETimeTask : BasePETask
{
public double destTime;//单位:毫秒
public double delay;
public PETimeTask(int tid, double destTime, Action callBack, double delay, int count):base(tid,callBack,count)
{
this.destTime = destTime;
this.delay = delay;
}
}
public class PEFrameTask : BasePETask
{
public int destFrame;//单位:秒
public int delay;
public PEFrameTask(int tid, int destFrame, Action callBack, int delay, int count) : base(tid, callBack, count)
{
this.destFrame = destFrame;
this.delay = delay;
}
}
public enum EPETimeUnit
{
Millisecond = 0,
Second,
Minute,
Hour,
Day
}
/// <summary>
/// 支持时间定时,帧定时
/// 定时任务可循环 取消 替换
/// </summary>
public class PETimer
{
Action<string> taskLog;
//声明锁
static readonly string obj = "lock";
//C#的计时 计算机元年
DateTime startDateTime = new DateTime(1970, 1, 1, 0, 0, 0, 0);
double nowTime;
int tid;
List<int> tids = new List<int>();
/// <summary>
/// tid缓存回收
/// </summary>
List<int> recTids = new List<int>();
/// <summary>
/// 临时列表 支持多线程操作 错开时间操作 避免使用锁 提升操作效率
/// </summary>
List<PETimeTask> tmpTimes = new List<PETimeTask>();
List<PETimeTask> taskTimes = new List<PETimeTask>();
int frameCounter;
List<PEFrameTask> tmpFrames = new List<PEFrameTask>();
List<PEFrameTask> taskFrames = new List<PEFrameTask>();
public void Update()
{
CheckTask(taskTimes, tmpTimes);
CheckTask(taskFrames, tmpFrames);
RecycleTid();
}
void CheckTask<T>(List<T> tasks, List<T> tmps) where T : BasePETask
{
//加入缓存区中的定时任务
for (int i = 0; i < tmps.Count; i++)
{
tasks.Add(tmps[i]);
}
tmps.Clear();
if (typeof(T) == typeof(PETimeTask))
{
nowTime = GetUTCMilliseconds();
}
else if (typeof(T) == typeof(PEFrameTask))
{
frameCounter += 1;
}
//遍历检测任务是否到达条件
for (int i = 0; i < tasks.Count; i++)
{
var task= tasks[i];
bool isTimeUp = false;
if (typeof(T) == typeof(PETimeTask))
{
isTimeUp = nowTime.CompareTo((task as PETimeTask).destTime) > 0;
}
else if (typeof(T) == typeof(PEFrameTask))
{
isTimeUp = frameCounter.CompareTo((task as PEFrameTask).destFrame) > 0;
}
//nowTime>task.destTime 1 nowTime<task.destTime -1 nowTime=task.destTime 0
if (isTimeUp)
{
LogInfo("满足");
try
{
//时间到 callBack不为空调用
task.callBack?.Invoke();
}
catch (Exception e)
{
LogInfo(e.ToString());
}
if (task.count == 1)
{
tasks.RemoveAt(i);
i--;
recTids.Add(task.tid);
}
else
{
if (task.count != 0)
{
task.count -= 1;
}
//重新赋值时间
if (typeof(T) == typeof(PETimeTask))
{
(task as PETimeTask).destTime += (task as PETimeTask).delay;
}
else if (typeof(T) == typeof(PEFrameTask))
{
(task as PEFrameTask).destFrame += (task as PEFrameTask).delay;
}
}
}
}
}
#region TimeTask
/// <summary>
/// 添加一个计时器
/// </summary>
/// <param name="callBack"></param>
/// <param name="delay"></param>
/// <param name="count"></param>
/// <param name="timeUnit"></param>
/// <returns></returns>
public int AddTimeTask(Action callBack, float delay, int count = 1, EPETimeUnit timeUnit = EPETimeUnit.Millisecond)
{
int tid = GetTid();
ChangeTimeWithType(ref delay, timeUnit);
//从游戏开始到现在的时间
nowTime = GetUTCMilliseconds();
tmpTimes.Add(new PETimeTask(tid, nowTime + delay, callBack, delay, count));
return tid;
}
/// <summary>
/// 移除一个计时器
/// </summary>
/// <param name="tid"></param>
/// <returns></returns>
public bool DeleteTimeTask(int tid)
{
return DeleteTask(tid, taskTimes, tmpTimes);
}
/// <summary>
/// 替换
/// </summary>
/// <param name="tid"></param>
/// <param name="callBack"></param>
/// <param name="delay"></param>
/// <param name="count"></param>
/// <param name="timeUnit"></param>
/// <returns></returns>
public bool ReplaceTimeTask(int tid, Action callBack, float delay, int count = 1, EPETimeUnit timeUnit = EPETimeUnit.Millisecond)
{
ChangeTimeWithType(ref delay, timeUnit);
//从游戏开始到现在的时间
nowTime = GetUTCMilliseconds();
PETimeTask newTask = new PETimeTask(tid, nowTime + delay, callBack, delay, count);
return ReplaceTask<PETimeTask>(tid, taskTimes, tmpTimes, newTask);
}
#endregion
#region FrameTask
/// <summary>
/// 添加一个帧计时器
/// </summary>
/// <param name="callBack"></param>
/// <param name="delay"></param>
/// <param name="count"></param>
/// <param name="timeUnit"></param>
/// <returns></returns>
public int AddFrameTask(Action callBack, int delay, int count = 1)
{
int tid = GetTid();
taskFrames.Add(new PEFrameTask(tid, frameCounter + delay, callBack, delay, count));
return tid;
}
/// <summary>
/// 移除一个帧计时器
/// </summary>
/// <param name="tid"></param>
/// <returns></returns>
public bool DeleteFrameTask(int tid)
{
return DeleteTask(tid, taskFrames, tmpFrames); ;
}
/// <summary>
/// 替换帧计时器
/// </summary>
/// <param name="tid"></param>
/// <param name="callBack"></param>
/// <param name="delay"></param>
/// <param name="count"></param>
/// <param name="timeUnit"></param>
/// <returns></returns>
public bool ReplaceFrameTask(int tid, Action callBack, int delay, int count = 1)
{
PEFrameTask newTask = new PEFrameTask(tid, frameCounter + delay, callBack, delay, count);
return ReplaceTask<PEFrameTask>(tid, taskFrames, tmpFrames, newTask);
}
#endregion
/// <summary>
/// 清空数据
/// </summary>
public void Destory()
{
tids.Clear();
recTids.Clear();
tmpTimes.Clear();
taskTimes.Clear();
tmpFrames.Clear();
taskFrames.Clear();
tid = 0;
taskLog = null;
}
public void SetLog(Action<string> log)
{
taskLog = log;
}
#region Tool Methonds
bool DeleteTask<T>(int tid, List<T> tasks, List<T> tmps) where T : BasePETask
{
bool exist = false;
for (int i = 0; i < tasks.Count; i++)
{
T task = tasks[i];
if (task.tid == tid)
{
tasks.RemoveAt(i);
for (int j = 0; j < tids.Count; j++)
{
if (tids[j] == tid)
{
recTids.Add(tid);
break;
}
}
exist = true;
break;
}
}
if (!exist)
{
for (int i = 0; i < tmps.Count; i++)
{
T task = tmps[i];
if (task.tid == tid)
{
tmps.RemoveAt(i);
for (int j = 0; j < tids.Count; j++)
{
if (tids[j] == tid)
{
recTids.Add(tid);
break;
}
}
exist = true;
break;
}
}
}
return exist;
}
bool ReplaceTask<T>(int tid, List<T> tasks, List<T> tmps, T newTask) where T : BasePETask
{
bool isRep = false;
for (int i = 0; i < tasks.Count; i++)
{
if (tasks[i].tid == tid)
{
tasks[i] = newTask;
isRep = true;
break;
}
}
if (!isRep)
{
for (int i = 0; i < tmps.Count; i++)
{
if (tmps[i].tid == tid)
{
tmps[i] = newTask;
isRep = true;
break;
}
}
}
return isRep;
}
int GetTid()
{
lock (obj)
{
tid += 1;
//安全代码,以防万一(服务器)
while (true)
{
if (tid == int.MaxValue)
{
tid = 0;
}
//最后一个归0后从新赋值唯一id
bool used = false;
for (int i = 0; i < tids.Count; i++)
{
if (tid == tids[i])
{
used = true;
break;
}
}
if (!used)
{
tids.Add(tid);
break;
}
else
{
tid += 1;
}
}
}
return tid;
}
/// <summary>
/// tid回收
/// </summary>
void RecycleTid()
{
if (recTids.Count < 0)
return;
for (int i = 0; i < recTids.Count; i++)
{
int tid = recTids[i];
for (int j = 0; j < tids.Count; j++)
{
if (tids[j] == tid)
{
tids.RemoveAt(j);
break;
}
}
}
recTids.Clear();
}
void LogInfo(string info)
{
taskLog?.Invoke(info);
}
/// <summary>
/// 获取时间的方法
/// </summary>
/// <returns></returns>
double GetUTCMilliseconds()
{
//Now是本机时间
//现在世界标准时间-计算机元年时间
TimeSpan ts = DateTime.UtcNow - startDateTime;
//返回TimeSpan值表示的毫秒数
return ts.TotalMilliseconds;
}
void ChangeTimeWithType(ref float delay, EPETimeUnit timeUnit)
{
//时间单位换算 最小毫秒
if (timeUnit != EPETimeUnit.Millisecond)
{
switch (timeUnit)
{
case EPETimeUnit.Second:
delay = delay * 1000;
break;
case EPETimeUnit.Minute:
delay = delay * 1000 * 60;
break;
case EPETimeUnit.Hour:
delay = delay * 1000 * 60 * 60;
break;
case EPETimeUnit.Day:
delay = delay * 1000 * 60 * 60 * 24;
break;
default:
LogInfo("Add Task TimeUnit Type error");
break;
}
}
}
#endregion
}
好了这个是继承Mono的一个计时管理 然而服务器不能用 下次改成服务器的