游戏循环及实时模拟

游戏是实时的,动态的,互动的计算机模拟。

渲染循环

要在屏幕上快速连续地显示一连串静止影像,显然需要一个循环。在实时渲染应用中,此循环又称为渲染循环(render loop)

while (!quit) {
// 基于输入或者预设更新摄像机变换
updateCamera();

// 更新场景中所有动态元素的位置,定向及其他相关视觉状态
updateSceneElements();

// 把静止的场景渲染至屏幕外的帧缓冲(“背景缓冲”)
renderScene();

// 交换背景缓冲和前景缓冲
swapBuffers();
}

游戏循环

游戏由许多互动的子系统构成,包括输入输出设备,渲染,动画,碰撞检测及决议,可选的刚体动力学模拟,多玩家网络,音频等。在游戏运行时,多数游戏的子引擎系统都要周期性地提供服务。这些子系统所需的服务频率各有不同。

最简单的方法是——采用单一的循环更新所有的子系统。这种循环通常称为游戏循环,因为它是整个游戏的主循环,更新引擎中的所有子系统。

常见循环架构风格

  • 回调驱动框架
  • 时间驱动框架

抽象时间线

时间线是连续的一维轴,其原点\((t = 0)\)可以设置为系统中其他时间线的任何相对位置。

真实时间

直接使用 CPU 的高分辨率计时寄存器来量度时间,这种时间在所谓的真实时间线上。

游戏时间

我们可以为解决问题定义许多所需的时间线。例如游戏时间线,此时间线在技术上独立于真实时间。若希望暂停游戏,就可以简单地临时停止对游戏时间的更新。若要把游戏变成慢动作,可以把游戏时钟更新的慢于实时时钟。

局部及全局时间线

局部时间线能按原来制作或录制片段的时间量度播放时的进站时间。这些播放的速率可以有多重效果,比如加速,减慢,或者反向播放。所有这些效果都可以视觉化为局部和全局时间线之间的映射

测量及处理时间

  • 帧率(frame rate):一连串三维帧以多快的速度想观众展示。
  • 赫兹(Hertz):帧率的单位,即每秒的周期数量。
  • 每秒帧数(FPS,frame per second):等于赫兹,在游戏和电影里使用比较多。
  • 帧时间(frame time):两帧之间经过的时间。也称为时间增量 \(\Delta t\)

游戏循环中帧时间的使用例子如下。

// 开始时间设置为理想的帧时间(30 FPS)
F32 defaultDt = 1.0f / 30.0f;
F32 dtSeconds = defaultDt

// 在循环开始前先读取当前时间
U64 tBegin = readHiResTimer();

while (true) {
runOneIterationOfGameLoop(dtSeconds);

// 再读取当前时间,计算增量
U64 tEnd = readHiResTimer();
dtSeconds = (F32)(tEnd - tBegin) / (F32)getHiRestTimerFrequency();

// 如果 dt 过大,一定是从断点中回复过来,那可以重置 dt
if (dt > defaultDt) {
dt = defaultDt;
}

// 把 tEnd 用作下一帧新的 tBegin
tBegin = tEnd;
}