Unity 的时间逻辑

冬天吃雪糕2022年9月1日
大约 4 分钟

Unity 的时间逻辑

与时间相关的属性

Unity 中有一些与时间相关的数值属性,它们都在Time库中:

Time.time表示从游戏开始运行以来到当前帧经过的时间

Time.fixedTime表示从游戏开始运行以来到最近一次执行FixedUpdate()经过的时间

Time.deltaTime表示从上一帧到当前帧经过的时间

Time.timeScale控制时间流逝的速度

Time.fixedDeltaTime表示固定帧率之间的时间间隔

Time.maximumDeltaTime表示两帧之间时间间隔的上限

Time.unscaledTime表示从游戏开始运行以来到当前帧经过的时间(不受Time.timeScaleTime.maximumDeltaTime影响)

Time.realtimeSinceStartup表示从游戏开始运行以来经过的实际时间(不受游戏暂停影响。在同一帧内多次调用返回结果可能不同,同样在不同帧内多次调用返回结果也可能相同)

Time.unscaledDeltaTime表示从上一帧到当前帧经过的时间(不受Time.timeScaleTime.maximumDeltaTime影响)

可变时间步长和固定时间步长

Unity 中存在两种基于“时间”进行事件处理的系统,一种基于“可变时间步长”,另一种基于“固定时间步长”。

“可变时间步长”系统的两次事件处理之间的时间间隔是不固定的,在该系统中,每在屏幕上绘制一帧执行一次事件处理。

“固定时间步长”系统每隔一个固定的时间间隔执行一次事件处理,通常它与物理模拟相关,当然如果需要也可以在该系统中执行你自己的代码。

可变时间步长

我们可以在Update()方法中定义要在可变时间步长系统中执行的代码,也就是说,这些代码会每帧执行一次。

注意由于不同设备(或同一设备不同情况)绘制一帧所需的时间是不固定的,通常需要在Update()方法中使用Time.deltaTime对代码产生的副作用(例如物体移动的距离)进行缩放,以保证不管游戏的帧率是多少,在相同的时间内可以产生一致的结果。

例如在下面的例子中,通过Update()方法读取用户的输入,并以每秒1米的速度移动对象。

using UnityEngine;
using System.Collections;

public class CubeController : MonoBehaviour
{
    public float speed = 1.0f;

    void Update()
    {
        // 从 Input 类中获取用户输入
        float xAxis = Input.GetAxis("Horizontal");
        float zAxis = Input.GetAxis("Vertical");
        Vector3 pos = transform.position;
        Vector3 step = new Vector3(xAxis, 0, zAxis).normalized * speed * Time.deltaTime;
        pos.x += step.x;
        pos.z += step.z;
        transform.position = pos;
    }
}

固定时间步长

在固定时间步长系统中执行的代码在FixedUpdate()中定义,“固定时间步长”系统会在每一帧开始时,尽可能多地调用FixedUpdate(),直到赶上当前时间(在下面的时间逻辑图中查看更多的细节)。

Unity 的物理系统也以固定时间步长运行,如果要执行物理相关的代码(例如为一个刚体施加力),也必须在FixedUpdate()中处理。

根据帧率的不同,FixedUpdate()可能在一帧内运行0次到多次。因为FixedUpdate()可能会在某一帧不执行,所以不能FixedUpdate()中读取输入,可能会因此错过用户的输入。

时间逻辑图

以下流程图说明了 Unity 用于计算单个帧中时间的逻辑,以及timedeltaTimefixedDeltaTimemaximumDeltaTime属性如何相互关联。

time-flowchart.png

  1. 将当前时间减去上一次记录的时间,得到deltaTime
  2. 判断deltaTime是否大于maximumDeltaTime,如果是,则将deltaTime限制为maximumDeltaTime
  3. deltaTime加到time中,更新time
  4. 判断timefixedTime的时间差是否不小于fixedDeltaTime,如果是,跳到5,否则跳过7
  5. fixedDeltaTime加到fixedTime中,更新fixedTime
  6. 调用FixedUpdate(),然后回到4
  7. 调用Update()
  8. 回到1,重复循环

以上顺序只是描述了 Unity 事件循环中与时间相关的部分,完整的事件循环请查阅事件函数的执行顺序 - Unity 手册open in new window

通过这张时间逻辑图,就可以更好地理解上面已经提到的“在每一帧开始时(Update()执行前),尽可能多地调用FixedUpdate()”的含义,以及在整个时间循环中,Time.timeTime.deltaTimeTime.fixedTime的变化机制

上次编辑于: 2022/9/16 08:09:25
贡献者: WingSnow