游戏循环

while(true)
{
    processInput();
    update();
    render();
}

游戏循环的一次更新可以用术语“滴答” tick 或 “帧” frame来描述。

一个游戏循环会在游戏过程中持续地运转。每循环一次,它非阻塞地处理用户的输入,更新游戏状态,并渲染游戏。它跟踪流逝的时间并控制游戏的速率。

跑,能跑多快就跑多快。

首先来看看做一点小改动,我们希望游戏以60帧每秒运行,也就是说我们大概有16毫秒的时间来处理每一帧。

while (true)
{
 double start = getCurrentTime();
 processInput();
 update();
 render();

 sleep(start + MS_PER_FRAME − getCurrentTime());
}
  1. 每次更新游戏花去一个固定的时间值
  2. 需要花一些实际的时间来进行更新
double lastTime = getCurrentTime();
while(true)
{
 double current = getCurrentTime();
 double elapsed = current − lastTime;
 processInput();
 update(elapsed);
 render();
 lastTime = current;
}

把时间追回来

double previous = getCurrentTime();
double lag = 0.0;
while (true)
{
 double current = getCurrentTime();
 double elapsed = current − previous;
 previous = current;
 lag += elapsed;
 processInput();
 while (lag >= MS_PER_UPDATE)
 {
  update();
  lag - = MS_PER_UPDATE;
 }
 render();
}

如果我们的渲染,卡在了两次更新之间是会有问题的,

设想一个子弹正横穿屏幕,首次更新时它在左侧,而第二次更新将它移动到屏幕右端。渲染在两次更新之间的某个时间点进行,所以玩家希望看到子弹出现在屏幕的中间。以我们现在的实现方式,它将依然在屏幕左端。这意味着动作看起来会显得卡顿而不流畅。

我们实际上知道渲染时相邻两帧之间的间隔长度:也就是变量lag 。当这个值小于更新时间步长时,我们跳出更新循环,而不是当lag为0时跳出。那么此时lag剩余的量呢?其实这个量就是我们进入下一帧的时间间隔。

render(lag / MS_PER_UPDATE);

渲染器知道每个游戏对象的属性以及其当前速度。假设子弹在距离屏幕左侧20像素的地方并以400像素每帧的速率向右移动,假设我们在两帧的正中间渲染,传入render() 的参数值即为0.5。故它绘制了下半帧的子弹飞行情况,也就是在距离屏幕左侧220的位置。流畅的动作。

当然,可能会遇到推断错误的情况。当计算下一帧时,子弹可能撞上了障碍物,或者减速了等。我们只是设想其前一帧的位置以及下一帧可能所在的位置并在两者之间插值交换地渲染其位置。除非物理引擎和AI更新完成,否则我们并不能确切地知道子弹究竟会在哪儿。

谁来控制游戏循环,你还是平台