Unity3D 使用鏈?zhǔn)骄幊趟枷氲腡imer Trigger

談到鏈?zhǔn)骄幊蹋蠹叶紩氲椒椒〝U(kuò)展,具象一些,就是想到DoTween,但是DoTween鏈?zhǔn)剿枷肜锩孢€有個很有意思的分支,那就是委托+方法+返回This完成的鏈?zhǔn)骄幊?。本文重點來了,那就是學(xué)習(xí)一下這個思路實現(xiàn)一個小巧實用的定時器。

代碼

using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

namespace TimeTrigger
{
    public class Timer
    {
        static List<Timer> timers = new List<Timer>();
        Action<float> UpdateEvent;
        Action EndEvent;
        private float _time = -1;   // 用戶設(shè)定的定時時長
        private bool _loop;          // 是否循環(huán)執(zhí)行
        private bool _ignorTimescale;  // 是否忽略Timescale
        private string _flag;// 用戶指定的定時器標(biāo)志,便于手動清除、暫停、恢復(fù)
        private static TimerDriver driver = null;//拿驅(qū)動器的引用只是為了初始化驅(qū)動器
        private float CurrentTime { get { return _ignorTimescale ? Time.realtimeSinceStartup : Time.time; } }// 獲得當(dāng)前時間
        private float cachedTime;//緩存時間
        float timePassed;        //已經(jīng)過去的時間
        private bool _isFinish = false; //計時器是否結(jié)束
        private bool _isPause = false; //計時器是否暫停

        private static bool showLog = true;  //確認(rèn)是否輸出Debug信息
        public static bool ShowLog { set { showLog = value; } }
        public bool IsPause // 暫停計時器
        {
            get { return _isPause; }
            set
            {
                if (value)
                {
                    Pause();
                }
                else
                {
                    Resum();
                }
            }

        }
        /// <summary>
        /// 構(gòu)造定時器
        /// </summary>
        /// <param name="time">定時時長</param>
        /// <param name="flag">定時器標(biāo)識符</param>
        /// <param name="loop">是否循環(huán)</param>
        /// <param name="ignorTimescale">是否忽略TimeScale</param>
        private Timer(float time, string flag, bool loop = false, bool ignorTimescale = true)
        {
            if (null == driver) driver = TimerDriver.Get; //初始化Time驅(qū)動
            _time = time;
            _loop = loop;
            _ignorTimescale = ignorTimescale;
            cachedTime = CurrentTime;
            if (timers.Exists((v) => { return v._flag == flag; }))
            {
                if (showLog) Debug.LogWarningFormat("【TimerTrigger(容錯)】:存在相同的標(biāo)識符【{0}】!", flag);
            }
            _flag = string.IsNullOrEmpty(flag) ? GetHashCode().ToString() : flag;//設(shè)置辨識標(biāo)志符
        }
        private void Pause() // 暫停計時  
        {
            if (!_isFinish)
            {
               _isPause = true;
            }
        }
        private void Resum() // 繼續(xù)計時  
        {
            if (!_isFinish&&_isPause)
            {
               cachedTime = CurrentTime-timePassed; 
                    _isPause = false;
            }
        }
        private void Update() // 刷新定時器
        {
            if (!_isFinish && !_isPause) //運行中
            {
                timePassed = CurrentTime - cachedTime;
                if (null != UpdateEvent) UpdateEvent(Mathf.Clamp01(timePassed / _time));
                if (timePassed >= _time)
                {
                    if (null != EndEvent) EndEvent();
                    if (_loop)
                    {
                        cachedTime = CurrentTime;
                    }
                    else
                    {
                        Stop();
                    }
                }
            }
        }
        private void Stop() // 回收定時器
        {
            if (timers.Contains(this))
            {
                timers.Remove(this);
            }
            _time = -1;
            _isFinish = true;
            _isPause = false;
            UpdateEvent = null;
            EndEvent = null;
        }
        #region--------------------------靜態(tài)方法擴(kuò)展-------------------------------------
        #region-------------添加定時器---------------
        /// <summary>
        /// 添加定時觸發(fā)器
        /// </summary>
        /// <param name="time">定時時長</param>
        /// <param name="flag">定時器標(biāo)識符</param>
        /// <param name="loop">是否循環(huán)</param>
        /// <param name="ignorTimescale">是否忽略TimeScale</param>
        public static Timer AddTimer(float time, string flag = "", bool loop = false, bool ignorTimescale = true)
        {
            Timer timer = new Timer(time, flag, loop, ignorTimescale);
            timers.Add(timer);
            return timer;
        }
        #endregion

        #region-------------刷新所有定時器---------------
        public static void UpdateAllTimer()
        {
            for (int i = 0; i < timers.Count; i++)
            {
                if (null != timers[i])
                {
                    timers[i].Update();
                }
            }
        }
        #endregion

        #region-------------暫停和恢復(fù)定時器---------------
        /// <summary>
        /// 暫停用戶指定的計時觸發(fā)器
        /// </summary>
        /// <param name="flag">指定的標(biāo)識符</param>
        public static void PauseTimer(string flag)
        {
            Timer timer = timers.Find((v) => { return v._flag == flag; });
            if (null != timer)
            {
                timer.Pause();
            }
        }
        /// <summary>
        /// 恢復(fù)用戶指定的計時觸發(fā)器
        /// </summary>
        /// <param name="flag">指定的標(biāo)識符</param>
        public static void ResumTimer(string flag)
        {
            Timer timer = timers.Find((v) => { return v._flag == flag; });
            if (null != timer)
            {
                timer.Resum();
            }
         }

        #endregion
        #region-------------刪除定時器---------------
        /// <summary>
        /// 刪除用戶指定的計時觸發(fā)器
        /// </summary>
        /// <param name="flag">指定的標(biāo)識符</param>
        public static void DelTimer(string flag)
        {
            Timer timer = timers.Find((v) => { return v._flag == flag; });
            if (null != timer)
            {
                timer.Stop();
            }
        }
        /// <summary>
        /// 刪除用戶指定的計時觸發(fā)器
        /// </summary>
        /// <param name="flag">指定的定時器</param>
        public static void DelTimer(Timer timer)
        {
            if (timers.Contains(timer))
            {
                timer.Stop();
            }
          }
        /// <summary>
        /// 刪除用戶指定的計時觸發(fā)器
        /// </summary>
        /// <param name="completedEvent">指定的完成事件(直接賦值匿名函數(shù)無效)</param>
        public static void DelTimer(Action completedEvent)
        {
            Timer timer = timers.Find((v) => { return v.EndEvent == completedEvent; });
            if (null != timer)
            {
                timer.Stop();
            }
        }
        /// <summary>
        /// 刪除用戶指定的計時觸發(fā)器
        /// </summary>
        /// <param name="updateEvent">指定的Update事件(直接賦值匿名函數(shù)無效)</param>
        public static void DelTimer(Action<float> updateEvent)
        {
            Timer timer = timers.Find((v) => { return v.UpdateEvent == updateEvent; });
            if (null != timer)
            {
                timer.Stop();
            }
        }
        #endregion
        #endregion

        #region-------------添加事件-------------------
        public Timer OnCompleted(Action completedEvent) //添加完成事件
        {
            if (null == EndEvent)
            {
                EndEvent = completedEvent;
            }
            return this;
        }
        public Timer OnUpdated(Action<float> updateEvent) //添加update更新事件
        {
            if (null == UpdateEvent)
            {
                UpdateEvent = updateEvent;
            }
            return this;
        }

        #endregion

        #region ---------------運行中的定時器參數(shù)修改-----------
        public void Setloop(bool loop) // 設(shè)置運行中的定時器的loop狀態(tài)
        {
            if (!_isFinish)
            {
                _loop = loop;
            }
        }
        public void SetIgnoreTimeScale(bool ignoreTimescale)// 設(shè)置運行中的定時器的ignoreTimescale狀態(tài)
        {
            if (!_isFinish)
            {
                _ignorTimescale = ignoreTimescale;
            }
        }
        #endregion

    }

    public class TimerDriver : MonoBehaviour
    {
        #region 單例
        private static TimerDriver _instance;
        public static TimerDriver Get
        {
            get
            {
                if (null == _instance)
                {
                    _instance = FindObjectOfType<TimerDriver>() ?? new GameObject("TimerEntity").AddComponent<TimerDriver>();
                }
                return _instance;
            }
        }
        private void Awake()
        {
            _instance = this;
        }
        #endregion
        private void Update()
        {
            Timer.UpdateAllTimer();
        }
    }
}

Tips:上面腳本為了節(jié)省篇幅,刪除了很多注釋和輸出Log信息,可以下載下面的Demo體驗更好,Demo含示例。

使用示例:

添加回調(diào)
Timer交互

Tips:使用Timer.DelTimer("12545");即可隨時停止計時器,當(dāng)然,DelTimer也提供了多個重載。

動畫示例:

Tips: 演示了延時+是否循環(huán)+是否忽略TimeScale+回調(diào),當(dāng)然也包含Lambda表達(dá)式支持下的多參數(shù)回調(diào)。

暫停與恢復(fù)

Tips:由于所有定時器的驅(qū)動是同一個,并非Itween的那種對誰起作用就綁定在誰身上驅(qū)動。所以,如果回調(diào)中用到了游戲?qū)嶓w對象且對象總是顯示隱藏不停切換的話,建議定時器也隨之暫停和恢復(fù).避免游戲?qū)ο蟛豢梢娏?,定時器依舊觸發(fā)的尷尬局面(Itween是隨著游戲?qū)ο蟮娘@示隱藏自動暫停和恢復(fù)的,畢竟就在那個游戲?qū)ο笊向?qū)動嘛)

寫到最后:

這個定時器使用了靜態(tài)的管理器,所以請養(yǎng)成銷毀游戲?qū)ο髸r主動移除事件的好習(xí)慣哈~
這個計時核心算法(假裝是算法哈)除了上面這個方案,還有-=(+=)Time.deltatime,還有 協(xié)程
說道協(xié)程,這里筆者也不是不想封裝,有,只是后來發(fā)現(xiàn)協(xié)程的頻繁開啟停止發(fā)現(xiàn)有安全問題。所以摒棄了。
再說協(xié)程怎么點對點關(guān)閉呢,剛剛好,關(guān)于協(xié)程管理的筆者也恰寫了點文字可以挪步瞧一瞧的;
Unity3D 協(xié)程管理
Unity3D IEnumerator 做一個通用的定時器

筆者編程實數(shù)小菜級別,互相學(xué)習(xí)咯。

Demo下載:https://pan.baidu.com/s/1pHSX6-6cTfI6KEKy2xqKmA

擴(kuò)展閱讀

【大話鏈?zhǔn)骄幊讨〞r器Timer(四)】-開發(fā)者專欄-【游戲蠻?!?/a>,有木有很熟悉的感覺?嗯哼,就是本文的詳盡版啦~

標(biāo)簽: 定時器,定時觸發(fā)器, 鏈?zhǔn)骄幊?,Timer ,TimeTrigger

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容