Update、FixedUpdate、LateUpdate函数,定时器和帧速率

请尊重原作者的工作,转载时请务必注明转载自:www.xionggf.com

1 Update、LateUpdate、FixedUpdate函数

Unity将每帧调用一次 Update() 函数。这意味着,如果你以60fps的速度运行,那意味着每秒调用60次 Update() 函数。如果以50fps的速度运行,则每秒调用50次Update函数。这也就是说。每两次执行update函数之间的时间间隔,其实是变化不定的。正是由于这一点,使用 Update() 函数的最大问题是它可能与物理引擎不同步。调用行为可以改变,因为它可以结束更快或更慢,这取决于您的图形在渲染的硬件上放置了多少负载,使用物理更新可能会导致完全错误的反应。

Unity确实会尝试以项目所指定的速率调用**FixedUpdate()函数。但是,这些调用也需要根据当前实际的帧速率去确定。这意味着虽然任何给定帧上可能有零次,一次甚至超过100次对FixedUpdate()函数的调用,但Unity会动态调整到实际帧速率。通过深入挖掘,我们逐渐意识到,这也意味着对FixedUpdate()**的所有调用都不一定在指定的时间间隔内。下面举例说明下这种情况:

默认情况下 FixedUpdate() 函数的更新频率是50FPS(0.02s),如果当游戏的更新频率为60FPS时,那么FixedUpdate有固定的更新速率是很好理解的,因为这种绝对可以保证到,每执行一次 FixedUpdate() 函数,就至少执行了一帧,但是如果游戏本身的更新频率就很低,例如为30FPS时,每一帧的时间间隔明显大于0.02秒的,那么这时候在每两次 Update() 函数的调用之间。 FixedUpdate() 函数就被调用了多次。

  1. 通常 FixedUpdate() 函数比 Update() 函数更频繁地被调用。

  2. FixedUpdate() 函数可以在一帧内被调用多次,如果帧速率很低,则根本不能在帧之间调用。

  3. 如果帧速率很高,也可能不会调用 FixedUpdate()

  4. FixedUpdate() 函数被调用之后立即进行所有物理计算和更新。

  5. 作用力,扭矩或其他与物理相关的函数时,应使用FixedUpdate函数。

  6. FixedUpdate() 函数由一个准确可信的计时器调用,而与实际的帧速率无关,因此在 FixedUpdate() 中应用移动计算时,您不需要将您的值乘以Time.deltaTime。

  7. Update() 函数是每一帧中执行更新操作的主要(入口)函数。

  8. Update() 函数中用来移动非物理系统驱动的物体。

  9. Update() 函数用来接受用户输入。

  10. Update() 函数可用来实现简单的计时器。

由于 Update() 的执行方式,与物理引擎的执行步调不同,即可能在一帧中, FixedUpdate() 函数执行了多次,而Update函数则只执行了一次。这取决于渲染器在渲染图形时的负载量,以及所耗费的CPU时间。如果在 Update() 函数中执行物理运算时,会导致未知的结果。同时也可以看到: 函数的执行间隔,未必就是TimeManager界面中Fixed TimeStep项中所指定的值。

  1. LateUpdate() 函数每帧会被调用一次,在 Update() 函数结束后调用
  2. 所有在 Update() 函数中执行的计算,将会在 LateUpdate() 函数调用之前完成执行。
  3. 如果实现一个跟随式的第三视角摄像机,则摄像机的状态更新放在 LateUpdate() 函数中执行是一个通用的做法。

因为你的角色是在 Update() 函数里面执行移动和转向的操作。所以在 LateUpdate() 函数执行摄像机的位置和旋转度计算,可以确保在计算时,角色已经全部完成了位置计算,摄像机可以正确地跟踪到角色的正确位置。

2 UnityEngine.Time类

2.1 Time.deltaTime

Time.deltaTime 是类Time的一个公有静态的浮点类型成员变量

1public static float deltaTime;

这个变量是只读的, 返回本帧到上一帧流逝的时间,单位是秒Time.deltaTime 的使用例子如下: 如果要沿着Y轴移动某个game object,那么假设移动速度为每秒n个单位,那么每一帧移动的距离distance为:

1float n = 10.0f; // 每秒移动的速度
2float distance = Time.deltaTime * n

然后把计算结果distance累加到game object自身的local Position的y值上即可

1GameObject o = GameObject.Find("AnObject");
2var localPos = o.transform.localPosition
3localPos.y += distance;
4o.tranform.localPosition = localPos;

这样子,虽然不确定一秒内到底能执行了多少帧,但在一秒内每帧执行的时长是可以得知的。利用每帧之间的时长乘以每秒物体运行的速度,就可以得到该帧要发生的位移的距离值。最终无论帧速率如何,一秒钟内移动的距离是确定的。当然如果帧速率太低的话,物体移动时就会有很明显的跳跃感。比如一秒钟内要移动一百米,一秒钟内执行了30帧,和一秒钟内执行了1帧,其给用户的观感肯定有着非常明显的差异。

MonoBehaviour.FixedUpdate() 函数中要使用 fixedDeltaTime 变量代替 deltaTime .在 MonoBehaviour.OnGUI() 函数中不要依赖于 Time.deltaTime 进行计算,因为在一帧中 OnGUI() 中可能会被调用多次,那么每次在 OnGUI() 函数中拿到的 deltaTime 值就是一个定值,这显然会导致计算逻辑出错。

2.2 Time.timeScale

Time.timeScale 属性可以用来控制 Time.deltaTime 的大小。如果 Time.timeScale 为1, Time.timeDelta 的值为N。每帧之间流逝的时间为R。这时候:

$ N = R $

当把 Time.timeScale 设置为 0.5 的时,每帧之间流逝的 实际时间依然为R ,但这时候 Time.timeDelta 则变成了 0.5N 。这时候每帧的移动距离 distance ,从原来的:

$ distance = N \times moveSpeed $

变成了:

$ distance = 0.5 \times N \times moveSpeed $

实际每帧所花费的时间还是R,但每帧移动的距离变小了。所以画面呈现了“慢动作”的效果。如果把 Time.timeScale 设置为 2 的时,每帧移动的距离变了,所以画面呈现了快进的效果。可参照示例工程

参考文档

https://aresthedog.com/unity-concepts-update-vs-fixedupdate-vs-lateupdate

FixedUpdate真的是固定时间片执行的吗?

初学unity Time.deltatime的理解

Unity3D研究院之Time.timeScale、游戏暂停(七十四)