Unity 封装一个依赖于MonoBehaviour的计时器(上) 基本功能

news2025/3/13 17:23:45

        灵感来自下面这本书的协程部分,因此我就自己尝试写了一个

       我的新书Unity3D游戏开发(第3版) | 雨松MOMO程序研究院

   如果你不知道什么是协程:unity保姆级教程之协同程序_unity协同-CSDN博客 

    一句话概括:协程就是单线程的异步操作,其作用于Unity的主线程

1.我写了如下几个功能(只展示无参数):

基础校验

    private bool CheckCount(int count)
    {
        if (count < 0)
        {
            Debug.LogError("循环次数不能为负数!");
            return false;
        }
        return true;
    }

    private bool CheckTime(float time)
    {
        if (time < 0)
        {
            Debug.LogError("等待时间不能为负数!");
            return false;
        }
        return true;
    }

1.等待受缩放时间影响后的秒数

  public void WaitTime(float waitTime, Action callback)
  {
      if (CheckTime(waitTime))
      {
          StartCoroutine(WaitTimeHandle(waitTime, () => callback?.Invoke()));
      }
  }

2.等待不受缩放时间影响后的秒数

    public void WaitRealTime(float waitTime, Action callback)
    {
        if (CheckTime(waitTime))
        {
            StartCoroutine(WaitRealTimeHandle(waitTime, () => callback?.Invoke()));
        }
    }

2.按固定时间间隔循环执行

 public void LoopTime(float spacing, int overNumber, Action callback)
 {
     if (CheckTime(spacing) && CheckCount(overNumber))
     {
         StartCoroutine(LoopTimeHandle(spacing, overNumber, () => callback?.Invoke()));
     }
 }

3.等待固定帧执行一次

    public void WaitFrame(int frameCount, Action callback)
    {
        if (CheckCount(frameCount))
        {
            StartCoroutine(WaitFrameHandle(frameCount, () => callback?.Invoke()));
        }
    }

4.进度反馈

    public void WaitTimeWithProgress(float waitTime, Action<float> progressCallback, Action completeCallback)
    {
        if (CheckTime(waitTime))
        {
            StartCoroutine(ProgressTimer(waitTime, progressCallback, completeCallback));
        }
    }

    private IEnumerator ProgressTimer(float duration, Action<float> progress, Action complete)
    {
        float startTime = Time.time;
        while (Time.time - startTime < duration)
        {
            progress?.Invoke((Time.time - startTime) / duration);
            yield return null;
        }
        complete?.Invoke();
    }

5.等待当前帧渲染结束执行回调(本帧执行)

    public void WaitForEndOfFrame(Action callback)
    {
        StartCoroutine(WaitForEndOfFrameHandle(callback));
    }

    private IEnumerator WaitForEndOfFrameHandle(Action callback)
    {
        yield return new WaitForEndOfFrame();
        callback?.Invoke();
    }

2.总览

      回调函数我写了无参 一参 两参的版本 可以自行添加参数

using System;
using System.Collections;
using UnityEngine;

public class TimeManager : MonoBehaviour
{
    private static TimeManager instance;
    public static TimeManager Instance => instance;

    private void Awake()
    {
        if (instance == null)
        {
            instance = this;
        }
    }

    private bool CheckCount(int count)
    {
        if (count < 0)
        {
            Debug.LogError("循环次数不能为负数!");
            return false;
        }
        return true;
    }

    private bool CheckTime(float time)
    {
        if (time < 0)
        {
            Debug.LogError("等待时间不能为负数!");
            return false;
        }
        return true;
    }

    #region 等待固定时间秒
    /// <summary>
    /// 等待固定时间以后执行回调
    /// </summary>
    /// <param name="waitTime">等待时间(秒)</param>
    /// <param name="callback">回调函数</param>
    public void WaitTime(float waitTime, Action callback)
    {
        if (CheckTime(waitTime))
        {
            StartCoroutine(WaitTimeHandle(waitTime, () => callback?.Invoke()));
        }
    }

    public void WaitTime<T>(float waitTime, T param, Action<T> callback)
    {
        if (CheckTime(waitTime))
        {
            StartCoroutine(WaitTimeHandle(waitTime, () => callback?.Invoke(param)));
        }
    }

    public void WaitTime<T, K>(float waitTime, T param1, K param2, Action<T, K> callback)
    {
        if (CheckTime(waitTime))
        {
            StartCoroutine(WaitTimeHandle(waitTime, () => callback?.Invoke(param1, param2)));
        }
    }

    private IEnumerator WaitTimeHandle(float waitTime, Action action)
    {
        yield return new WaitForSeconds(waitTime);
        action?.Invoke();
    }
    #endregion
    #region 等待固定时间秒(不受缩放影响)
    /// <summary>
    /// 等待固定时间以后执行回调(不受Time.timeScale影响)
    /// </summary>
    /// <param name="waitTime">等待时间(秒)</param>
    /// <param name="callback">回调函数</param>
    public void WaitRealTime(float waitTime, Action callback)
    {
        if (CheckTime(waitTime))
        {
            StartCoroutine(WaitRealTimeHandle(waitTime, () => callback?.Invoke()));
        }
    }

    public void WaitRealTime<T>(float waitTime, T param, Action<T> callback)
    {
        if (CheckTime(waitTime))
        {
            StartCoroutine(WaitRealTimeHandle(waitTime, () => callback?.Invoke(param)));
        }
    }

    public void WaitRealTime<T, K>(float waitTime, T param1, K param2, Action<T, K> callback)
    {
        if (CheckTime(waitTime))
        {
            StartCoroutine(WaitRealTimeHandle(waitTime, () => callback?.Invoke(param1, param2)));
        }
    }

    private IEnumerator WaitRealTimeHandle(float waitTime, Action action)
    {
        yield return new WaitForSecondsRealtime(waitTime);
        action?.Invoke();
    }
    #endregion

    #region 按固定时间间隔循环执行
    public void LoopTime(float spacing, int overNumber, Action callback)
    {
        if (CheckTime(spacing) && CheckCount(overNumber))
        {
            StartCoroutine(LoopTimeHandle(spacing, overNumber, () => callback?.Invoke()));
        }
    }

    public void LoopTime<T>(float spacing, int overNumber, T param, Action<T> callback)
    {
        if (CheckTime(spacing) && CheckCount(overNumber))
        {
            StartCoroutine(LoopTimeHandle(spacing, overNumber, () => callback?.Invoke(param)));
        }
    }

    public void LoopTime<T, K>(float spacing, int overNumber, T param1, K param2, Action<T, K> callback)
    {
        if (CheckTime(spacing) && CheckCount(overNumber))
        {
            StartCoroutine(LoopTimeHandle(spacing, overNumber, () => callback?.Invoke(param1, param2)));
        }
    }

    private IEnumerator LoopTimeHandle(float spacing, int overNumber, Action action)
    {
        for (int i = 0; i < overNumber; i++)
        {
            yield return new WaitForSeconds(spacing);
            action?.Invoke();
        }
    }
    #endregion

    #region 等待固定帧执行一次
    public void WaitFrame(int frameCount, Action callback)
    {
        if (CheckCount(frameCount))
        {
            StartCoroutine(WaitFrameHandle(frameCount, () => callback?.Invoke()));
        }
    }

    public void WaitFrame<T>(int frameCount, T param, Action<T> callback)
    {
        if (CheckCount(frameCount))
        {
            StartCoroutine(WaitFrameHandle(frameCount, () => callback?.Invoke(param)));
        }
    }

    public void WaitFrame<T, K>(int frameCount, T param1, K param2, Action<T, K> callback)
    {
        if (CheckCount(frameCount))
        {
            StartCoroutine(WaitFrameHandle(frameCount, () => callback?.Invoke(param1, param2)));
        }
    }

    private IEnumerator WaitFrameHandle(int frameCount, Action action)
    {
        for (int i = 0; i < frameCount; i++)
        {
            yield return null;
        }
        action?.Invoke();
    }
    #endregion

    #region 进度反馈
    public void WaitTimeWithProgress(float waitTime, Action<float> progressCallback, Action completeCallback)
    {
        if (CheckTime(waitTime))
        {
            StartCoroutine(ProgressTimer(waitTime, progressCallback, completeCallback));
        }
    }

    private IEnumerator ProgressTimer(float duration, Action<float> progress, Action complete)
    {
        float startTime = Time.time;
        while (Time.time - startTime < duration)
        {
            progress?.Invoke((Time.time - startTime) / duration);
            yield return null;
        }
        complete?.Invoke();
    }

    public void WaitTimeWithProgress<T>(float waitTime, T param, Action<float, T> progressCallback, Action<T> completeCallback)
    {
        if (CheckTime(waitTime))
        {
            StartCoroutine(ProgressTimer(waitTime, param, progressCallback, completeCallback));
        }
    }

    private IEnumerator ProgressTimer<T>(float duration, T param, Action<float, T> progress, Action<T> complete)
    {
        float startTime = Time.time;
        while (Time.time - startTime < duration)
        {
            progress?.Invoke((Time.time - startTime) / duration, param);
            yield return null;
        }
        complete?.Invoke(param);
    }

    public void WaitTimeWithProgress<T, K>(float waitTime, T param1, K param2, Action<float, T, K> progressCallback, Action<T, K> completeCallback)
    {
        if (CheckTime(waitTime))
        {
            StartCoroutine(ProgressTimer(waitTime, param1, param2, progressCallback, completeCallback));
        }
    }

    private IEnumerator ProgressTimer<T, K>(float duration, T param1, K param2, Action<float, T, K> progress, Action<T, K> complete)
    {
        float startTime = Time.time;
        while (Time.time - startTime < duration)
        {
            progress?.Invoke((Time.time - startTime) / duration, param1, param2);
            yield return null;
        }
        complete?.Invoke(param1, param2);
    }
    #endregion

    #region 等待当前帧结束执行回调
    public void WaitForEndOfFrame(Action callback)
    {
        StartCoroutine(WaitForEndOfFrameHandle(callback));
    }

    private IEnumerator WaitForEndOfFrameHandle(Action callback)
    {
        yield return new WaitForEndOfFrame();
        callback?.Invoke();
    }

    public void WaitForEndOfFrame<T>(T param, Action<T> callback)
    {
        StartCoroutine(WaitForEndOfFrameHandle(param, callback));
    }

    private IEnumerator WaitForEndOfFrameHandle<T>(T param, Action<T> callback)
    {
        yield return new WaitForEndOfFrame();
        callback?.Invoke(param);
    }

    public void WaitForEndOfFrame<T, K>(T param1, K param2, Action<T, K> callback)
    {
        StartCoroutine(WaitForEndOfFrameHandle(param1, param2, callback));
    }

    private IEnumerator WaitForEndOfFrameHandle<T, K>(T param1, K param2, Action<T, K> callback)
    {
        yield return new WaitForEndOfFrame();
        callback?.Invoke(param1, param2);
    }
    #endregion
    public void Stop(IEnumerator func)
    {
        StopCoroutine(func);
    }

    public void StopAll()
    {
        StopAllCoroutines();
    }

    public TimeChainContext StartChain()
    {
        return new TimeChainContext(this);
    }
}

3.Ai给出了如下拓展思路       

        优先级高

        链式调用扩展

        物理时间步长同步

         优先级中

        调试可视化工具

        网络同步时钟

        优先级低

      自动化测试框架集成

      使用Mono管理类去管理该脚本从而摆脱Mono的控制

        我目前的需求没有这么复杂,当作一个简单的计时器使用

        其是依赖于Unity的这一点可以用await和async来替代,但是可能涉及到了多线程,我这块学了但是用的不多还需加强

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2314413.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

使用Nodejs基于DeepSeek加chromadb实现RAG检索增强生成 本地知识库

定义 检索增强生成&#xff08;RAG&#xff09;的基本定义 检索增强生成&#xff08;Retrieval-Augmented Generation&#xff0c;简称RAG&#xff09;是一种结合了信息检索技术与语言生成模型的人工智能技术。RAG通过从外部知识库中检索相关信息&#xff0c;并将其作为提示&…

笔试刷题专题(一)

文章目录 最小花费爬楼梯&#xff08;动态规划&#xff09;题解代码 数组中两个字符串的最小距离&#xff08;贪心&#xff08;dp&#xff09;&#xff09;题解代码 点击消除题解代码 最小花费爬楼梯&#xff08;动态规划&#xff09; 题目链接 题解 1. 状态表示&#xff1…

LeetCode977有序数组的平方

思路①&#xff1a;先平方&#xff0c;后快排&#xff0c;输出&#xff08;基准元素&#xff0c;左小右大&#xff09; 时间复杂度&#xff1a;O&#xff08;nlogn&#xff09; 思路②&#xff1a;双指针左右开弓&#xff0c;首先原数组已经是按照非递减顺序排序&#xff0c;那…

网络变压器的主要电性参数与测试方法(4)

Hqst盈盛&#xff08;华强盛&#xff09;电子导读&#xff1a;网络变压器的主要电性参数与测试方法&#xff08;4&#xff09;.. 今天我们继续来看看网络变压器的2个重要电性参数与它的测试方法&#xff1a; 1.反射损耗&#xff08;Return loss&…

Windows10 WSL又又又一次崩了 Docker Desktop - Unexpected WSL error

问题&#xff1a;Windows10 WSL又又又一次崩了 这回报错&#xff1a; 然后再打开WSL Ubuntu就卡住了&#xff0c;等很长时间没反应&#xff0c;就关掉了。 手动启动Docker Desktop&#xff0c;报错&#xff1a; An unexpected error occurred while executing a WSL comman…

【前端基础】:HTML

超链接标签: a href: 必须具备, 表示点击后会跳转到哪个页面. target: 打开方式. 默认是 _self. 如果是 _blank 则用新的标签页打开 <a href"http://www.baidu.com">百度</a>链接的几种形式: 外部链接: href 引用其他网站的地址 <a href"http…

JVM垃圾收集器合集

前言&#xff1a;JVM GC收集器的回顾与比较 JVM&#xff08;Java虚拟机&#xff09;中的垃圾收集器是自动管理内存的重要机制&#xff0c;旨在回收不再使用的对象所占用的内存空间。以下是JVM中几种常见的垃圾收集器的详细介绍&#xff1a; 一、新生代垃圾收集器 1.Serial收集…

Sourcetree——使用.gitignore忽略文件或者文件夹

一、为何需要文件忽略机制&#xff1f; 1.1 为什么要会略&#xff1f; 对于开发者而言&#xff0c;明智地选择忽略某些文件类型&#xff0c;能带来三大核心优势&#xff1a; 仓库纯净性&#xff1a;避免二进制文件、编译产物等污染代码库 安全防护&#xff1a;防止敏感信息&…

本地部署 OpenManus 保姆级教程(Windows 版)

一、环境搭建 我的电脑是Windows 10版本&#xff0c;其他的没尝试&#xff0c;如果大家系统和我的不一致&#xff0c;请自行判断&#xff0c;基本上没什么大的出入啊。 openManus的Git地址&#xff1a;https://github.com/mannaandpoem/OpenManus 根据官网的两种安装推荐方式如…

视频推拉流:EasyDSS平台直播通道重连转推失败原因排查与解决

视频推拉流EasyDSS视频直播点播平台&#xff0c;集视频直播、点播、转码、管理、录像、检索、时移回看等功能于一体&#xff0c;可提供音视频采集、视频推拉流、播放H.265编码视频、存储、分发等视频能力服务。 用户使用EasyDSS平台对直播通道进行转推&#xff0c;发现只要关闭…

【科研绘图系列】python绘制分组点图(grouped dot plot)

禁止商业或二改转载,仅供自学使用,侵权必究,如需截取部分内容请后台联系作者! 文章目录 介绍加载R包数据下载导入数据函数`generateRectBoxDF` 函数主要作用参数解释逻辑流程`nmfDotPlot` 函数主要作用参数解释逻辑流程画图1画图2画图3画图4介绍 【科研绘图系列】python绘制…

Springfox、Springdoc和Swagger

Springfox、Swagger 和 Springdoc Springfox、Swagger 和 Springdoc 是用于在 Spring Boot 项目中生成API文档的工具&#xff0c;但它们之间有显著的区别和演进关系&#xff1a; 1.Swagger 简介 Swagger 是一个开源项目&#xff0c;旨在为 RESTful APIs 提供交互式文档。最…

在Spring Boot项目中如何实现获取FTP远端目录结构

Java语言实现获取FTP远端目录结构的实现方式有多种,在Spring Boot 项目中,最简单和快速的方式就是使用Spring Integration 实现FTP相关的功能。 前言 本篇的示例和演示基于Windows 的FTP 服务,关于如何在Windows 开启FTP服务可以参考: Windows 如何开启和使用FTP服务 本…

Flutter_学习记录_device_info_plus 插件获取设备信息

引入三方库device_info_plus导入头文件 import package:device_info_plus/device_info_plus.dart;获取设备信息的主要代码 DeviceInfoPlugin deviceInfoPlugin DeviceInfoPlugin(); BaseDeviceInfo deviceInfo await deviceInfoPlugin.deviceInfo;完整案例 import package…

Java高频面试之集合-10

hello啊&#xff0c;各位观众姥爷们&#xff01;&#xff01;&#xff01;本baby今天来报道了&#xff01;哈哈哈哈哈嗝&#x1f436; 面试官&#xff1a;详解红黑树&#xff1f;HashMap为什么不用二叉树/平衡树呢&#xff1f; 一、红黑树&#xff08;Red-Black Tree&#xff…

never_give_up

一个很有意思的题&#xff1a; never_give_up - Bugku CTF平台 注意到注释里面有1p.html&#xff0c;我们直接在源代码界面看&#xff0c;这样就不会跳转到它那个链接的&#xff1a; 然后解码可得&#xff1a; ";if(!$_GET[id]) {header(Location: hello.php?id1);exi…

Python Selenium库入门使用,图文详细。附网页爬虫、web自动化操作等实战操作。

文章目录 前言1 创建conda环境安装Selenium库2 浏览器驱动下载&#xff08;以Chrome和Edge为例&#xff09;3 基础使用&#xff08;以Chrome为例演示&#xff09;3.1 与浏览器相关的操作3.1.1 打开/关闭浏览器3.1.2 访问指定域名的网页3.1.3 控制浏览器的窗口大小3.1.4 前进/后…

AI4CODE】3 Trae 锤一个贪吃蛇的小游戏

【AI4CODE】目录 【AI4CODE】1 Trae CN 锥安装配置与迁移 【AI4CODE】2 Trae 锤一个 To-Do-List 这次还是采用 HTML/CSS/JAVASCRIPT 技术栈 Trae 锤一个贪吃蛇的小游戏。 1 环境准备 创建一个 Snake 的子文件夹&#xff0c;清除以前的会话记录。 2 开始构建 2.1 输入会…

Linux 进程的一生(一):进程与线程的创建机制解析

在 Linux 操作系统中&#xff0c;每个任务都以「进程」的形式存在。但 Linux 下的「线程」又是什么&#xff1f;Linux 并没有单独定义一种全新数据结构来表示线程&#xff0c;而是将线程视为一种特殊的进程——一种共享资源的轻量级进程。然而&#xff0c;在具体实现和运行机制…

STM32之I2C硬件外设

注意&#xff1a;硬件I2C的引脚是固定的 SDA和SCL都是复用到外部引脚。 SDA发送时数据寄存器的数据在数据移位寄存器空闲的状态下进入数据移位寄存器&#xff0c;此时会置状态寄存器的TXE为1&#xff0c;表示发送寄存器为空&#xff0c;然后往数据控制寄存器中一位一位的移送数…