前言

在游戏世界中,时间无疑是最为重要的参数,它是游戏世界能否正常运转的关键。一旦它出错,轻则卡顿,重则游戏世界崩坏。在使用Unity引擎创造的世界中,Time类无疑是必须要掌握的一个类,它是控制时间的关键,是造物主最为重要的权柄,有了它,我们就可以肆意的玩弄我们所创造的游戏世界,一言万年。

很中二的前言hhh,但Time类真的非常重要,本篇主要讲解一下Time类的各个API,以及它们对Unity各项系统的影响。

目录

  • 前言
  • 一、Time类基础
    • 1.Time类总览
    • 2.何为Time.deltaTime
    • 3.何为timeScale | 时间加速与减速
    • 4.获取时间
    • 5.Time.captureDeltaTime
    • 6.关于timeAsDouble
  • 二、Time与生命周期函数的循环
    • 1.Time.time和Time.UnscaledTime的时间为何不相同?
    • 2.timeScale为0时会影响哪个周期函数?
    • 3.物理更新为什么要放到FixedUpdate中呢?
  • 三、总结和参考资料
    • 1.总结
    • 2.参考资料

Unity版本[2019.4.10f1] 梦小天幼 & 禁止转载

视频讲解:
【详解Unity】Time类与深入探究


一、Time类基础

1.Time类总览

大概浏览一遍即可,后续讲解到某个属性再翻回来看

变量 释义
总时间
Time.time 从游戏开始到现在所用的时间
Time.unscaledTime 从游戏开始到现在所用的时间(不受timeScale影响)
Time.fixedTime 从游戏开始到现在所用的时间
Time.fixedUnscaledTime 从游戏开始到现在所用的时间(不受timeScale影响)
Time.realtimeSinceStartup 游戏开始以来的实际时间
Time.timeSinceLevelLoad 自最后一个非添加场景(non-additive scene)完成加载以来的时间
增量时间
Time.deltaTime 帧间隔时间
Time.unscaledDeltaTime 帧间隔时间(不受timeScale影响)
Time.fixedDeltaTime 固定间隔帧时间
Time.fixedUnscaledDeltaTime 固定间隔帧时间(不受timeScale影响)
Time.smoothDeltaTime 经过平滑处理的Time.deltaTime
其他
Time.frameCount 总帧数
Time.timeScale 时间流逝尺度,1正常速度,2则2倍速
Time.inFixedTimeStep 在FixedUpdate()中调用返回true,否则返回false
Time.maximumDeltaTime 最大增量时间
Time.maximumParticleDeltaTime 粒子更新的最大增量时间
Time.captureDeltaTime 捕获增量时间
Time.captureFramerate Time.captureDeltaTime的倒数

2.何为Time.deltaTime

我们在学习Unity过程中,最先接触到的参数应该就是这个Time.deltaTime了,在Update中,我们的移动、旋转一般都要乘以一个Time.deltaTime,用以保证我们的速度是平均的、可控的。

因为Update是每帧调用一次,写在这个函数中的功能代码自然也是每帧调用一次,如果写死了,比如【transform.Translate(transform.forward)】,那么结果就是一秒执行了N次这句代码,移动速度就不可控了,这是就需要乘以一个Time.deltaTime。

Time.deltaTime简单来说,就是指 “前后两帧的间隔时间” ,So我们假设1秒钟内Update被调用了5次,那么帧间隔时间就是 1/5=0.2。

    //如图所示,假设Update每秒执行5次,那么这段代码则表示一秒移动了5米,很显然这不是我们想要的void Update(){transform.Translate(transform.forward); }//如图所示,假设Update每秒执行5次,而Time.deltaTime的值是0.2//那么每次Update移动的距离就是0.2,一共执行了五次,五次移动距离一起刚好1米void Update(){transform.Translate(transform.forward * Time.deltaTime); }

Update的执行次数是不固定的,用户电脑越强,它的1秒内执行次数越高,但是只要我们乘以了Time.deltaTime,就能保证它的移动速度是平均的。因为Time.deltaTime也是根据当前帧率计算出来的。

3.何为timeScale | 时间加速与减速

Time.timeScale属性用于控制游戏世界的时间流速,默认为1,正常状态,如果设置为2,则是两倍速,以此类推。必须大于等于0且小于100。

Time.timeScale之所以能控制两个立方体的旋转速度,是因为两个立方体的旋转代码中用到了Time.deltaTime,如果我们将旋转角度写死,那么Time.timeScale任它有天大能耐也无法控制的。

//正方体的自转代码
public class 自转 : MonoBehaviour
{public float speed;private void Update(){transform.Rotate(new Vector3(0, speed * Time.deltaTime, 0));}
}

这就意味着,timeScale并不是万能的,如果我们向让它去控制游戏世界中的一切,那么我们必须要按照Unity定下的规则办事,也就是在需要自己实现的运动功能代码部分乘以一个Time.deltaTime。

那么这里又引出一个问题,如果我不想被timeScale控制,但是我又要保证我的物体运动是平均可控的,怎么办?

那就引入一个新的属性 — Time.unscaledDeltaTime

该属性和Time.deltaTime没什么区别,但是当timeScale变更时,Time.unscaledDeltaTime是不受影响的。

4.获取时间

对于获取时间这块,Unity为我们提供了很多选择,我直接把上面的表格复制下来了,我们可以看到,对于获取时间足足有6个选择。

变量 释义
总时间
Time.time 从游戏开始到现在所用的时间
Time.unscaledTime 从游戏开始到现在所用的时间(不受timeScale影响)
Time.fixedTime 从游戏开始到现在所用的时间,固定更新
Time.fixedUnscaledTime 从游戏开始到现在所用的时间,固定更新(不受timeScale影响)
Time.realtimeSinceStartup 游戏开始以来的实际时间
Time.timeSinceLevelLoad 自最后一个非添加场景(non-additive scene)完成加载以来的时间

最重要的就是Time.time啦,该属性会返回从游戏开始到现在所用的时间,以秒为单位,如果大家想要计算玩家从本次已经运行游戏多长时间了,就可以使用该属性来计算。

但是请注意! Time.time是受到Time.timeScale的影响的,如果你的游戏用到了Time.timeScale,那么这时就需要将受缩放影响的时间和不受缩放影响的时间区分开了,由此,Unity为我们提供了Time.unscaledTime。

这样的话我们可以使用Time.time来计算游戏世界内的时间流逝,如果这个世界被时停了,那么时间也理应被时停,对吧?

对于现实世界而言,我们使用Time.unscaledTime,毕竟该属性不受Time.timeScale影响。

除此之外,还有下面四个属性

其中Time.fixedUnscaledTime、Time.realtimeSinceStartup是和Time.unscaledTime一样,不受Time.Scae影响
而Time.fixedTime、Time.timeSinceLevelLoad是受到Time.Scale影响的。

那么它们的不同之处在什么地方呢?

受Scale影响

  • Time.time 由Time.deltaTime累加更新
  • Time.fixedTime 由Time.fixedDeltaTime累加更新
  • Time.timeSinceLevelLoad 自最后一个非添加场景(non-additive scene)完成加载以来的时间
    上述三个是保持同步的

不受Scale影响

  • Time.unscaledTime 由Time.unscaledDeltaTime累加更新
  • Time.fixedUnscaledTime 由Time.fixedUnscaledDeltaTime累加更新
  • Time.realtimeSinceStartup 从系统计时器获取时间更新
    上述三个也是保持同步的
  • 一旦timeScale为0,则fixedUnscaledTime停止更新,但timeScale一旦恢复为1,则fixedUnscaledTime与unscaledTime同步。
  • 一旦通过编辑器暂停游戏,timeScale、fixedUnscaledTime都会停止更新,但realtimeSinceStartup会继续更新,直到编辑器停止暂停,则三个参数同步。

5.Time.captureDeltaTime

该参数主要用于减慢游戏的帧率,保证Unity在帧与帧之间完成截图,更多的使用情况是完成一系列的截图,组成视频,从而达成录像的目的。

默认该值为0,如果不启用该值就直接开始截图,可能会出现卡顿的情况,也就是最终合成的视频可能无法保持恒定的帧速率,所以想要录制流畅的视频,务必使用它!

下面这张动态图展示了录制一组图片的整个过程,但是表现不是很明显,可以结合下面给出的源码看,这就是一个案例应用

using UnityEngine;
using UnityEngine.UI;
using System.Collections;public class 截图 : MonoBehaviour
{//存放截图文件夹的名字public string folder = "Test截图";//截图需保持的帧率public int frameRate = 25;public Button startBtn;public Button stopBtn;public bool key = false;public void Update{if(key){Shot();}}//点击开始按钮public void StartSS(){// 设置播放帧率Time.captureDeltaTime = 1.0f / frameRate;//创建文件夹System.IO.Directory.CreateDirectory(folder);key = true;}//点击结束按钮public void StopSS(){key = false;//结束录制后,记得将该值置零Time.captureDeltaTime = 0;}//录制函数public void Shot(){// 添加文件名到文件夹名称(格式为“0005.png”)//Test截图/0001.pngstring name = string.Format("{0}/{1:D04}.png", folder, Time.frameCount);// 捕获截图 PNG文件保存至XX路径ScreenCapture.CaptureScreenshot(name);}
}

6.关于timeAsDouble

上述列表中还有六个参数的双精度版本的参数我没有写入,因为这是2021LTS版新增的内容,因为比较简单,所以不作赘述。
详情参阅:https://docs.unity3d.com/cn/current/Manual/WhatsNew2021LTS.html

Time.timeAsDouble引入了各种属性,使您可以在项目中使用双精度时间。这对于构建长时间运行的应用程序(如专用游戏服务器)的创作者特别有用。


二、Time与生命周期函数的循环

有关生命周期函数的调用顺序我之前出过一篇文章,详情请移步(https://blog.csdn.net/weixin_43147385/article/details/123928964?spm=1001.2014.3001.5502)
接下来主要是讲TimeScale对生命周期函数的影响。

下图为生命周期函数调用关系图,供参考

我们首先把循环流程图搬出来,供后续讲解参考(该图从Unity文档找的,我重新画了一下)

1.Time.time和Time.UnscaledTime的时间为何不相同?

在timeScale总为1的情况下,按理说,time和UnscaledTime应该是相同的,除非timeScale不为1,它们才会就此不同。但实际上从系统运行一开始,这俩参数就不一致了。

若想深究起原因,就不得不谈谈这两个参数都是依靠谁来进行更新的,如何进行更新的。

上面已经提过这个问题,time依靠deltaTime更新,UnscaledTime则依靠UnscaledDeltaTime更新。

它们对总时长累加一次,Update就更新一次,但是这个累加是有限制的,这个限制就是maximumDeltaTime,该参数默认为0.33。

但该参数的限制仅对deltaTime生效,仔细看上面给出的循环流程图,进行到第二步时就会判断deltaTime是否大于maximumDeltaTime,若大于,则直接返回maximumDeltaTime,若小于就进行下一步。

所以差异就在这个地方,当我在启动Unity时,它会卡顿两秒才成功启动,这两秒钟内是Unity内部在调用资源,但当我按下按钮一瞬间后,实际上time就已经开始计时了,只不过屏幕还没有刷新出来,而Unity在卡顿的时候,帧与帧之间的间隔时间会很大,导致deltaTime的值变得很大,但是maximumDeltaTime的存在就是为了防止这种事情发生,所以time被限制住了,而UnscaledTime则没有被限制住,所以实际上UnscaledTime才是真正的Unity从运行开始到现在的时间。

2.timeScale为0时会影响哪个周期函数?

这里我们做一个小案例即可明了

从上图中我们可以很清晰的看出,当timeScale为0时,FixedUpdate停止了刷新,而Update和LateUpdate则没有停。其原因我们可以在循环流程图中得到解答。

每当deltaTime被添加到Time.time中时就会检查一次,fixedTime和time是否差距过大,如果不大就持续更新Update,如果差距大于fixedDeltaTime则累加一次fixedTime,然后更新一次FixedUpdate。

So,当timeScale为0时,deltaTime受此影响变为0,循环依旧运行,但time也一直在累加,但是一直都是加0,所以time与fixedTime的差距始终小于fixedDeltaTime,所以始终更新Update,而无法满足更新FixedUpdate的条件。

3.物理更新为什么要放到FixedUpdate中呢?

我们经常听别人说,物理相关的一定要放到FixedUpdate中,但是为什么这么写呢?

通过上一段我们已经了解,timeScale会影响FixedUpdate,其实答案已经很明了了。

我们假设有一架不停发射的机枪,架在主角必经之路,这时如果主角打开了设置界面,世界是不是需要暂停?需要等待主角关掉设置界面。那么这个时候机枪怎么办?

我们假设机枪代码如下:

public class 机枪 : MonoBehaviour
{public GameObject bullet;public float force_ = 40;private void Update(){GameObject g;g = GameObject.Instantiate(bullet);g.AddComponent<Rigidbody>();g.GetComponent<Rigidbody>().AddForce(transform.forward * force_);}
}

这里使用了Update,那么时停过程中,Update是不会暂停的,So,机枪会不断生成子弹,但子弹并不会发射,因为代码给子弹添加了力,这里是由物理系统控制的。所以这里我们换成FixedUpdate,一旦时停,代码也会停止运行。



三、总结和参考资料

1.总结

最后总结,来一张全参数的变化图,还挺好玩的,你们可以自己去做一下.

挑重要的说
关于time的总结

  • time和fixedTime和timeSinceLevelLoad总是同步
  • unscaledTime和fixedUnscaledTime和realtimeSinceStartupas总是同步

关于deltaTime的总结

  • deltaTime受到timeScale影响
  • deltaTime最大值受到maximumDeltaTime影响
  • unscaledDeltaTime不受timeScale影响
  • fixedDeltaTIme不受timeScale影响
  • fixedUnscaledDeltaTime受到timeScale影响,会按照反比改变
  • smoothDeltaTime总是会在deltaTime和unscaledDeltaTime计算出一个平衡值

关于对周期函数影响的总结

  • FixedUpdate会受到timeScale影响,为0时会停止运行,为2时为2倍速运行
  • Update不会受到timeScale影响

2.参考资料

[1].Unity官方.Unity官方文档-Time类
[2].Unity官方.Unity官方文档-Time类API
[3].SerenaHaven.Unity——Time.timeScale详解
[4].Unity官方.New in Unity 2021 LTS
[5].天生爱赞美.Unity关于Time.timeScale详解

详解Unity中Time类的用法与深入探究相关推荐

  1. python中break怎么用_详解Python中break语句的用法

    详解Python中break语句的用法 在Python中的break语句终止当前循环,继续执行下一个语句,就像C语言中的break一样. break最常见的用途是当一些外部条件被触发,需要从一个循环中 ...

  2. 【视频】详解Scala中的类及与Java的详细区别

    详解Scala中的类及与Java的详细区别

  3. python中index函数_详解python中的index函数用法

    1.函数的创建 def fun(): #定义 print('hellow') #函数的执行代码 retrun 1 #返回值 fun() #执行函数 2.函数的参数 普通参数 :要按照顺序输入参数 de ...

  4. python3 yield 大文件_详解Python3中yield生成器的用法

    任何使用yield的函数都称之为生成器,如: def count(n): while n > 0: yield n #生成值:n n -= 1 另外一种说法:生成器就是一个返回迭代器的函数,与普 ...

  5. 详解 javascript中offsetleft属性的用法(转)

    详解 javascript中offsetleft属性的用法 转载  2015-11-11   投稿:mrr    我要评论 本章节通过代码实例介绍一下offsetleft属性的用法,需要的朋友可以做一 ...

  6. 详解Vue中watch的高级用法

    转载自  详解Vue中watch的高级用法 我们通过实例代码给大家分享了Vue中watch的高级用法,对此知识点有需要的朋友可以跟着学习下. 假设有如下代码: 1 2 3 4 5 6 7 8 9 10 ...

  7. python中heapq的库是什么_详解Python中heapq模块的用法

    详解Python中heapq模块的用法 来源:中文源码网    浏览: 次    日期:2018年9月2日 [下载文档:  详解Python中heapq模块的用法.txt ] (友情提示:右键点上行t ...

  8. 详解Unity中的粒子系统Particle System (七)

    前言 本篇来讲一讲Collision和Triggers模块,这两个模块主要用于粒子系统与物理世界的交互,一个是碰撞器,另一个是触发器.有了这两个模块我们又可以做出更炫酷的粒子效果啦! 目录 前言 本系 ...

  9. python中get函数是什么意思_详解python中get函数的用法(附代码)_后端开发

    strncmp函数用法详解_后端开发 strncmp函数为字符串比较函数,其函数语法为"int strncmp ( const char * str1, const char * str2, ...

最新文章

  1. FillMode详解
  2. 启动jar包并生成日志的linux脚本
  3. thttpd源码分析
  4. windows配置solr5.5.2(不通过tomcat,使用内置jetty)
  5. spring中怎么让事物提交_Spring怎么在一个事务中开启另一个事务
  6. Requests redirect
  7. (笔记)Mysql命令show databases:显示所有数据库
  8. 二叉树的遍历实验报告C语言,数据结构-二叉树的遍历(类C语言描写叙述)
  9. 信息系统项目管理--上午分析笔记
  10. 惠普m128fn中文说明书_hp m125m126m127m128使用说明.pdf
  11. 麦本本笔记本怎么U盘重装Win10系统教学?
  12. 【软件工程】——软工视频总结
  13. 我的盖洛普“优势识别器”测试报告
  14. 裁员潮,互联网寒冬的黑暗时刻
  15. Vasp 石墨烯能带计算
  16. lpad与rpad函数
  17. 超融合服务器系统,超融合服务器|超融合架构|超融合一体机|业务系统一体机
  18. vue 如何获取图片的原图尺寸_js获取图片信息(一)-----获取图片的原始尺寸
  19. 程序员的可迁移技能和经验
  20. vscode使用remote--ssh进行远程连接报错Resolver error: Error: Connecting with SSH timed out

热门文章

  1. Python常用内建模块(内含实例)
  2. 106短信和10690短信有什么区别?
  3. SpringMvc拦截器解决返回数据乱码问题
  4. CV 领域的最美情话
  5. 项目九 配置磁盘配额与管理RAID卷
  6. Flex(Gumbo)中Skin解析
  7. 网络安全等级保护主要标准简要说明
  8. 帝国CMS网站管理系统安装方法图文教程
  9. MySQL设置密码的三种方法
  10. linux 三剑客命令(grep,sed ,awk)