入门:游戏主循环

作者:eastecho
2014-02-03
21 38 1

主循环是一款游戏或者框架的核心以及基础,它会让游戏以及动画看起来是在做实时的运行。几乎所有游戏(除了回合制等几种类型以外)都要基于主循环以及精确的时间控制。

下面就是一个最基本的主循环示例代码:

注意:这里采用的所有代码均是伪代码,仅仅是为了讲解而使用。

void main()
{
  bool running = true;

  init(); // 初始化整个体系,框架、图形、声音等等

  while(running)
  {
    update(); // 执行游戏逻辑
    draw(); // 绘制游戏

    if(KeyDown("Escape"))
      running = false;
  }

  exit(); // 退出,关闭各种接口等
}

主循环每次执行的时候,都会调用指定好的函数来执行相应的工作,比如在上面代码中我们设置 update() 来处理游戏逻辑,设置 draw() 来绘制游戏当前的画面。比如下面这个例子

void update()
{
  player.x += 1;
}

每一次游戏逻辑函数被触发的时候,都会将玩家角色的水平 x 位置加 1,这样,经过相应的 draw() 方法处理,就会看到角色在横向运动。

但是,上面这个主循环有着明显的问题,那就是:主循环能够被执行的次数是取决于机器配置的,越快的机器,主循环执行的次数越多,那么角色也就运动得越快。

当然,如果我们知道运行游戏的硬件系统是一致的,比如说都运行在某种主机平台上,那么,还是可以直接使用这样的主循环的。

那么,既然这种主循环不是很合理,我们希望游戏在任何系统上都保持一致的速度,那就需要引入基于时间的主循环了。

基于时间的主循环

下面这个主循环例子基于度过的时间,那么,会在不同机器上表现达到一致:

void main()
{
  float totaltime = GetTime();
  float lasttime = 0;
  float deltatime = 0;
  
  init();
  while(running)
  {
    lasttime = totaltime;
    totaltime = GetTime();
    deltatime = totaltime - lasttime;

    update(deltatime); 
    draw();

    if(KeyDown("Escape"))
      running = false;
  }
  shutdown();
}
void update(float deltatime)
{
  player.x += deltatime
}

我们通过 GetTime() 取得程序运行的时间,每一次主循环执行的时候我们都取得时间差,然后通过时间差来决定更新距离。

我们用 lasttime 来记录上一次的时间,然后通过 GetTime() 取得当前的时间,然后减去上一次的时间,就得到了 deltatime ——时间差。

totaltime = GetTime();
deltatime = totaltime - lasttime; 
lasttime = totaltime;

那么还有一种是通过时钟来直接触发的:

基于时钟的主循环

有一些语言或者框架提供了按照时钟触发的方式,可以设定固定的触发时间间隔,比如下面的例子:

var interval;  // 声明 interval,用来标识触发器

void main()
{
  init();
  interval = setInterval(update, 1/30);  // 设定 1/30 秒触发一次,执行 update
}
void update()
{
  if(KeyDown("Escape")) {
    clearInterval(interval);  // 退出的话,停止事件跟踪
    shutdown();
  }
  player.x += 1;

  draw();
}

可见,这种方式可以通过系统时钟来不断的触发主循环 update(),可以精确的控制游戏的运行状态。它可以保证游戏在任何机器上都以同样的速度运行。现在很多主流的游戏框架都支持通过时钟来触发事件。

近期点赞的会员

 分享这篇文章

eastecho 

从前的边城浪子,现在的路人乙 

您可能还会对这些文章感兴趣

参与此文章的讨论

  1. 张燊 2017-08-31

    赞,更新和循环是游戏能够推进的核心机制

您需要登录或者注册后才能发表评论

登录/注册