前言
游戏里对于世界的计算和显示是有两条独立的时间轴,一个是逻辑帧,另一个是渲染帧。
计算和显示
在游戏世界中,世界可以看作一个包含非常多的状态的对象,这些状态可以根据代码既定规则改变,也可能是由于用户触发交互事件(鼠标键盘等输入设备)来改变。
世界这个对象,需要根据触发事件进行运算,比如在一个地图上,用户可以操纵一个游戏单位移动,敲击键盘的方向键,世界引擎代码收到了触发方向更新的信号,就会尝试把游戏单位的坐标计算后更新,这就是“计算”。
受限于性能,以及为了方便编写代码,游戏引擎把世界的状态更新看作是离散的,也就是每一次更新作为一个逻辑帧。
渲染帧则与 GPU 和显示器有关,显示器是按照一定的帧率,把渲染帧一帧帧绘制到电脑屏幕上,一般的帧率有 60HZ,120HZ,144HZ 等等,以 60HZ 为例,意味着 1 秒内刷新了 60 次画面,也就是约 16 毫秒刷新一帧画面。
对于人眼来说,画面达到10帧左右,就可以感觉到画面中的物体是在运动的,一般的动画和电影都是24帧。
世界状态
逻辑帧的帧率决定了世界的状态每隔多少毫秒刷新一次,比如逻辑帧的帧率是 30 HZ,意味着每隔 33 毫秒世界的状态发生一次更新。
假设当前游戏单位的坐标是(1,1),用户按下方向键的右键,游戏单位的坐标会在下一次逻辑帧中变为(2,1)。
为了保证在不同机器上,世界的状态都是固定更新的,逻辑帧往往不受机器性能影响,不同的机器下,逻辑帧是固定的。
但是渲染帧的帧率会受到不同机器 GPU 性能的影响,有的是 60HZ,有的是 120HZ。
这就会产生两条时间线,以 30HZ 的逻辑帧,60HZ 的渲染帧为例:
0ms State1 Render1
16ms - Render2
33ms State2 Render3
50ms - Render4
66ms State3 Render5
83ms - Render6
99ms State4 Render7
前 100ms,逻辑帧 33ms 更新一次,渲染帧 16ms 更新一次,意味着渲染帧多于逻辑帧,假设在 State1 到 State2 的过程中,游戏单位从(1,1)移动到了(2,1),不做任何处理的情况下,Render1 会渲染出位于(1,1)的游戏单位,Render3 会渲染出位于(2,1)的游戏单位,游戏单位就像是从一个点跳到了另外一个点位。
这种逻辑帧少于渲染帧的情况,一般会引入插值算法,使得在缺失逻辑帧的时候,画面从 Render N 到 Render N+1 过渡的更加流畅平滑。
一上面的时间线为例,Render2 可以插值,渲染出游戏单位运动到(1.5,1)的位置,这样看起来就不是跳过去,而是平滑的移动过去了。
参考
- https://zhuanlan.zhihu.com/p/2011403554175337644
- https://blog.csdn.net/u010180372/article/details/121312636
- https://www.zhihu.com/question/568766453