当前位置: 首页 > news >正文

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(){//绑定UpdateMonoMgr.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));}}}}
}

相关文章:

  • 数据结构-线性表的顺序表示
  • Webstorm vue项目@路径不能跳转到对应资源,提示Cannot find declaration to go to
  • Android记录19-朋友圈动态发布时间计算
  • 事件传播机制 与 责任链模式
  • Matlab 入门学习
  • .net core使用EPPlus设置Excel的页眉和页脚
  • G7易流赋能化工物流,实现安全、环保与效率的共赢
  • Java延迟初始化Logger日志对象
  • 【C++11 之nullptr关键字 用以消除空指针和0歧义】基础知识必须了解
  • 【Python教程】压缩PDF文件大小
  • Vue3中的常见组件通信之`provide`、`inject`
  • webkit 的介绍
  • 大模型网信办备案全网最详细说明(付附件)
  • Docker部署Nginx1.21.5(保姆级图文教程)
  • Mybatis框架的缓存
  • @angular/forms 源码解析之双向绑定
  • [译] 理解数组在 PHP 内部的实现(给PHP开发者的PHP源码-第四部分)
  • C++回声服务器_9-epoll边缘触发模式版本服务器
  • export和import的用法总结
  • Go 语言编译器的 //go: 详解
  • IOS评论框不贴底(ios12新bug)
  • log4j2输出到kafka
  • markdown编辑器简评
  • nginx 负载服务器优化
  • RedisSerializer之JdkSerializationRedisSerializer分析
  • Redux系列x:源码分析
  • SegmentFault 2015 Top Rank
  • spring boot 整合mybatis 无法输出sql的问题
  • 初探 Vue 生命周期和钩子函数
  • 基于HAProxy的高性能缓存服务器nuster
  • 如何使用 JavaScript 解析 URL
  • 微信小程序设置上一页数据
  • ​LeetCode解法汇总2182. 构造限制重复的字符串
  • ### Error querying database. Cause: com.mysql.jdbc.exceptions.jdbc4.CommunicationsException
  • #数学建模# 线性规划问题的Matlab求解
  • #周末课堂# 【Linux + JVM + Mysql高级性能优化班】(火热报名中~~~)
  • (4)STL算法之比较
  • (a /b)*c的值
  • (delphi11最新学习资料) Object Pascal 学习笔记---第8章第5节(封闭类和Final方法)
  • (el-Date-Picker)操作(不使用 ts):Element-plus 中 DatePicker 组件的使用及输出想要日期格式需求的解决过程
  • (Java企业 / 公司项目)点赞业务系统设计-批量查询点赞状态(二)
  • (Qt) 默认QtWidget应用包含什么?
  • (Redis使用系列) Springboot 使用redis的List数据结构实现简单的排队功能场景 九
  • (ZT) 理解系统底层的概念是多么重要(by趋势科技邹飞)
  • (三)Pytorch快速搭建卷积神经网络模型实现手写数字识别(代码+详细注解)
  • (译) 函数式 JS #1:简介
  • (转)JVM内存分配 -Xms128m -Xmx512m -XX:PermSize=128m -XX:MaxPermSize=512m
  • (转)用.Net的File控件上传文件的解决方案
  • .NET Core中Emit的使用
  • .NET开源纪元:穿越封闭的迷雾,拥抱开放的星辰
  • ;号自动换行
  • @Bean有哪些属性
  • @德人合科技——天锐绿盾 | 图纸加密软件有哪些功能呢?
  • [ 数据结构 - C++]红黑树RBTree
  • [17]JAVAEE-HTTP协议