Update、FixedUpdate、LateUpdate函数,定时器和帧速率
Table of Contents
请尊重原作者的工作,转载时请务必注明转载自: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的一个公有静态的浮点类型成员变量
public static float deltaTime;
这个变量是只读的, 返回本帧到上一帧流逝的时间,单位是秒 。 Time.deltaTime
的使用例子如下:
如果要沿着Y轴移动某个game object,那么假设移动速度为每秒n个单位,那么每一帧移动的距离distance为:
float n = 10.0f; // 每秒移动的速度
float distance = Time.deltaTime * n;
然后把计算结果distance累加到game object自身的local Position的y值上即可
GameObject o = GameObject.Find("AnObject");
var localPos = o.transform.localPosition
localPos.y += distance;
o.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