《游戏程序设计模式》 2.3 - 更新方法

intent

    一次通知一堆独立对象处理一帧的行为

motivation

    玩家控制的角色正在执行一个任务,任务内容是偷取死了很久的巫师王骸骨上的珠宝。它犹豫地靠近地穴的入口。诅咒的雕塑并没有向他雷电攻击。没有不死士卫巡逻门口。它直接走进去,拿到战利品,游戏结束。你赢了。

    好吧。那永远不会出现。

    这个地穴需要几个守卫-我们的英雄可以与之搏斗的敌人。首先,我们需要一个守卫在门口巡逻。如果你忽略一些细节,最简单的实现守卫来回巡逻的代码可能就像这样:

while (true)
{
  // Patrol right.
  for (double x = 0; x < 100; x++)
  {
    skeleton.setX(x);
  }
  // Patrol left.
  for (double x = 100; x > 0; x--)
  {
    skeleton.setX(x);
  }
}

    问题是,虽然角色左右移动了,但是玩家看不到。程序被卡在无限循环中,这不是个好的体验。我们真正想要的是每一帧移动一步。

    我们必须移除那些循环,并使用外部循环。这保证游戏在守卫巡逻时,依然可以响应输入,可以渲染。像:

Entity skeleton;
bool patrollingLeft = false;
double x = 0;
// Main game loop:
while (true)
{
  if (patrollingLeft)
  {
    x--;
    if (x == 0) patrollingLeft = false;
  }
  else
  {
    x++;
    if (x == 100) patrollingLeft = true;
  }
  skeleton.setX(x);
  // Handle user input and render game...
}

    后一种比前一种代码更复杂。左右巡逻上面是两个简单的for循环。现在我们使用外部循环,每一帧都从离开的位置重新开始,并使用patrollingLeft标记方向。

    但是这个大约可以工作,我们继续。这些骷髅守卫没什么更多动作,接下来我们添加魔法塑像。他们射出箭状闪电,阻挡角色。

    继续我们的“最简单的实现方式”,像:

// Skeleton variables...
Entity leftStatue;
Entity rightStatue;
int leftStatueFrames = 0;
int rightStatueFrames = 0;
// Main game loop:
while (true)
{
  // Skeleton code...
  if (++leftStatueFrames == 90)
  {
    leftStatueFrames = 0;
    leftStatue.shootLightning();
  }
  if (++rightStatueFrames == 80)
  {
    rightStatueFrames = 0;
    rightStatue.shootLightning();
  }
  // Handle user input and render game...
}

    你可以分辨出这不是我们喜欢维护的代码。我们会将每一个游戏实体的大量变量和必要的代码放在游戏循环中。为了使他们一起工作,我们把代码搅在一起。

    解决这个模式非常简单,可能你已经知道了:每一个实体应该封装自己的行为。这样可以使游戏循环代码整洁,而且很容易添加和删除实体。

    为此,我们需要一个抽象层,通过定义一个update方法创建它。游戏循环维护一个对象的集合,但是不知道具体的类型。它知道的是它们都能update。这使得每个对象的行为与游戏循环、其它对象的行为分离开来。

    每一帧,游戏循环遍历对象调用update。通过调用update,所有对象同时表现行为。

    游戏循环有一个动态对象集合,所以添加和删除都是很简单的-就是直接从集合中添加和删除即可。不再有硬编码了,我们甚至可以使用文件来填充关卡,这正是关卡设计师想要的。

the pattern

    游戏世界维护一个对象的集合。每一个对象都实现了update方法,模拟一帧的行为。每一帧,游戏循环更新每一个对象。

when to use it

    如果把Game Loop比作面包,那么update就是黄油。与玩家交互的相当多的游戏对象采用这个模式或相似的模式。如果一个游戏有太空陆战队,龙,火星人,鬼,还有运动员,那么这是使用这个模式的好机会。

    然而,如果一个游戏更加抽象,游戏对象不怎么活动更像是棋盘的棋子,那么这个模式不适合。像象棋这种游戏,不必模拟所有的棋子,甚至不必每一帧都更新一个卒子。

    更新方法当这些情况时好用:

  • 当游戏同时运行大量对象或系统时。

  • 每一个对象的行为都是独立的。

  • 对象行为随时间流逝来模拟

keep in mind

    这个模式相当简单,没有什么令人惊讶的内容。

    但是,每一行代码都有衍生版本。

 sliptting code into single frame slices makes it more complex

    当你比较前两段代码,你会发现第二段更复杂。都是简单地使守卫左右巡逻,但是后者把这个过程分散到每一帧进行。

    这个修改是必须的,因为要处理用户输入,渲染还有其他的事务,所以第一个不实际。但是记住将代码分散会增加很大的复杂性。

you have to store state to resume where you left off each frame

    在第一个例子中,我们并没有一个变量表示向左向右。它隐式取决于哪段代码正在执行。

    当我们把它改成“一帧一次”的形式,我们不得不创建一个patrolLeft变量追踪方向。当我们执行完代码,执行位置丢失了,所以我们不得不显式存储足够多的数据以便下一帧恢复。


你可能感兴趣的:(《游戏程序设计模式》 2.3 - 更新方法)