意图

  将游戏时间的进度从玩家输入和处理器速度中分离出来。

  动机

  如果让我选一个本书最不能少的模式,那就是这个。游戏循环是游戏编程模式中最精髓的一个例子。几乎所有的游戏都会有它,再也没有第二个应用如此广泛的。但是在游戏之外有很少用到。

  要看它是如何起作用的,让我们把记忆拉回到过去。在哪个编程跟洗碗工一样的青葱岁月。你需要把一堆代码塞进机器,按下按钮,等待,然后出结果。这就是批次模式的编程——一旦工作完成,程序就停止运行了。

  你今天仍然能够看到,虽然谢天谢地我们不要在卡片上写代码了。Shell脚本,命令行程序,甚至是那些本书中的简化小Python脚本,都是批次模式的编程。

  采访CPU

  最后,当程序员们发现必须先把一堆代码放到计算办公司,然后回来等几个小时,才能拿到结果。这改起bug来就会非常慢。他们希望能够得到及时的反馈。交互式编程诞生了,一些最早的交互式程序,就是游戏:

 

  1. YOU ARE STANDING AT THE END OF A ROAD BEFORE A SMALL BRICK
  2. BUILDING . AROUND YOU IS A FOREST. A SMALL
  3. STREAM FLOWS OUT OF THE BUILDING AND DOWN A GULLY.
  4.  
  5. > GO IN
  6. YOU ARE INSIDE A BUILDING, A WELL HOUSE FOR A LARGE SPRING.

  代码如下:

 

  1. while (true)
    • {
      • double start = getCurrentTime();
        • processInput();
          • update();
            • render();
              •  
              • sleep(start + MS_PER_FRAME - getCurrentTime());
                • }

  注意这里的时间间隔不再是可见的帧率了。MS_PER_UPDATE只是我们用来更新游戏的时间粒度。间隔越短,追赶真实时间的处理次数就越多。间隔越长,游戏变化就月剧烈。你会希望这个时间足够短,最好比60FPS还要快,这样游戏就会在快机器上模拟的更准确。

  但是要小心不能搞的太短,你需要确保这个时间段要比update()的时间长,即使是在最慢的机器上。否则,你的游戏就会跟不上真实时间。

  幸运的是,我们为自己留了一些余地。我们把渲染强行从更新循环中分拆出来。这也节省了很大一部分CPU时间。渲染的结果就是游戏可以用固定的时间间隔和恒定的速度运行,不管在什么样的机器上。只不过在慢机器上,玩家看到的变化稍微剧烈一些。

  卡在中间

  我们还遗留了一个问题。我们用固定的时间间隔更新游戏,但是我们在不确定的时间点渲染。这意味着在玩家看来,游戏经常在两次更新之间进行显示。

  这是时间线:

111214hitufo7ruwuo9njc.png
  你看到了,我们的更新操作很紧凑,间隔固定。然而我们的渲染却不一样,比更新频率要低,并且不稳定。这样也还好。令人不爽的是渲染并不一定跟更新在一起。看一下第三次渲染。它正好在两次更新之间:

111215sxxbub131nonzboi.png
  想像一下,一颗要穿越屏幕。在第一次更新的时候,它在屏幕左边。第二次更新的时候它移动到了屏幕右边。游戏在两次更新之间做了渲染,所以玩家期待的应该是出现在屏幕中央。在我们现有的实现中,它仍然会出现在屏幕左侧。这就意味着游戏看起来会磕磕绊绊。

  不过,我们知道渲染的时间离两次更新的时间有多久:它储存在lag里面。当它小于更新间隔时,我们跳出更新循环,而不是为0的时候。这就是我们距离下一次更新的时间。

  当我们渲染的时候,我们把它传进去。

 

  1. render(lag / MS_PER_UPDATE);
复制代码
  渲染器知道每一个游戏对象和它当前的速度。发现离屏幕的左边缘20像素,并且每帧向又移动400像素。如果我们在两帧的正中间,我们会传入0.5给render()。因此,它就会把华仔中间,在220像素上。哒哒!平滑的运动。

  当然,这也许会造成一个错误结果。当我们计算下一帧的时候,可能会发现遇到了障碍,或者减速了,或者有别的变化。我们渲染的位置有可能跟它下一帧的位置冲突。但是如果不完全更新完物理和AI,我们也不知道。

  所以,这种推演或多或少的是在猜测并且有时会出错。幸运的是这种错误不容易被发现。至少它不会比卡顿更容易被感受到。

  设计决策

  限于篇幅,有更多的内容我们并没有讲。一旦搀和进了像垂直同步,多线程,多GPU这些东西,一个真正的游戏循环就会变得很恐怖。在更高的层次,有几个问题你需要考虑:

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

  更多情况下,这不需要你选择。如果你的游戏运行在浏览器上,你就不能写你自己的传统意义上的游戏循环。浏览器的基于事件的机制天然支持。与之相似,如果你用现成的游戏引擎,你就直接用它提供的游戏循环,而不需要自己造轮子。

  使用平台的消息循环:

  1> 简单,你不需要担心写和优化游戏的核心循环。

  2> 它可以在平台上很好的运行。你不需要关注是不是要给平台时间去处理它自己的消息,缓存消息,或者处理输入冲突。

  3> 你会丧失对时间的控制。平台会在他觉得合适的时间去调用你的代码。如果调用频率和流畅度你不满意,那也没招,更可恶的是,大多数应用消息循环在设计的时候压根就没考虑游戏,所以经常很慢,切不稳定。

  使用游戏引擎的循环:

  1>你真的没必要自己写。写一个游戏循环是很蛋疼的事情。因为核心代码每一帧都会调用,所以一些很小的bug和效率问题都会大大的影响你的游戏。一个可靠的游戏循环是选择已有引擎的一个很重要的原因。

  2> 你不去写它,当然,带来的问题就是,即使引擎不能完美的满足你,你也束手无策。


  自己写:

  1> 完全控制。你可以为所欲为。你可以设计成最适合你游戏的方式。

  2> 你必须要面对平台。应用框架或者操作系统通常都需要处理一些自己的消息。如果你接管了循环,它就得不到这些消息了。你需要手动控制这些消息,好让框架不被卡死。

  如何管理能量消耗?

  这不像5年前那样,游戏是运行在街机或者一些专用的手持设备上。现在出现在了智能手机,笔记本,和其他移动设备上。就出现了你需要关注的问题了。一个游戏运行的很漂亮,但是不到半小时就把玩家的手机搞的没电了,这不会让玩家开心。

  现在,你不仅需要考虑你的游戏看起来很漂亮,还需要考虑尽可能的少用CPU。也就是当你在一帧中做完了该做的时间,就让CPU去sleep。

  尽可能的快跑:

  这可能是PC游戏的做法(也可能运行在笔记本上)。你的游戏循环可能从不让操作系统调用sleep,相反,空余的时钟,会被用来提高FPS,或者图形表现力。

  这回给你带来最好的游戏体验,但是也会耗费更多的电。如果玩家用笔记本,他们就会得到一个很好的暖腿宝。

  限制帧率:

  移动端游戏通常更注重游戏性而不是图形表现。很多游戏都会这只帧率上限(通常是30或者60FPS)。如果游戏循环在时间限制前完成工作,它会调用sleep歇一歇。

  这给了玩家“足够的”体验,并且让电池更抗用一些。

  怎样控制游戏速度?

  一个游戏循环有两个要点:不阻断的用户输入和适应时间分割。输入很直观,关键在于你如何处理时间。有无数的平台可以跑游戏,但一款游戏只能在少数几个平台上跑,如何适应变化是关键。

  固定时间间隔并且不同步

  这就是我们的第一个示例代码。你的游戏循环有多快,跑多快。

  1> 简单,这是主要(呃,也是唯一的)好处。

  2> 游戏速度完全依赖硬件和游戏复杂度。主要缺点就是只要有一点变化就会影响游戏速度。这正是游戏循环要解决的。

  带同步的固定时间间隔

  进一步复杂一点的就是使用固定时间间隔但是加入验车或者同步点,避免游戏运行的太快。

  1> 也很简单。它只比前面的多了一行代码。在多数游戏循环中,不管怎样,都要进行同步。至少你需要双缓存你的图形,并且同步翻转缓冲区,去刷新显示。

  2> 对能量很友好。这对移动游戏显得更重要。你不希望干掉玩家的电池。你只需要让它sleep几毫秒,而不是把更多的处理塞进去,就会节省能量。

  3> 游戏不会跑的太快,这就消除了固定间隔的一半忧虑。

  4> 游戏跑太慢。如果它用太长的时间去更新和渲染,游戏也会变慢。因为这种做饭并没有把更新和渲染分离。它不仅会降低帧率,也会降低游戏速度。

  可变时间间隔

  我在这里把它当成一种可选方案,因为我认识的开发者,大多数都不赞成这种做法。最好记住为什么它是一个坏想法:

  1> 它适应过快或者过慢。如果游戏不能追上真实时间,它就会增加时间间隔,直到追上。

  2> 它会让游戏变得不可控,不稳定。这才是真正的问题。在可变的时间间隔下,物理和网络就会变得更难控制。

  固定的更新时间,可变的渲染

  我们最后展示的代码是最复杂的,但也是适应性最强的。它用固定时间间隔更新,但是如果需要,可以丢掉渲染帧去追赶真实时间。

  1> 它适应过快或过慢。只要游戏能够及时更新,游戏时间就不会落后。如果玩家的机器够好,那就会得到更流畅的游戏体验。

  2> 它更复杂。主要的缺点就是实现起来更复杂,你必须确定一个更新时间间隔,足够小已适应高端机器,足够大以适应低端机器。

www.wantgame.net编译 【原译文

相关阅读:游戏编程设计模式-state
锐亚教育

锐亚教育,游戏开发论坛|游戏制作人|游戏策划|游戏开发|独立游戏|游戏产业|游戏研发|游戏运营| unity|unity3d|unity3d官网|unity3d 教程|金融帝国3|8k8k8k|mcafee8.5i|游戏蛮牛|蛮牛 unity|蛮牛