在游戏中,由于每一帧需要处理的数据不同,所以两帧之间的间隔时长并不固定。因此,如果在 MonoBehaviour.Update 这种按帧刷新的函数中去处理物理计算,就可能出现以下情况:

上面这张图可能代表了玩家跳跃轨迹的抛物线,在抛物线顶部,玩家可能会碰到障碍物或者拾取道具。但是由于第二帧和第三帧的间隔时间太长,从而错过了这部分。

所以,我们需要一种稳定的函数来处理物理计算,它就是 MonoBehaviour.FixedUpdate。在 Unity 文档中,是这样描述它的:

Frame-rate independent MonoBehaviour.FixedUpdate message for physics calculations.

MonoBehaviour.FixedUpdate 是一个与帧率无关的函数,它总是以“固定频率”进行更新。这里的固定频率并不是指间隔相等的时间点,而是由代码模拟出来的。相关代码很可能长这样:

public static class Time
{
    public static float fixedTime;
    public static float fixedDeltaTime;
    public static float time;
    public static float deltaTime;
}

public class Game
{
    private float lastTime;

    private static float fixedTimestep = 0.02f;

    void Game()
    {
        Time.fixedDeltaTime = fixedTimestep;
    }

    void UpdateEachFrame()
    {
        var thisTime = GetRealTime();

        while (Time.fixedTime + fixedTimestep <= thisTime)
        {
            Time.fixedTime += fixedTimestep;
            // https://docs.unity3d.com/ScriptReference/Time-time.html
            // When Time.time called from inside MonoBehaviour.FixedUpdate, it returns Time.fixedTime
            Time.time = Time.FixedTime;
            FixedUpdate();
        }

        Time.time = thisTime;
        Time.deltaTime = thisTime - lastTime;
        Update();
    }
}

Update() 方法前,FixedUpdate() 会被调用零次或多次,具体多少次取决于每一帧的时间以及 fixedTimestep 的值。每一次 FixedUpdate() 的调用,都能保证 Time.fixedTime 是按固定间隔增长的。因此,在 FixedUpdate() 中处理物理计算,就能保证每一帧的物理计算都基于相同的时间间隔。