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() 函数就被调用了多次。
-
通常 FixedUpdate() 函数比 Update() 函数更频繁地被调用。
-
FixedUpdate() 函数可以在一帧内被调用多次,如果帧速率很低,则根本不能在帧之间调用。
-
如果帧速率很高,也可能不会调用 FixedUpdate() 。
-
在 FixedUpdate() 函数被调用之后立即进行所有物理计算和更新。
-
作用力,扭矩或其他与物理相关的函数时,应使用FixedUpdate函数。
-
FixedUpdate() 函数由一个准确可信的计时器调用,而与实际的帧速率无关,因此在 FixedUpdate() 中应用移动计算时,您不需要将您的值乘以Time.deltaTime。
-
Update() 函数是每一帧中执行更新操作的主要(入口)函数。
-
Update() 函数中用来移动非物理系统驱动的物体。
-
Update() 函数用来接受用户输入。
-
Update() 函数可用来实现简单的计时器。
由于 Update() 的执行方式,与物理引擎的执行步调不同,即可能在一帧中, FixedUpdate() 函数执行了多次,而Update函数则只执行了一次。这取决于渲染器在渲染图形时的负载量,以及所耗费的CPU时间。如果在 Update() 函数中执行物理运算时,会导致未知的结果。同时也可以看到: 函数的执行间隔,未必就是TimeManager界面中Fixed TimeStep项中所指定的值。
- LateUpdate() 函数每帧会被调用一次,在 Update() 函数结束后调用
- 所有在 Update() 函数中执行的计算,将会在 LateUpdate() 函数调用之前完成执行。
- 如果实现一个跟随式的第三视角摄像机,则摄像机的状态更新放在 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