1. 程式人生 > >Unity計時器(實現暫停,繼續等功能)

Unity計時器(實現暫停,繼續等功能)

       計時器是遊戲開發的一個非常常用的功能,簡單計時器的實現方式有很多中,可以每幀給一個float變數加Time.deltaTime,到一定時間的時候呼叫某個函式。也可以使用unity的協程和InvokeRepeating。但是簡單的使用這些組合呼叫起來不是很方便,並且無法實現暫停,繼續等功能,也無法在計時器執行到某個時間點的時候移除或者新增新的事件。

1.計時器

       這個計時器用到了之前文章裡的Singleton和MonoEvent,連結在此  Unity單例模式及MonoEvent

       直接上程式碼,註釋也很詳細,後面再寫上使用方式。一共兩個類,一個Timer是計時器,TimeManager由於只在Timer中用到,直接定義在Timer裡面。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using OnEventDelegate = System.Action;

public class Timer
{
    private OnEventDelegate timeUpDel;
    /// <summary>
    /// 是否執行
    /// </summary>
    private bool _isRunning;
    /// <summary>
    /// 已執行時間(每次滿足執行間隔就會加這個)
    /// </summary>
    private float _useTime;
    /// <summary>
    /// 執行時間
    /// </summary>
    public float RunTime { get; private set; }
    /// <summary>
    /// 已執行次數
    /// </summary>
    public int UseCount { get; private set; }
    /// <summary>
    /// 執行間隔
    /// </summary>
    public float TimeInterval { get; set; }
    /// <summary>
    /// 設定的執行次數
    /// </summary>
    public int RepeatCount { get; set; }

    /// <summary>
    /// <param name="interval">時間間隔,單位是毫秒</param>
    /// <param name="repeatCount">執行次數,一秒一次的話MaxValue可以執行68年</param>
    /// </summary>
    public Timer(float interval, int repeatCount = int.MaxValue)
    {
        RunTime = 0f;
        TimeInterval = Mathf.Max(interval, 1);  // 最小間隔為1毫秒
        RepeatCount = repeatCount;
    }

    /// <summary>
    /// 是否執行中
    /// </summary>
    public bool IsRunning
    {
        get { return _isRunning; }
        set
        {
            if (_isRunning != value)
            {
                _isRunning = value;
                if (_isRunning)
                {
                    TimerManager.Instance.AddTimer(this);
                }
                else
                {
                    TimerManager.Instance.RemoveTimer(this);
                }
                //這裡可以加一個計時器狀態變換的委託
            }
        }
    }

    /// <summary>
    /// 每幀執行
    /// </summary>
    /// <param name="deltaTime"></param>
    public void Update(float deltaTime)
    {
        if (IsRunning && UseCount < RepeatCount)
        {
            RunTime += deltaTime;
            var f = TimeInterval / 1000;
            while (RunTime - _useTime > f && UseCount < RepeatCount)
            {
                UseCount++;
                _useTime += f;
                timeUpDel?.Invoke();
            }
        }
        if (UseCount >= RepeatCount)
        {
            IsRunning = false;
        }
    }

    /// <summary>
    /// 新增事件
    /// </summary>
    /// <param name="type"></param>
    /// <param name="fun"></param>
    public void AddEventListener(OnEventDelegate fun)
    {
        timeUpDel += fun;
    }

    /// <summary>
    /// 移除事件
    /// </summary>
    /// <param name="type"></param>
    /// <param name="fun"></param>
    public void RemoveEventListener(OnEventDelegate fun)
    {
        timeUpDel -= fun;
    }

    /// <summary>
    /// 開始(呼叫了IsRunning的Set,初始化了TimerManager)
    /// </summary>
    public void Start()
    {
        IsRunning = true;
    }

    /// <summary>
    /// 停止
    /// </summary>
    public void Pause()
    {
        IsRunning = false;
    }

    /// <summary>
    /// 重置
    /// </summary>
    public void ReSet()
    {
        IsRunning = false;
        RunTime = 0f;
        _useTime = 0f;
        UseCount = 0;
    }

    /// <summary>
    /// 計時器管理
    /// 除了計時器以外其他類暫時不需要呼叫,以後需要再放到外面去
    /// </summary>
    public class TimerManager : Singleton<TimerManager>
    {

        private readonly List<Timer> _timers = new List<Timer>();

        public TimerManager()
        {
            MonoEvent.Instance.LATEUPDATE += LateUpdate;
        }

        private void LateUpdate()
        {
            for (var i = 0; i < _timers.Count; i++)
            {
                if (_timers[i].IsRunning)
                {
                    // unscaledDeltaTime和deltaTime一樣,但是不受TimeScale影響
                    _timers[i].Update(Time.unscaledDeltaTime);
                }
            }
        }

        public void AddTimer(Timer timer)
        {
            if (_timers.Contains(timer) == false)
            {
                _timers.Add(timer);
            }
        }

        public void RemoveTimer(Timer timer)
        {
            if (_timers.Contains(timer))
            {
                _timers.Remove(timer);
            }
        }
    }
}

2.使用方式

步驟一:構造

Timer timer = new Timer(1000,3);      //  計時器的構造以毫秒為單位,這是一個沒秒觸發一次,觸發三次結束的計時器

步驟二:新增事件

事件的新增和Button的onClick.AddListener()一樣,將函式當引數傳入,也可以使用Lambda表示式

timer.AddEventListener(() =>
{
    Debug.Log("new time");
});

對應的還有移除事件

timer.RemoveEventListener

步驟三:操作

timer.Start();      // 開始
timer.Pause();   //  暫停
timer.ReSet();   //   重置

暫停後繼續計時器也呼叫timer.Start();