控制游戏的速度


  随着计算机速度的飞速增长,软件的运行速度越来越快。但这
在某些情况下却未必是件好事。游戏在不同的机器上,或者在不同
的环境下运行不能保持相同的恰当速度困扰着许多游戏程序员。尤
其有些RPG游戏,在比较高档的机器上运行,按一下键盘,游戏
中的主角便走的比飞还快。即使这没有给玩家带来麻烦,也是在挑
剔的玩家眼中所不能容忍的。                
  也不是说一个游戏必须在所有的机器上都必须保持相同帧数,
即使是想实现也不切实际。其实只要保证游戏角色运动的频率差不
多就可以了。马上你就会想到以一定的频率去控制游戏里的物体的
运动。一个直接的想法是直接将控制函数放在时钟中断函数中。不
过细想一下……别开玩笑了,这样绝对不行。时钟中断函数不能太
复杂,必须保证中断函数的简洁。除了这个,想想在保护模式下(
现在的游戏都是保护模式编程的)时钟中断函数所触及的数据,函
数都要锁住,这会带来多少问题!              
  所以比较现实的方法是做两个计数器。一个放在时钟中断里以
固定频率递增,而另一个随着每次游戏循环递增。前一个量表示到
目前为止应当做完了游戏循环的次数,它通常递增速度应当大于前
者;而后一个则是已经做完的游戏循环的次数。在游戏程序的主循
环里,我们将反复比较这两个量,如果实际做的游戏循环次数落后
了,就让它继续做,直到赶上应当做的数量。否则我们就应该马上
停止,这个时候可以等待或去做其它的事,比如显示一帧图片。为
什么选择画一帧图片?部分原因是,这个工作通常比较慢,需要一
段时间,这样使得实际游戏循环数量又落后于应当做的游戏循环数
量;另外还有部分原因是这个过程还包括了等待屏幕垂直回扫的过
程,在这段时间里,时钟中断函数也在以正确的频率递增计数器。
当然,我们就不能在两个计数器相等时马上继续处理了;而必须等
到画完了图以后。但是当我们花完了图,也可以清楚的知道需要做
多少次游戏循环来跟上应当做的数量。            

下面给出上述算法的粗略的例子程序:
(引用 Grorge Foot 所著的 Allegro Vivce 9.4.3 节的例子)

volatile int target_cycle;
  
void target_incrementor()
{
    target_cycle++;
}
END_OF_FUNCTION (target_incrementor);
    
int actual_cycle;
int end_game;
  
void game_loop()
{
  LOCK_VARIABLE (target_cycle);
  LOCK_FUNCTION (target_incrementor);
  install_int_ex (target_incrementor,BPS_TO_TIMER(cycles_per_sec));
  end_game = 0;
  actual_cycle = target_cycle = 0;        /* 初始化 */
  do {
      draw_one_frame();                   /* 画一帧图 */
      while (target_cycle > actual_cycle) /*如果实际的要做的落后*/
          do_one_game_cycle();            /*   进行游戏循环    */
  } while (!end_game);
}
draw_one_frame 画一帧画, 如果需要还要进行垂直回扫同步.
do_one_game_cycle 为完成一个游戏循环, 并累加 `actual_cycle'.
cycles_per_sec 变量保存每秒钟进行的目标游戏循环的数量.
如果你用过Allegro. 其他的应该没什么问题, 关于 程序中涉及的Allegro函数,
请自行查阅Allegro 手册

本文参考 George Foot 所著的 Allegro Vivace
Section 9.4.3 Regulating game speed


云风工作室制作