zoukankan      html  css  js  c++  java
  • 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到控制台。

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

  • 相关阅读:
    (转)【web前端培训之前后端的配合(中)】继续昨日的故事
    ural(Timus) 1136. Parliament
    scau Josephus Problem
    ACMICPC Live Archive 6204 Poker End Games
    uva 10391 Compound Words
    ACMICPC Live Archive 3222 Joke with Turtles
    uva 10132 File Fragmentation
    uva 270 Lining Up
    【转】各种字符串哈希函数比较
    uva 10905 Children's Game
  • 原文地址:https://www.cnblogs.com/CaomaoUnity3d/p/4940655.html
Copyright © 2011-2022 走看看