Unity定时(延迟)管理器实现

前言

Unity中实现定时功能的方法有很多,比如协程、UpdateInvokeAsync等,可以说是五花八门,对于这类实现方法多、需求频繁的功能还是需要一个管理器来统一处理。

功能

下面列出了该管理器支持的功能,可以根据根据所列功能判断是否符合你的需求。
1.添加和移除并中断
2.自定义完成事件
3.延迟时间或帧数
4.指定执行次数
5.循环执行
6.是否受TimeScale影响

源码

代码中使用的MonoMgr是我实现的公共组件,方便外部代码调用MonoBehaviour,这部分可以根据自己的实际需求调整。其余部分开箱即用。
代码中有注释,没有太复杂的功能,就不过多解释了。

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

public class TimerMgr : Singleton<TimerMgr>
{
    public delegate void CompleteEvent();
    class TimerData
    {
        public int id;
        public CompleteEvent onCompleted; //完成回调事件
        public float time;   // 所需时间或帧数
        public float targetTime;   // 目标时间(如果是帧数则无效)
        public bool isIgnoreTimeScale;  // 是否忽略时间速率
        public bool isLoop;     //是否重复
        public int executeCount;   //循环次数
        public bool isSecond;   //是否以秒为单位 否则为帧数
        public Coroutine coroutine; //标记协程
    }

    int timerId = 0;
    Dictionary<int, TimerData> timerDict = new Dictionary<int, TimerData>();
    List<int> tempRemoveTimer = new List<int>();

    public TimerMgr()
    {
        //绑定Update
        MonoMgr.Instance.AddUpdateListener(UpdateTime);
    }

    void UpdateTime()
    {
        for(int i = timerDict.Count - 1; i >= 0 ; i--)
        {
            TimerData timeData = timerDict.ElementAt(i).Value;
            if (!timeData.isSecond)
            {
                continue;
            }

            float nowTime = TimeNow(timeData.isIgnoreTimeScale);
            if (nowTime >= timeData.targetTime)
            {
                timeData.onCompleted?.Invoke();
                timeData.executeCount -= 1;
                if (timeData.isLoop)
                {
                    timeData.targetTime = nowTime + timeData.time;
                }
                else
                {
                    if(timeData.executeCount <= 0)
                    {
                        tempRemoveTimer.Add(timeData.id);
                    }
                    else
                    {
                        timeData.targetTime = nowTime + timeData.time;
                    }
                }
            }
        }
        for(int i = 0; i < tempRemoveTimer.Count; i++)
        {
            RemoveTimer(tempRemoveTimer[i]);
        }
        tempRemoveTimer.Clear();
    }

    // 获取当前时间
    float TimeNow(bool isIgnoreTimeScale)
    {
        return isIgnoreTimeScale ? Time.realtimeSinceStartup : Time.time;
    }

    /// <summary>
    /// 创建一个新的定时器(支持循环)
    /// </summary>
    /// <param name="time">延迟时间(秒)</param>
    /// <param name="onCompleted">结束回调</param>
    /// <param name="isLoop">是否循环,false则只执行一次</param>
    /// <param name="isSecond">秒/帧数</param>
    /// <param name="isIgnoreTimeScale">是否受TimeScale影响</param>
    /// <returns></returns>
    public int CreateNewTimer(float time, CompleteEvent onCompleted, bool isLoop = false, bool isSecond = true,bool isIgnoreTimeScale = false)
    {
        timerId += 1;
        timerDict.Add(timerId, new TimerData()
        {
            id = timerId,
            onCompleted = onCompleted,
            time = time,
            targetTime = time + TimeNow(isIgnoreTimeScale),
            isIgnoreTimeScale = isIgnoreTimeScale,
            isLoop = isLoop,
            executeCount = 1,
            isSecond = isSecond
        });

        // 如果不是以秒为单位,则执行延迟帧数
        if (!isSecond)
        {
            timerDict[timerId].coroutine = MonoMgr.Instance.StartCoroutine(DelayedExecution(timerDict[timerId]));
        }
        return timerId;
    }

    /// <summary>
    /// 创建一个新的定时器(支持执行次数)
    /// </summary>
    /// <param name="time">延迟时间(秒)</param>
    /// <param name="onCompleted">结束回调</param>
    /// <param name="count">执行次数</param>
    /// <param name="isSecond">秒/帧数</param>
    /// <param name="isIgnoreTimeScale">是否受TimeScale影响</param>
    /// <returns></returns>
    public int CreateNewCountTimer(float time, CompleteEvent onCompleted, int count, bool isSecond = true, bool isIgnoreTimeScale = false)
    {
        timerId += 1;
        timerDict.Add(timerId, new TimerData()
        {
            id = timerId,
            onCompleted = onCompleted,
            time = time,
            targetTime = time + TimeNow(isIgnoreTimeScale),
            isIgnoreTimeScale = isIgnoreTimeScale,
            isLoop = false,
            executeCount = count,
            isSecond = isSecond
        });

        // 如果不是以秒为单位,则执行延迟帧数
        if (!isSecond)
        {
            timerDict[timerId].coroutine = MonoMgr.Instance.StartCoroutine(DelayedExecution(timerDict[timerId]));
        }
        return timerId;
    }

    /// <summary>
    /// 移除指定定时器
    /// </summary>
    public void RemoveTimer(int id)
    {
        if (timerDict.ContainsKey(id))
        {
            TimerData data = timerDict[id];
            if (!data.isSecond)
            {
                MonoMgr.Instance.StopCoroutine(data.coroutine);
            }
            data = null;
            timerDict.Remove(id);
        }
    }

    /// <summary>
    /// 清除所有定时器
    /// </summary>
    public void RemoveAllTimer()
    {
        for (int i = timerDict.Count - 1; i >= 0; i--)
        {
            int id = timerDict.ElementAt(i).Key;
            RemoveTimer(id);
        }
    }

    IEnumerator DelayedExecution(TimerData data)
    {
        // 等待指定的帧数
        for (int i = 0; i < data.time; i++)
        {
            yield return null; // 等待下一帧
        }
        // 执行动作
        data.onCompleted?.Invoke();
        data.executeCount -= 1;
        if (data.isLoop)
        {
            // 防止完成事件中移除了定时器,不判断会导致依然执行协程
            if(data != null && timerDict.ContainsKey(data.id))
            {
                data.coroutine = MonoMgr.Instance.StartCoroutine(DelayedExecution(data));
            }
        }
        else
        {
            if(data.executeCount <= 0)
            {
                RemoveTimer(data.id);
            }
            else
            {
                if (data != null && timerDict.ContainsKey(data.id))
                {
                    data.coroutine = MonoMgr.Instance.StartCoroutine(DelayedExecution(data));
                }
            }
        }
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

我寄人间雪满头丶

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值