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

intent
oop

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

motivationspa

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

    好吧。那永远不会出现。
code

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

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标记方向。
ip

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

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

// 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变量追踪方向。当咱们执行完代码,执行位置丢失了,因此咱们不得不显式存储足够多的数据以便下一帧恢复。

相关文章
相关标签/搜索