Unity定时回调(Mono)(代替协程)(分析协程)

因为协程开启后比较难控制管理

生命周期

然后我们想去写要先去了解unity的生命周期
有我们常用的Awake Start Update OnDestory


image.png

Update 帧率不固定 看硬件性能和逻辑复杂度
FixedUpdate 固定帧率 调用时间间隔不确定 用于物理计算准确(刚体)
在这里设置


image.png

比如1s50次 但是我们计算要花费时间所以有个最大计算时间,如果计算时间超过最大计算时间就不算了,不然CPU可能会当机,效果合算满有偏差,但是肉眼看不出来

LateUpdate 相机用比较好会没那么抖动
OnEnable 在Awake 和 Start之间 激活调用
OnDisable失活调用 对象池这两个用的比较多

脚本的生命周期

比如挂在同一个物体上的A脚本要运行在B之前运行
在这里添加脚本可以改脚本运行时间


image.png

那同一个脚本挂不同物体呢
那么就没办法从上面改变去控制先后
我们可以不用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执行下一个


image.png

字符串传参只能传一个参数 而且性能开销比直接传类大一点 其实可以忽略不计
然后停止协程


image.png

但是这个只能停止传参是字符串启动的 就下面这个
image.png

这种这样才能停止


image.png

image.png

image.png

或者


image.png

image.png

image.png

这样就很麻烦 要保留引用 协程多了就很难受

还有个粗暴的方法


image.png

直接干掉所有协程

还有一种萌新最容易以为可以停止的方式


image.png

image.png

没错没报错 停不下来 因为要的是引用不是重新传参

image.png

协程套协程呢 然后是


image.png

然后我们在3s之后出了124之后把这个脚本失活 再激活


image.png

之后的就没有了 之后会解释为什么
image.png

运行没报错 这样的话就是在主线程调用的

image.png

yield都是在这里调用的
然后他们都在OnEnable OnDisable之间所以激活失活相当于把之间的一段帧就给去除掉了 然后资源在OnDisable释放 所以不会运行

yield返回的值
null 或者数字 字符串 bool 函数 嵌套协程
null 数字 字符串 会在下一帧Update()函数运行之后继续运行
就像这样异步加载资源


image.png
image.png

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()
    {
      
    }
}

image.png

挂在一起 创一个Btn拖进去 然后点击


image.png

简单的定时器就好了

然后加上时间转换 循环功能 和移除、

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()
    {

    }
}

image.png

然后在他一次之后移除他


image.png

image.png

没有就是个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运行


image.png

image.png

替换移除都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的一个计时管理 然而服务器不能用 下次改成服务器的

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 218,525评论 6 507
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,203评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,862评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,728评论 1 294
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,743评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,590评论 1 305
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,330评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,244评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,693评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,885评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,001评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,723评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,343评论 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,919评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,042评论 1 270
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,191评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,955评论 2 355