• Unity3d通用工具类之定时触发器


    时隔多日,好不容易挤出点时间来写写博文。不容易,请送我几朵红花,点个赞也行。

    今天呢,我们主要来扩展下通用工具类==>定时触发器。

    顾名思义,所谓的定时触发器,就是告诉程序在过多长时间后,我要执行某个特定的任务。

    比如举个小栗子:

    电饭煲,相信大家都用过,当我们出去工作或者上学的时候,我们只要设置下煮饭时间,就可以安心的离开。

    电饭煲会自动的开始计时工作,等到了你设置的时间后,他就会自动的开始煮饭啊什么的。而你却可以在远在千里的上班。

    智能化,对就是这样的效果。我们今天就来写写这个智能的小东西。

    首先在设计这个小功能之前,我们要明白自己需要的是什么?如何设计?

    1.需要什么:

    (1)肯定要有个管理定时器的类,命名TimeTaskManager。(上网查了下定时器英文可以为:TimeTask,所以就取了这个名字)

    (2)既然有了这个管理类,那么这个管理类要管理什么东西?对喽,是你所要定时执行的任务。那么这个任务要包含什么东西?

        1.多久时间开始执行任务肯定要,

        2.重复执行间隔(有些任务要定时的重复执行,比如像机器加工厂的机器昼夜重复一个加工动作)

    ok,我们命名为TimeTask

    2.如何设计:

    当我们设计一个个有相关联的类的时候,我们可能需要纸笔来打草稿,其实完全不用,学过uml的同学可以新手拈来。这里呢我推荐使用Process On这个工具。在线绘画工具,非常好用。

    这里我们边设计边画图:

    首先从TimeTask下手,对于这个类,我们要想作为一个任务,而且还是定时的。那么一下就能想到,任务执行用委托。还有程序肯定有许多任务,所以要定义一个id识别这个唯一任务。

    那么定时肯定也需要一些变量,

      1.private uint id;//任务id

      2.private uint interval;//间隔多少秒,重复这个任务

      3.private Action action;//无参委托

    看到这里,这个Timetask任务类,大致建立好了。

    哎!细心的同学可能会发现,这个Action委托是个无参委托,那么假如说我的任务方法有带参的怎么办呢?哎,那么问题就来了。

    那么我再设计一个带一个参数的Timetask<T>类,然后Action<T> action不就行了。那二个参数呢,三个参数呢......?

    有多少个参数,你都要设计多少个类。

    所以,对于这样的情况,我们需要把Timetask抽象成一个基类,命名为AbstractTimeTask

    哎!只要我们所有无参带参的TimeTask都继承与AbstractTimeTask抽象类,这样代码的复用性就大大提高了。

    设计好了之后,我们编写代码:

    AbstractTimeTask:

    using UnityEngine;
    using System.Collections;
    using System;
    #region 模块信息
    /*----------------------------------------------------------------
    // 模块名:AbstractTimeT
    // 创建者:chen
    // 修改者列表:
    // 创建日期:2015.11.5
    // 模块描述//----------------------------------------------------------------*/
    #endregion
    public abstract class AbstractTimeTask
    {
    	#region 字段
        private uint m_uiTimeId;//任务id
        private int m_iInterval;//任务重复时间间隔,为0不重复
        private ulong m_ulNextTick;//下一次触发的时间点
    	#endregion
    	#region 属性
        public uint TimeId
        {
            get 
            {
                return m_uiTimeId;
            }
            set 
            {
                m_uiTimeId = value;
            }
        }
        public int Interval 
        {
            get { return m_iInterval; }
            set { m_iInterval = value; }
        }
        public ulong NextTick 
        {
            get
            {
                return m_ulNextTick;
            }
            set 
            {
                this.m_ulNextTick = value;
            }
        }
        /// <summary>
        /// 抽象属性,给子类自定义自己的action委托
        /// </summary>
        public abstract Action Action
        {
            get;
            set;
        }
    	#endregion
    	#region 公有方法
        /// <summary>
        /// 抽象方法,给自己自己定义执行委托
        /// </summary>
        public abstract void DoAction();
    	#endregion
    }
    

    TimeTask:(这里主要先讲无参)

    using UnityEngine;
    using System.Collections;
    using System;
    #region 模块信息
    /*----------------------------------------------------------------
    // 模块名:TimeTask
    // 创建者:chen
    // 修改者列表:
    // 创建日期:2015.11.5
    // 模块描述:定时触发任务类
    //----------------------------------------------------------------*/
    #endregion
    public class TimeTask : AbstractTimeTask
    {
    	#region 字段
        private Action m_action;//定义自己的委托
    	#endregion
    	#region 属性
        public override Action Action
        {
            get
            {
                return m_action;
            }
            set
            {
                m_action = value;
            }
        }
    	#endregion
    	#region 公有方法
        /// <summary>
        /// 重新父类的委托方法
        /// </summary>
        public override void DoAction()
        {
            m_action();
        }
    	#endregion
    }
    

      

    这里我们增加了NextTick字段,有什么卵用呢?主要是用来与当前程序运行时间比较,如果刚好等于这个NextTick值时,就触发委托函数,执行任务。

    细想一下,我们定时管理器类要把任务一个个加到队列里面管理,那么肯定需要一个时间变量与task里面的时间变量进行比较。

    所以,定时管理类就需要一个static uint tick变量来记录程序运行总的时间,如果吧task加到队列里面,task的NextTick=程序运行的总的时间tick+start(多久之后执行任务);还有就是如果task的interval的值大于0,也就是说有重复的执行,那么,就需要再加上interval的值,然后再加入到队列里面。

    分析了这么多,接着来写管理类:

    TimeTaskManager:

    using UnityEngine;
    using System.Collections.Generic;
    using System.Diagnostics;
    using System;
    #region 模块信息
    /*----------------------------------------------------------------
    // 模块名:TimeTaskManager
    // 创建者:chen
    // 修改者列表:
    // 创建日期:2015.11.5
    // 模块描述:定时触发器管理类
    //----------------------------------------------------------------*/
    #endregion
    public class TimeTaskManager 
    {
    	#region 字段
        private static uint m_uiNextTimeId;//总的id,需要分配给task,也就是每加如一个task,就自增
        private static uint m_uiTick;//总的时间,用来和task里面的nexttick变量来进行比较,看是否要触发任务
        private static Queue<AbstractTimeTask> m_queue;
        private static Stopwatch m_stopWatch;//c#自带的计时器,不会的自行百度
        private static readonly object m_queueLock = new object();//队列锁
    	#endregion
    	#region 构造方法
        private TimeTaskManager()
        {
     
        }
        static TimeTaskManager()
        {
            m_queue = new Queue<AbstractTimeTask>();
            m_stopWatch = new Stopwatch();
        }
    	#endregion
    	#region 公有方法
        /// <summary>
        /// 吧Task加入到队列里面来管理,既然是个管理器肯定要有个添加task的操作
        /// </summary>
        /// <param name="start">多久之后开始执行ms</param>
        /// <param name="interval">重复时间间隔ms</param>
        /// <param name="action">任务委托</param>
        /// <returns>任务id</returns>
        public static uint AddTimer(uint start, int interval, Action action)
        {
            AbstractTimeTask task = GetTimeTask(new TimeTask(), start, interval, action);
            lock (m_queueLock)
            {
                m_queue.Enqueue(task);
            }
            return task.TimeId;
        }
        /// <summary>
        /// 周期性执行
        /// </summary>
        public static void Tick()
        {
            TimeTaskManager.m_uiTick += (uint)(m_stopWatch.ElapsedMilliseconds);
            //nityEngine.Debug.Log(TimeTaskManager.m_uiTick);
            m_stopWatch.Reset();
            m_stopWatch.Start();
            while (m_queue.Count != 0)
            {
                AbstractTimeTask task;
                lock (m_queueLock)
                {
                    task = m_queue.Peek();//这里注意队列并没有删除元素,只是放回元素,元素还在队列里面
                }
                if (TimeTaskManager.m_uiTick < task.NextTick)//如果程序的总时间小于task要执行的时间点,就break点,继续等待
                {
                    break;
                }
                lock (m_queueLock)
                {
                    m_queue.Dequeue();
                }
                if (task.Interval > 0)//如果需要重复的话
                {
                    task.NextTick += (ulong)task.Interval;
                    lock (m_queueLock)
                    {
                        m_queue.Enqueue(task);//再次加入队列里面,注意哦,id不变的
                    }
                    task.DoAction();
                }
                else 
                {
                    task.DoAction();//执行委托
    
                }
            }
        }
    	#endregion
    	#region 私有方法
        private static AbstractTimeTask GetTimeTask(AbstractTimeTask task,uint start,int interval,Action action) 
        {
            task.Interval = interval;
            task.TimeId = ++TimeTaskManager.m_uiNextTimeId;
            task.NextTick = TimeTaskManager.m_uiTick + start;
            task.Action = action;
            return task;
        }
    	#endregion
    }
    

     注意:AddTimer的参数的单位是毫秒,不是秒。

    接下来是实验:

    首先写个Driver,作为驱动类。

    using UnityEngine;
    using System.Collections;
    #region 模块信息
    /*----------------------------------------------------------------
    // 模块名:Driver
    // 创建者:chen
    // 修改者列表:
    // 创建日期:2015.11.5
    // 模块描述:驱动类
    //----------------------------------------------------------------*/
    #endregion
    public class Driver : MonoBehaviour
    {
        void Start()
        {
            TimeTaskManager.AddTimer(5000, 5000, DebugTest);
            InvokeRepeating("Tick", 0, 0.02f);
        }
        void Update()
        {
            
        }
        void Tick()
        {
            TimeTaskManager.Tick();
        }
        void DebugTest()
        {
            Debug.Log("111");
        }
    }
    

     创建一个空物体,然后赋予它这个脚本,作为驱动所有程序脚本。

    运行,发现程序在5秒之后,每隔5秒打印一个111到控制台。

    这个定时类,非常的有用,就比如说网络通信啊,我们可以定时的发送心跳包,还有弹出警告窗口,计时多少秒之后自动关闭等等

  • 相关阅读:
    word2vec层次化softmax理解
    python动态加载模块,并获取模块中的类与方法(类似反射)
    用Python装饰器给函数自动加上try except
    3D打印第二弹:狗牌
    【高等代数】06
    【高等代数】05
    沙箱-guacamole
    关于写博客的一点思考
    二阶段目标检测总结
    Waymo object detect 2D解决方案论文拓展
  • 原文地址:https://www.cnblogs.com/CaomaoUnity3d/p/4940655.html
Copyright © 2020-2023  润新知