当Unity中Time.time变得很大时,会发生什么?


37

正如这里所说的:

时间

该帧开始的时间(只读)。这是自游戏开始以来的时间(以秒为单位)。

据我所知,时间存储在float中。因此,我的问题是,当时间价值变得非常大时会发生什么?它会溢出吗?它会失去精度并导致错误吗?游戏会崩溃还是会崩溃?


6
仅出于上下文考虑,请参见最长视频游戏马拉松吉尼斯世界纪录
Theraot

5
@AlexandreVaillancourt我认为如果我们将其解释为“ Time.time变得很大时有问题吗?”,这是一个有用的问题。而不是只专注于“溢出”。我已按照以下方式编辑了问题,以尝试解决您的反馈。
DMGregory

2
@DMGregory但是问题已经得到回答和接受...我同意您的编辑提出了一个更有用的问题,只是有点晚了。
MichaelHouse

我尝试用短语表达编辑内容,以使接受的答案仍然是编辑版本的正确答案。(毕竟,它已经在谈论精度问题),即使它回滚,我也不会冒犯。
DMGregory

Answers:


62

使用单精度浮点数确实会浪费时间精度

它仍然可以将精度保持到最近的97秒。但是在游戏中,我们经常关心帧持续时间的准确性。

9小时后,以秒为单位的单精度浮点时间值开始失去毫秒精度。

这已经不在单人游戏时段的范围之内,或者在您上班/上学或睡觉时让游戏运行“ AFK”。(这就是为什么测试游戏的一种常见方法是通宵运行游戏并检查其在早上仍能正常播放的原因之一)

在现代游戏机上,游戏通常在游戏时段之间暂停和恢复,而不是完全关闭,因此从游戏的角度来看,“单个时段”超过9小时的运行时间就不足为奇了,即使是短暂播放也是如此。

假设Unity的deltaTime计算是由高精度时间源计算的,这在Peter的另一个答案中得到了证实,那么依赖deltaTime帧尺度的定时是相对安全的(请谨慎通过求和来累积很长的时间deltaTime-将小增量添加到即使使用精明的算法进行补偿,较大的浮点数也是降低精度的经典方法。

由于fixedDeltaTime保持与您设置的相同值,而不是逐帧动态更改,因此还可以将定时敏感的行为放入FixedUpdate中,以获得更强的一致性保证。deltaTime将在这些方法中自动返回适当的固定增量。尽管固定和帧更新之间可能有拍子频率,但您可以通过插值来使其平滑

您要避免的是通过从另一个时间戳中减去一个时间戳来计算持续时间。玩了几个小时后,这可能会导致灾难性的取消,导致精度大大低于您在运行初期获得的精度。如果比较时间戳记对您的系统很重要,则可以使用其他方法(例如System.Diagnostics.Stopwatch而不是)来生成自己的高分辨率时间值Time.time


虚幻引擎还选择在代码蓝图中以单精度浮点数形式显示游戏时间。您可以实时处理时间戳并进行自己的时间扩展,就像在Unity中一样。(请注意,虚幻的时间戳类型基于32位纪元)。如果您愿意,虚幻中的C#扩展确实存在。
DMGregory

对于[ms],您开始在16 484-32 768频段附近失去精度。也就是说,您可能会在4h30min之后失去精确度,而在9h之后您肯定不能相信这一点。
xmedeko

从技术上讲,在4.5-9个小时之间,您的精度在0.98毫秒以内-我选择标记错误超过1.00毫秒的点。但是重点是正确的-精度一直都会下降,因此需要更高精度的代码可能甚至更早开始表现不佳。
DMGregory

16

float的最大值为3.40282347 * 10 ^ 38,以秒为单位等于10 ^ 31年(如果以毫秒为单位,则等于10 ^ 28年)。相信我,它不会溢出。

可能会出现的唯一错误是准确性。但是单个精度浮点数的精度为7-8个十进制数字。如果使用它来测量秒,则它的精确度约为194天。如果以毫秒为单位,则仅在4.5小时内是准确的。因此,它完全取决于所需的精度,如果需要精确到毫秒(可能不需要),则可能需要寻找其他方法。


7
沃尔夫勒姆·阿尔法(Wolfram Alpha)提供了一个定调性的想法,即多少年值得一提:比目前的宇宙时代高大约20个数量级。不是二十倍高,而是当前年龄再加上二十个零。
doppelgreener

1
@ Draco18s取决于单位测量deltaTime的方式,但是如果它们为每个帧取一个时间点并减去那两个,我想答案肯定是。
路加福音'17

7
这个答案根本不对。您可以进行实验。您可以在循环中一一递增浮点数。达到10 000 000后,它将停止递增。原因是在处理浮点数时无法正确地将小数加到大数上。@DMGregory给出了正确的答案
Seagull

2
@Seagull我没有写关于递增的地方。事实是,浮点数可表示的最大数字是(约)3,4 * 10 ^ 38,因此可以从严格的意义上排除溢出(由OP表示)。我不明白为什么答案会像看台一样不正确?
路加福音'17

2
@Seagull我认为LukeG的答案是正确的(甚至在编辑方面有所改进之前)-他的答案和我的答案只是针对不同类别的需求。我喜欢了解浮点精度和极限情况,而LukeG则更广泛地看待这个问题,而不太强调精确的精度需求。
DMGregory

12

您的游戏需要多少精度?

32位浮点型具有24位精度。换句话说,在时间t处,精度为±2 -23 ×t。(这并不完全正确,但是很接近,确切的细节并不是很有趣。)

  • 如果需要1ms的精度,则在1ms÷2 -23 = 8400 s = 2h 20m之后,将不再接受32位浮点数。大多数游戏都不需要1ms的精度,但是对于音乐应用来说,它被认为是必需的。

  • 如果您的游戏在144 Hz监视器上运行,并且您需要帧精确的图形,则在1÷144 Hz÷2 -23 = 58000 s = 16h之后,将不再接受32位浮点数。16h是长时间玩游戏,但这并非不可想象。我敢打赌,我们当中有很大一部分人一次连续运行了16个小时的游戏-即使只是因为我们让游戏保持运行并睡了一段时间。到那时,动画和物理更新将减少到144Hz以下。

如果Unity的Time.deltaTime时钟是从高精度时钟计算出来的,那么您可以使用该时钟,并且仍然可以获得全精度。我不知道这是不是真的。

边注

Windows上的游戏和其他程序经常用来GetTickCount()计算时间。由于它使用32位整数来计数毫秒,因此,如果您将计算机放置在这么长的时间内,它大约每50天就会回绕一次。我怀疑如果您在50天的时间里玩游戏,许多游戏将以异常的方式挂起,崩溃或行为异常。

像Windows一样的整数GetTickCount()有溢出的风险,而像Unity一样的浮点数则Time.time有失去精度的风险。


10

其他答案仅是推测,因此我编写了一个简单的Unity项目,并使其运行了一段时间。几个小时后,结果如下:

  • UnityEngine.Time.time相当快地失去准确性。不出所料,在运行游戏4个小时后,值跳了1毫秒,此后误差确实会增加。
  • UnityEngine.Time.deltaTime即使Unity运行了几个小时,它仍保持最初的毫秒级精度。因此,实际上可以保证它 deltaTime源自与平台相关的高分辨率计数器(通常在10-1000年后溢出)。仍然存在微小的风险,deltaTime在某些平台上仍然会失去精度。

时间的不准确性是所有依赖的问题UnityEngine.Time.time。一个具体的例子是旋转(例如风车),通常基于UnityEngine.Mathf.Sin(UnityEngine.Time.time*rotationSpeed)_Time着色器明确将其用于动画的问题更多。在开始变得不明显之前,不准确度需要达到多少?意识到这一问题并密切注意的人应该注意到20-30毫秒的准确性(2天)。在50-100毫秒(5-10天)内,不知道该问题的敏感人员将开始解决该问题。在200-300毫秒(20-30天)内,问题将很明显。

到目前为止,我还没有测试的准确性_SinTime_CosTime以及Unity_DeltaTime,所以我不能对这些评论。

这是我用于测试的脚本。它附加到UI.Text元素上,项目的“播放器设置”允许项目在后台运行,并且“质量设置”中的“垂直同步”设置为“每秒第二个V空白”:

public class Time : UnityEngine.MonoBehaviour {
    void Start () {
        LastTime = (double)UnityEngine.Time.time;
        StartTime =  System.DateTime.Now;
        LastPrintTime =  System.DateTime.Now;
    }
    double LastTime;
    System.DateTime StartTime;
    System.DateTime LastPrintTime;
    void Update () {
        double deltaTimeError = (double)UnityEngine.Time.deltaTime - ((double)UnityEngine.Time.time - LastTime);
        if (System.DateTime.Now - LastPrintTime  > System.TimeSpan.FromMilliseconds(1000))
        {
            GetComponent<UnityEngine.UI.Text>().text = "StartTime: " + StartTime.ToLongTimeString()
                + "\nCurrentTime: " + System.DateTime.Now.ToLongTimeString()
                + "\n\nTime.deltaTime: " + UnityEngine.Time.deltaTime.ToString("0.000000")
                + "\nTime.time - LastTime: " + ((float)(UnityEngine.Time.time - LastTime)).ToString("0.000000")
                + "\nAbsolute Error: " +  System.Math.Abs(deltaTimeError).ToString("0.000E0");
            LastPrintTime = System.DateTime.Now;
        }
        LastTime = UnityEngine.Time.time;       
    }
}

在此处输入图片说明在此处输入图片说明在此处输入图片说明 在此处输入图片说明

如果您想知道该0.033203值,我可以肯定的是0.033203125,它的二进制浮点表示形式为00111101000010000000000000000000。注意0尾数中的许多。

解决方法

大多数类型不需要变通办法。大多数玩家甚至根本不会玩100个小时的游戏,因此,投入金钱来解决问题只会在假设的连续连续几天玩游戏在经济上不可行之后才会引起人们的注意。不幸的是,我无法提出一个简单的通用解决方法来解决整个问题,而又不会带来一些重大缺陷。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.