第二章 游戏体系结构

FireFly 专栏收录该内容
1 篇文章 0 订阅

第一节   实时软件

计算机游戏是一种应用软件,它是一种实时应用软件。这是我们必须要清楚的,因为这样分类能帮助我们理解游戏程序是怎样运行和编码的。为了还对这些概念还不时很清晰的读者,我下面停顿一下,简短地解释一下它们。

正式定义上,实时软件的意思是有时间界限的计算机应用软件,或者更一般说,应用软件的数据响应必须在给定的一个时间段内。例如,在机场大显示屏幕显示乘客信息的计算机程序:用多行文本显示飞机数量、状态、降落时间等。明显地,软件反映的是实时信息——如飞机晚点时,广播延时等。像这样的信息是不可预知的,应用软件必须作出处理和正确的反应。此外,这些因时而定的信息必须以视觉的方式将数据呈现在屏幕上。上述就是实时软件相关解释。

现在,我们考虑一个比较棘手的例子问题——一个设计用来协助空中交通管制的应用软件。如图:

 

 

软件通过雷达来获得空中信息,在屏幕上显示飞机以及它们的轨道,使地面人员能引导飞行员及时、安全地到达目的地。我们可以看到,软件由下面几部分组成:

     一个数据获得模块——本例中,应当加上雷达。

     一个显示/计算模块,帮助地面人员了解数据。

     发送信号引导飞机的交互模块。

这里,我们比上例更进一步,假设我们正在看的是一个实时交互应用程序,它能反应任何时间点的事件并显示相关信息,还允许操作人员处理这些事件。这种模型正和游戏相差无几。想象一下,我们除去雷达,用软件模拟出空中交通,并告诉使用者:他必须让飞机安全着陆。最后增加一个记分牌和结束画面,这正不成了游戏吗?

所有的游戏都是交互式的实时应用软件,操作人员(以后我们称为玩家)和用软件模仿的实时行为相交互。敌人追赶我们,电梯上下运动,来回射击,都是游戏里的虚拟实时成分。

但是在游戏里,你可能有足够的时间来思考。游戏同样也是时间限制;它们必须以每秒高于25帧的速度显示。这就限制了游戏的实时模拟器和表现层。我们不可以使用超过机器硬件分给我们的时间片。然而,游戏就像是魔术。它好像让看上去不可能发生的事情发生了。技巧超乎玩家意料地让多媒体效果超出硬件呈现限制。

概括说来,游戏是感知实时数据的虚拟模拟器组成的实时交互应用软件。它让显现模块显示游戏,用控制机制让玩家和游戏交互。

因为极高的交互频率,游戏所能模拟的东西受到了限制。但是游戏编程将挑战这种限制并且在表现和模拟上超越平台提供力。这就使游戏编程的关键和本书的主题。

第一部分,“Gameplay Programming”,讲述实现游戏世界的实时模拟器的编写。

第二部分,“Engine Programming”,覆盖的是表现层。

 

实时循环(Real-Time Loops

和以前提及的一样,所有实时交互软件的三个组成部分并发地运行。首先,场景的状态必须不断地产生(如雷达对空间飞机的感知和游戏中模拟场景的更新)。第二,操作人员能和它交互。第三,模拟场景和玩家输入所应该引起的结果必须呈现给玩家。最后,玩家只不过是游戏实体的一部分。为了便于理解,我将根据这条规则把游戏当作由更新程序Uptate”和绘制程序“Render”组成的应用程序。

当我们用具体游戏代码实现这两个程序的时候,问题随之而来了。我们怎么才能保证它们能同时地运行,用一个小小的窗口来反映现实世界呢?在理想状态,这两个程序将运行在由许多并行处理器组成的超级机器上。这样,两个程序都能无限制的使用硬件资源。但是,实时技术消除了很多局限性。现实中的许多计算机只由有限的内存和一个CPU组成。诚然,CPU不能在任何时间同时执行两个任务。所以,人们不得不想尽各种办法来解决。

起初方法是执行在一个循环中执行它们。如图:

所以,每一个“Uptate”后面都跟着一个“Render”调用,这就保证两者都同样重要。用这种方法,逻辑和表现大幅度增加了。但是如果复杂场景中敏感变化所引起的帧率改变时会发生什么呢?想象一下,如果复杂场景中10%的变化而导致引擎变慢,明显,逻辑圈循环的数量也会因此改变的。甚至更糟的是,如果一场游戏中,运行速度快的机器为第五个因素能赢过旧的机器作,那将会发生什么呢?难道AI在这些较小的机器上运行得慢吗?明显,使用第一种方法引起了一些有关游戏因执行变化而受影响的有趣问题。

为了解决这些问题,我们必须分析每一个代码的组成。一般来讲,“Render”部分必须尽量在硬件上执行。较新较快的机器能提供更流畅,更好的帧率等。但是场景内容不能被机器速度的提高所影响。实体也必须按照游戏设计的速度来变化,否则,游戏只能被扔掉。想象一下,如果你购买了一个足球游戏,因为硬件原因,游戏要么太快要么太慢。但是,让“Render”和“Update”同步的话使编码复杂化了,因为“Update”有固定的频率,而“Render”没有。

解决这个问题的一个办法是同步保持“update” 和“render”,但是根据连续调用的共用时间改变“update”例程的间隔。我们计算出实时单元的共用时间,“update”部分就能用它来决定事件的速度,因此来保证事件发生在合适的时候,且与硬件无关。无疑,“update”和“Render”会在一个循环里面。但是“update”部分依赖硬件的速度-硬件速度越快,每次调用完成得越好尽管在特殊的情况下,这是一个有效的办法,但是一般来说它是没有价值的。随着速度和频率的升高,提高“update”的速度已经没有意义。“character AI”真的需要每秒搜索50次吗?决策系统是一个复杂的过程,如果执行超过需要的话,那简直是浪费时钟周期。

另外一个解决同步的办法是用一个双线程方法:一个线程执行“Render”部分,另外一个线程负责“Upate”。通过控制每个线程的调用频率,我们确保“Render”部分尽可能持续的被调用和“Uptate”的硬件无关性。大多数游戏中,AI每秒执行10-20次已经够多了。

如果一个动作游戏以60 fps的速度运行,而AI在另外一个线程中以15 fps的速度运行。明显,只有四分之一的画面实现AI更新。尽管这是一个保证定速逻辑的好的实践,但是必须小心对待。例如,我们怎么确定四个画面有效地共用同一个AI周期,流畅地显示活物和图像呢?

如果所有在同一个AI 周期里的画面看上去完全一样,那么多画片也就没什么意义了,活物行速将达到15 fps。为解决这个问题,AI被分成两个部分。实时AI代码按固定步骤执行,反之,一些简单的程序如活物分类和弹道更新程序根据每个画面来处理。用这种方法,每秒额外的画面对玩家来说至关紧要。但是,基于线程的方法有一些问题需要解决。基本上,这个方法很好,但是在一些硬件上不能很好实现。一些单CPU机器不能很好地处理线程,特别是一些精确的时间函数。在频率周期中发生的变化会降低玩家的可体验性。问题并不在于产生线程的函数调用,而是精确的系统时间函数。因此,我们必须找出一个区域来在单CPU上模拟线程。

 最流行的可选方法是同过在单线程程序中使用规则的软件循环和定时器来实现线程,这并不支持并发机制。它的主要思想是连续地执行“Update”和“Render”调用,为保持固定的调用频率而跳过“Update”调用。我们减弱“Update”中的“Render”程序。“Render”被尽可能快地调用,而“Update”和时间同步。为达到这一效果,我们必须为 “Update”中的每一个调用记录时间邮戳。然后,再以后的反复循环中,我们从上次调用计算共用时间,并和期望频率相比。同过这样,我们测试是否需要一个“Update”调用来保持正确的调用频率。例如,如果你想以20次每秒的速度运行AI,你就必须每50毫秒调用一次“Update”程序。因而,你要做的就是保存你每次调用“Update”程序的时间点,在共用50毫秒后再执行它。很多时候,这种机制比基于线程和简单程序的方法提供了更好的控制。你不必去担心共享内存,同步等等。实际上,它是一个原始的基于线程的方法,如下图:

这是它的C代码;

 Long timelastcall=timeGetTime();

  While (! end)

  {

     If (( timeGetTime()-timelastcall)>1000/frequency)

         {

             Game_logic();

             Timelastcall=timeGetTime();

} 

}

   注意看看上面我们是怎样使用Win32 API timeGetTime()调用来作为定时器的。它毫秒级返回的Windows自启动以来的时间。因此,通过两次timeGetTime()调用结果的相减,我们可以得到精确到毫秒级的时间差。

上面的代码部分说明了我们所关注的东西,它是一个好的起跑点。然而,它似乎离专业游戏还很远:我们假设逻辑时钟0次就完成了,我们没有处理Alt-Tab游戏关等。为完整说明,我下面提供一个专业级别的游戏循环,思想是一样的,只多用了一步来实现更好的控制。

代码如下:

time0 = getTickCount();

while (!bGameDone)

   {

   time1 = getTickCount();

   frameTime = 0;

   int numLoops = 0;

 

   while ((time1 - time0) > TICK_TIME && numLoops < MAX_LOOPS)

      {

      GameTickRun();

      time0 += TICK_TIME;

      frameTime += TICK_TIME;

      numLoops++;

      }

   IndependentTickRun(frameTime);

 

   // If playing solo and game logic takes way too long, discard

   // pending time.

   if (!bNetworkGame && (time1 - time0) > TICK_TIME)

      time0 = time1 - TICK_TIME;

   if (canRender)

      {

      // Account for numLoops overflow causing percent > 1.

      float percentWithinTick = Min(1.f, float(time1 - time0)/TICK_TIME);

      GameDrawWithInterpolation(percentWithinTick);

      }

   }

现在,我们一步一步地来。上面循环由两部分组成:第一个实现游戏逻辑(while循环控制GameTickRun执行),第二部分就是“Render”部分(if控制GameDrawWithInterpolation)。

 在游戏逻辑部分,当从上次逻辑调用开始的共用时间超过了一个TICK_TIME (毫秒级)时,我们控制它。如果你想让你的AI代码每秒被运行20次,那么我们TICK_TIME应为50;然后,我们把它放到一个while子句中,因为我们可能会同时运行号及各游戏逻辑,特别是有暂停、磁盘交换使程序变慢等时候。可以看到,我们把TICK_TIME 合并到新定时time0中。然后,调用IndependentTickRun来处理玩家输入,完成一般的“家务管理”等。这些程序很少需要精确的时间功能。

 最后,我们到了“Render”阶段。我们开始计算当前已经包含的时钟,存储在percentWithinTick中。这是很有趣的,因为它允许我们触发“Render”调用。

 现在让我们在我们最初的总体水平下,分析和探究游戏逻辑和呈现表达部分。同过这样做,我将介绍一个全局的游戏架构,帮助你理解做游戏必需要用哪些软件片(soft pieces,以及它们的内在联系。

 

 

第二节   游戏的逻辑部分

 

首先,大量代码块用来保持游戏模拟器的运行。为理解我们的全局架构,我们将把它分解成三大块:updating the player, updating the world, and updating the nonplaying characters (NPCs)

 

Player Update

游戏必须执行一个程序来更新玩家状态。程序的第一步必需检测玩家的交互请求。玩家的交互请求通过许多的控制装置来实现,如操纵杆、键盘、鼠标。但是最终结果是一样的-一连串能被游戏代码识别的数字指明玩家的控制状态。用抽象的设备控制器是个不错的主意,这样游戏代码不需要和物理控制器相结合。一个抽象的设备控制器用一个通用接口处理操纵杆和键盘。我们会在第五章-“用户输入”讨论输入过程和设备。

  因为有能限制运动员的行动范围的一些问题,我们将不直接地把控制决定映射到玩家的行为。他也许想要向前行进,但是一堵墙可能刚好堵住他的去路。因此,第二个程序必须设计来限制玩家的交互。这些限制可以是自然物体。就如在以前例子中,或者更复杂,与逻辑结合。例如,玩家可以按下“开门”控制,但是如果他手里没有正确的钥匙,门是不会开的。

请记住,我们是在谈论适用于任何游戏的一般原理,因此,我们将这样的程序为“玩家限制”管理器。这种程序的有效应用于碰撞检测,我们将在第二十一章-"程序技巧"中讨论。一旦我们感觉到玩家的控制器并作限制检查,我们就需要一个更新程序小来让玩家看到他们交互的结果,同时再计算游戏状态。下面我们用两个例子来帮助理解游戏三部分组成的内在联系。

 任天堂的The Legend of Zelda。涉及到的三个程序将执行下面的任务:

 1.“玩家输入”模块必须有效取得游戏控制器的读入,然后把原始数据转化成有意义的游戏数据。例如,“操纵杆被推倒左边并且A按钮被按下”应该翻译成“要求物体移动到左边,并且使用当前装备的武器”。

 2.“玩家限制”程序存取游戏的结构,因为我们需要知道玩家所处的水平层次和他身边的物体(游戏中)。可以通过几何限制来计算,即碰撞检测和逻辑限制,主要处理玩家所在的状态和能实现的交互。这常常是三部分中最难的了,特别随着游戏复杂度的增强。

 3.“玩家更新”程序把限制映射成交互和正确的游戏层次的反应。如果玩家按往左边并且左边方向没有障碍物,我们必须触发移动“活物”,更新它的位置。

  有一些游戏,如Tetris它们没有清晰的屏幕呈现物,但是这样规则同样适用。通常,你可以认为,屏幕上的事物都能通过控制器交互。在Tetris中,指的是从屏幕上方落下来的砖块,没有严格的特征来辨认。我们在第一阶段检测玩家的输入,然后第二阶段,我们检测砖块限制;然后我们执行玩家更新程序来保存和现实玩家的当前状态。这些限制很容易测定;砖块不能移出屏幕区域,不能掉出地面线,和不能在当前砖块还在落下时下落。在玩家更新段中,我们根据玩家的输入简单地移动或旋转砖块,用一个默认行为保持平衡-不管玩家干什么,保持砖块不停下落。这种停顿的,非交互的行为在游戏中很常见,促你你拼命地加速玩。一些街机游戏限制你游戏的时间(除非你找到继续游戏的令牌)。

 

World Update

在生动的游戏世界里显示动态行为的理念从第一个游戏开始就呈现出来了。如Pong Space Invaders。除玩家动作之外,游戏世界保持自己的议程,呈现用户反应的行为。例如,用户试着躲避一块迎面而来的岩石(在Asteroids中);试图击会球(在Arkanoid中)等。

游戏世界的更新有效实现了游戏系统,使游戏更有趣味性。所以,这部分游戏代码在现代游戏中特别重要,也更加复杂。

首先,必须对两个广泛的游戏实体进行区分。在一方面,我们有被动实体,比如说墙以及游戏条款。为提供一个更正式的定义,游戏有一些没有附加上行为的条款。这些条款在玩家的限制方面起关键作用,但是对游戏更新来说却不是很重要。在一些大剧情游戏中,游戏更新程序预先选择一个子部分,所以玩家限制部分集中在这些基础上,因此变得更为有效。如一个地形冒险游戏,我们存贮一个指向玩家所砸房间的指针,只对那个房间的碰撞进行检测。

但是游戏更新的多多数时间用在其它类型的实体-它们具有内在的行为。从衬托物如飞鸟到敌人或开着和关着的门。这些必须和游戏经历保持一致。有些游戏将活体划分成简单的逻辑项-如门、电梯或移动的月台——和有独特行为的实时敌人。这些区别有代码的复杂性产生。逻辑问题的解决只需要不多的一些代码,因此,实时敌人需要高复杂度和计算代价的人工智能。

在一般的游戏架构中,我们假设有大量的逻辑的或是AI的活体元素。它们的更新过程由四部分组成。首先,过滤器将选择和游戏有关的元素。从玩家的立场看来,离他有 10英里 远的敌人不会对他构成威胁。过滤器不能取消任何实体,因为一些游戏将仍然需要计算所有实体的行为。但是很多时候,level-of-detail (LOD)技术将用来处理远项实体,所以,对他们进行中肯地分类是由必要的。再者,活体元素的状态必须更新,这里,逻辑和智力实体的差别很明显了。后者需要更多的相关过程来更新他们的状态。

一般来说,在整个游戏框架内,AI系统也将按照一个四步过程处理。首先,必须分析全局和当前状态。对一个飞行模拟器来说,这意味着获得武器系统的位置和种类、状态,以及不断炸毁的飞机-AI控制器的或玩家的。游戏的目的很简单:击落玩家。第二,必须感知限制条件,包括逻辑和我们已经感知的玩家的几何限制。在飞行模拟器中,主要的限制就是避免与玩家或地面相撞,我们不能闯入附近的山脉。分析后,我们了解了AI实体的状态,玩家的状态,要达到的目的,已经所应用的限制。

回到我们的游戏架构。第三步,一个决策引擎必须执行、有效地产生行为规测。飞机即将转弯,然后开始射击等。一些游戏实现瞬间飞机,这样每帧都得再计算。每一帧画面,必须产生最佳的弹道来决定玩家的飞机是否爆炸。但是大多数决策程序产生战略需要持续的时钟周期。一个飞行模拟器可能需要很多秒甚至是分钟来做决策,还来,AI周期主要只是根据条件来提炼计划。

第四,我们需要更新游戏状态。我们必须储存数据,如敌人是否移动,或者从数据结构中消除玩家击落的飞机。随后我们具体学习AI时你会看到,第四步过程和许多游戏的AI结合得很好。

以上是完整的游戏逻辑框架。下面是一它的结构概要伪代码。

 

Player update

                   Sense Player input

                   Compute restrictions

                   Update player state

World update

                     Passive elements

                        Pre-select active zone for engine use

                     Logic-based elements

                        Sort according to relevance

                        Execute control mechanism

                        Update state

                     AI based elements

                        Sort according to relevance

                        Sense internal state and goals

                        Sense restrictions

                        Decision engine

                        Update world

End

 

 

 

 

 

第三节   游戏的表现部分

 

如果没有多媒体效果,游戏将无趣而又令人厌烦。作为人,我们对活体,声音,以及使游戏更生动形象的技术特写很敏感。所以,编写好的游戏当然需要认真对待它的表现。你也许会说,有些游戏只有一点点甚至没有表现价值,你可能是对的。像Tetris,不需要很多的声音视觉表现。但是如果把这个推广当作规则的话,是特别危险的。大量的游戏要求仔细设计多媒体来包装,以吸引玩家。我们需要观察恒星飞船和小行星,来找太空飞行的感觉。我们需要仔细设计环境声音来让我们感同身受。

表现层帮助传达精彩游戏的主要特征之——煽动人们的好奇心。这在许多不错的电影、书籍、游戏中出现,使我们沉浸在里面的情节中。拿一部电影来做例子,在前十分钟,观众忘记了所在的真实世界。观众的工作、烦恼、职责度统统扔到脑后。他(她)被魔法般的情节所迷倒。观众们将在以后的时间里沉浸于故事中。事实上,他们并没有感觉自己是在电影院。他们觉得自己是在扮演一个坏角色或是在探求神秘的事物等。这就是驱使人们好奇的原因。

有些电影试图用极小的表现价值来讲述一个引人注目的故事。教条运动是一个很好的例子。尽管这种趋势也会在计算机游戏中出现,也能制作出许多好的游戏。不管是教条游戏还是非教条游戏,表现同样重要。教条游戏不侧重人的能力,而是综合的讲述故事。因为游戏比电影需要更多的技术来驱动,表现依然不可缺失,甚至是一个简单的游戏。

另一方面,我们不要忘了,大量的游戏是做给群众的。在这种背景下,游戏的表现不可缺少。除了好的游戏系统外,玩家还要求有更好的图像、更多更好的音效。由于不断改进的计算机硬件,这是一个令人左右为难的规定。所以,在这些游戏中,游戏逻辑应最优先考虑外,还得密切关注游戏的表现价值,,确保它能被关大的消费者接受。

现在,让我们来探究一下游戏的多媒体渠道的构成部分。多媒体的关键在于图像和声音,它们是多媒体效果的两个主要因素。我们的一般渠道将会与作为游戏逻辑并且被区分为绘制游戏的那一个类似,绘制不可玩得角色和绘制游戏角色 。记住,这只是一个举例说明构成部分的结构。,并不意味着执行顺序。执行的时候,元素能用另外的顺序绘制。

 

游戏世界的绘制

第一步是视觉上、声音上和游戏场景的绘制。这里,我们侧重游戏的被动元素,如墙、地面,以及简单的、基于逻辑的物体(例如开着的门)。绘制它们需要很少甚至不需要活体,对象往往是固定的。而绘制完全的活体需要不同的设计。

实时绘制完全的游戏场景是几乎不可能的,除非是像Tetris这样的简单游戏。对任何有关的内容,必须在主绘制调用之前考虑哪些应对考虑和哪些不需要,并作过滤。例如,一些远距离和看不到的东西可以被精选掉,因为它们不能给玩家提供信息,并且增加复杂度、减少画面帧率。所以,任一个场景绘制途径或多或少由两部分组成:相关子集的选择和实际绘制。

对图像绘制途径来讲,选择程序实现截割、采集、处理闭塞,所以,结果产生的只是从玩家角度的游戏里所能看得见的部分。这样我们主要绘制的是和玩家相关的东西。这些程序是当代图像绘制的核心,我们将在第十二、十三、十四章中分析。一个可选的辅助方法是应用计算相关数据在合适的地方绘制的可视数据。 500m 远的一棵树可能不需要10000个三角形,一个三角形只占一个像素的极小部分。一个低代价的解决,可以利用没有不利效果的更有效率的表现。

   既然游戏几何被归纳成可视部分和分配了适当程度的细节,那么现在可以把它绘制到屏幕了。我们通过两个步骤达到。首先,几何图形用有效的格式存贮,这一步我们通常称为几何封装(geometry packing)。然后,封装好的几何发送给图像处理硬件。图像处理硬件对封装包很敏感,通过选择最优的输送装置使性能成几十倍地增长。我们将在第十二章探讨“包装”和绘制。如果你需要详细的绘制API,可以在附录B"OpenGL"和附录C“Direct3D”中找到。

音频绘制和图形绘制有细微的差别,我们不能过滤可见的或不可见的东西,你身后的敌人也许是不可见的,但是能听到它的脚步声。同样,有时候也得过滤,通常藉由使用一些距离和公制体积。声音通过计算声源和玩家之间的有效度来减弱或加强。一旦确定了声源,我们就把声音文件发送给声卡。

 

角色(NPC)的绘制

绘制NPC和绘制没有生命的几何体不一样。他们需要表现他们活力的道具。同样,我们可以从过滤特征表开始,只有游戏的角色和玩家相接近的时候,游戏才能打动玩家。此外,一个能见步骤也是相当常见的。只有在剪断和闭塞测试中生还的角色才会被转移至渠道。这对完全活体是特别重要的,用动力学、生命几何学刻划活体比表现静态的被动元素要昂贵得多,所以,我们只应用在必须的地方。我们随便举个例子,有些游戏会用LOD过程来创建一个简单的、基于视觉但远离玩家的角色表现。然后,主活体程序(the main animation routine)被计算,从外框到活体的内部骨骼都有许多的变化。所有这些我们会在第十五章“活体角色”中涉及。但是他们的最终结果是一样的:表现角色状态的当前快照的静态几何数据必须用一帧画片来绘制。在这一点上,如果一个用静态几何简单化的角色是规则的几何体,那么它能救能被处理。它会被被封装后发送给硬件显示。注意,在内部,一些活体方法需要特殊的绘制算法。所以,角色需要和被动几何物体分来来绘制。

 

玩家

主玩家是一个特别的角色(NPC)。但是它的绘制途径比次要的角色要简单,原因有两个:第一,玩家总是可见的。这样,没有必要分配时间去检测他的可见性。毕竟,他应该是被塑造成英雄,所以,让他隐藏起来没有意义。第二,没有必要作LOD处理,许多游戏把玩家当作关键角色来显现,他们总是使用high-resolution meshes。因此,主玩家不会像其他活体一样,封装以及绘制。他有时会比敌人的绘制效果好,因为他是中心角色,需要更多细节表现。

因此,我们提出了他的表现框架。它是一个比较难的过程,你不得不时时回顾它,直到完全理解它。下面,我们用伪代码扼要重述一下。

      World presentation     

        Select visible subset (graphics)

           Clip

           Cull

           Occlude

        Select resolution

        Pack geometry

        Render world geometry

        Select audible sound sources (sound)

      Pack audio data

      Send to audio hardware

      NPC presentation

        Select visible subset

        Animate

        Pack

       Render NPC data

      Player presentation

        Animate

        Pack

        Render

作为全局的绘制,我们在“游戏场景绘制”部分用伪代码来复习一些全部框架。看起来很长,但却是市场上大多数游戏的组成部分。事实上,我建议读者花些时间用框架来分解一两个商业游戏。当我们开始分析游戏的单独部分的时候,读者会这样做很很有价值的。注意,下面伪代码也是本书的索引,我在后面给出了相关章节信息,以方便读者查询附加信息。

       Game logic
          Player update
             Sense Player input      (chapter 5)
             Compute restrictions    (chapter 22)
             Update player state
          World update               (chapters 6 to 9)
             Passive elements        (chapter 4, spatial index)
                Pre-select active zone for engine use
             Logic-based elements
                Sort according to relevance
                Execute control mechanism
                Update state
             AI based elements
                Sort according to relevance
                Sense internal state and goals
                Sense restrictions
                Decision engine
                Update world
       End

 

       Presentation
          World presentation        (chapters 11 to 14, 17 to 21)
             Select visible subset  (graphics)
                Clip
                Cull
                Occlude
             Select resolution
             Pack geometry
             Render world geometry
             Select audible sound sources (sound)
             Pack audio data
             Send to audio hardware
           NPC presentation          (chapter 15)
             Select visible subset
             Animate
             Pack
             Render NPC data
          Player presentation (chapter 15)
             Animate
             Pack
             Render
       End

 

警告:Networked Games

    以上的模型在描述单人游戏实现得很好。但是联网方面需要作一些较小的改变。这些改变基本上影响游戏的逻辑部分,特别是玩家控制和角色部分。回想一下,从另外一个玩家的立场来说,你的角色只是通过网络连接、在另外一台机器是更新的一个NPC。一般来说,需要在模型上作两方面改动。

玩家更新部分需要改变,确保在通过网络发送新的位置给其他玩家时,每一个玩家的完成更新。玩家从网络上接收到信息,然后用它完成表现其他玩家NPC的更新。因此,第二个改变是AI系统核心。网络游戏仍然有AIs ,如自动怪物等。但是,必要还有表现其他玩家的AI,这种特殊的AI模型从连接渠道接收数据,然后反映给本地游戏环境。

除了这两个地方外,网络游戏和常规单人游戏一样,我们这样来理解,我们是显现在屏幕上的玩家,但是其他玩家把我们当作一个特殊的NPC。在第十章“网络编程”,我们将深入讲述NPCs.

 

 

 

第四节 设计过程(Programming Process

 

我们在细节上研究了游戏工程的建筑结构,包括它们的潜在风险和缺陷。为了给读者一个总的看法,我们现在将侧重于今天的软件生产技术和怎么让软件及时交付。现代的游戏需要成百上千的源文件、几十万行的代码。这样巨大的数据量,加上预算、生产周期,使游戏设计成为一项必须有系统处理的任务。

 

阶段

所有的游戏工程都由三个阶段组成(一些工作室在此基础上进行了细分):前期、生产、维护。在第一个阶段,游戏的概念确定好了,测试不同的技术和解决方法直到达到最终结构。这是一个实验性阶段。如厕使游戏系统规则,评估技术、建立早期的游戏环境概念。前期阶段是游戏公司允许实验的唯一一个阶段,因为后来的阶段必须集中在做游戏。前期阶段的结果就是游戏的工作原型——越接近最终成品越好。原型的建立是帮助建立工作流、测试游戏内容和技术途径等。它帮助开发者建立良好的开端:预算、重要事件、团队结构等。在有些时候,原型用来向消费者或发行者展示游戏的潜力。前期阶段的任务是,分析事物,最后完成详细设计。一旦前期完成了,许多问题应该都应该被解答,因为产品此后只是一个劳动加工过程。游戏设计最终定型。今天,一般趋向强调前期的降低将来风险的重要性。在数百万美元预算后,游戏成为巨大的商品。如果经营不善,对能给开发者特别是发行方带来严重的后果。

意料中,前期关键的一项就是开发游戏的技术。如果游戏是基于一个得到许可的引擎,那么应该在前期选定。另一方面,团队准备采用新的技术,那么必须在前期做出游戏原型。在发行者/投资者看来,技术总是被当作是一个潜伏的风险。很多卖得很差的游戏都是由技术原因引起的。所有,把技术看作和游戏一样重要是个不错的主意,在一定程度上,摆脱技术风险。

如果前期完成了,并且u游戏的投资确方定了(或自己生产或卖原型给发布者),生产开始了。这是最长的一个过程,通常需要一到三年来完成。这也是根据前期计划定型的一个劳动时期。根据游戏需要,会对前期的原型作改动。工艺原型集中在游戏系统的关键元素上,因为它们在前期只是粗糙的形式。因此,制造是毫无头脑的组装,而游戏却表现了它们技术上的复杂性。

因为这是一个很长的过程,生产常常被按月或季度分成进度点,标记开发中的关键事件。这些进度点用来确保游戏开发人员接近于理想速度并且向发布方体提供进度报告。展示最近的游戏构造,修正初始设计等等。最后,必须把最终版本的游戏交付给发布方测试和验收。保证游戏尽量少的Bug和达到设计标准。在控制台游戏上,这个过程比在PC机上复杂,因为我们不仅要得到发布方的确认,还有目标平台制造商的对游戏内容和质量的审查。读者还记得当初Atari因为质量问题而导致平台市场不景气吗?平台制造商深深记住了教训,他们不得不控制游戏和平台的协调,以保持自己平台的升值。

测试一到三个月的过程后,游戏的最终版本确定了。这个版本,一般被称为Gold Master,然后被用来复制,包装在精美的盒子里,最后送到商店。因为所有的物理原料(包装盒、手册、封面等)是在测试阶段时另外团队制作的,所以最后一步只需极短的时间。一旦Gold Master出来,大约两周左右,商店就会有这款游戏出售了。

最后一步就是游戏维护。游戏有较短的保鲜期,除非是大剧情的网络游戏。但是,还必须提供游戏支持:补丁、编辑工具、任务更新等,通过它们来延长游戏的保鲜期。网络游戏却是另外i完全不同的世界。游戏中的许多内容是在游戏发行后创建的,这样,游戏者能看到新的游戏区域,执行新的任务等。其他游戏中,需要好好控制游戏服务器,让玩家们能创造好的游戏环境。显然,网络游戏维护时间更长,甚至没有明确的期限。Ultima Online的不同化身现在已经超过五年被访问。它是开发者和发布者的持续的产品支持结果。

下面我们更详细地探讨一下游戏工程的三个阶段。

在游戏的前期阶段,我们将考虑游戏怎么产生,怎么确立游戏特征,和设计编码结构,用这些方法应该建立怎样的原型。接下来,我们将谈论游戏的生产。它是最长的部分,所以我们将彻底地来探讨它。另外,我们会分析维护阶段,来更好理解我们给玩家的承诺而作哪些工作。

 

Preproduction: Where Do Ideas Come From?

  许多不错的游戏都是从原始的游戏设计开始,即从游戏的中心思想开始。通常用一句话定义游戏的类型和游戏系统以及故事的角色。如一个好的例子,“游戏的主角是一个射手,他在野外和大量妖怪战斗,来解救被困的公主。”这是游戏设计最好的开始方法。因为你有游戏怎么设计的最初思想,知道哪一个角色应该设计有趣。用这种方法进行游戏设计,你首先必须了解下面问题:

     玩家是谁?

     玩家的目的是什么?

     游戏的类型是什么?

     游戏怎么进行?

尽管这样,开发者还有可选的方法。有时候,游戏以浓厚的叙述描写开始,比如,“你是一个军事科学家,你的身边全幻想征服世界的士兵。”当然,我们不会对游戏系统叙述太多。游戏可以是缓慢进行的图形冒险游戏,射击游戏,平台游戏等。游戏用叙述去代替编码,因为叙述要比编码容易多了。你也必须理解驱动游戏编码进行的游戏元素和规则。另外甚至更危险的游戏类型因为一些独特的、令人影响深刻的技术而开始,像,“让我们用全新的户外描绘建立游戏。”虽然很多好游戏是这样编写的,但是你必须记住这样一个有启发的事实:只有一些中坚的游戏者才热衷于技术,而大量的玩家却不是。技术并不能让游戏因它而卖出,也不能给游戏增添趣味。

所以,以游戏可玩性开始是一个非常安全的赌注。你可以用少量的技术制作出一个很有趣的原型,并可在以后再加入技术。另一方面,确定一个用大量技术堆积起来的游戏是很困难得,大多数时候你会不得不采用普通庸俗的游戏系统。就像Shigeru Miyamoto所说:“A lot of people ask me if I start designing games with a story in mind, or a particular scenario, or a particular character, but actually I start on a much more basic level. And really what I do is, I start with some basic core experiments, testing out the action on the screen or a specific gameplay style. When we started with Mario, all we had were some blocks onscreen, and we would try to make those blocks bounce around and jump as we intended them to do using the controller. So it's really at that core element that our game design begins.”因此,比较稳定的做法是从游戏的中心思想开始,甚至可以采用叙述,和你的设计人员、团队讨论刻画游戏世界的最好技术。通过根据游戏系统和叙述来采用技术,这样,才会编写出一个匀称、可玩性高的游戏。

我个人建议,团队应该趁早确立“中心思想句”大纲。这样,这些句子就成为游戏的中心设计思想。像Donkey Kong,它的中心句是,“你是一个叫Mario的水管工人,你必须去营救你被一大群人猿绑架在摩天大楼顶上的女朋友Pauline” 。幸运的是,你将有用像这样好的思想满足技术团队的主设计师。他将制作出早期的游戏系统结构,甚至键盘或控制映射等。必须适当考虑历史原因,这样你能知道游戏的角色是什么,以及他的游戏目的。这是开始游戏项目的最好途径,远远好过在细节海洋中迷失。

 

Discussing Feature Sets

任何一个主程序员在游戏前期的最早任务是定义游戏系统和表现的特征。应该显示多少角色?玩家怎么能捡到物品?……这些是设计者和编码团队的共同努力的结果。这样,游戏充满了设计和技术可行性。得到一个被设定在纸上展开的合理特征的一个好方法是使用一个扩充- 收缩的程序。在第一步中,必须建立一个详细记下游戏每一个特征的表。这个阶段,和现实期望无关。它是一个不保险的研究阶段,需要游戏项目的群体努力。你尽管把你近似疯狂的想法写下来,如果它超过一张纸了,没关系,这是好迹象。如果你用一个文字处理软件来完成,那最好不过了。无论在任何时候,记住每一个特征,这样你在以后的步骤中能保持头脑清晰。我们完成了游戏的首要、易扩充的阶段。当特征表确立时,然后就是紧缩过程。根据编码来合并相似的特征。例如,poisons" "power-ups"可以合到一个特征叫“影响生命的因素”,这样就能用一段代码编写出来了。随着你的紧缩,出现了有大有小的特征族。另外,一些特殊的特征将继续单独保留。

然后,回顾一下结果。大的特征族将呈现许多能相似编码的特征。这些一般是你的主要子系统:武器,角色等。它们将组成最后的特征表,因为它们是游戏设计的有效组成部分。相反,单个特征是很危险的,它们是最特定的不被再使用的代码,它们中的许多代码将不会最后发行。

一个好的客观的方法是用极小化方法选择要执行的族。如你所知,极小化能最小化劣势、最大化优点。这将在下面的成本——效益图2D图描述 。

特征确立后,下面优点将呈现出来。

     用户察觉价值

     一般性

明显,我们在编写特征的时候,总是既想细微区分也希望尽量使用通用代码。我们很容易想到下面两点:

     编码大小

     编码难度

如果你的团队很小,你应该考虑前者。后者应该是新团队所关注的。接下来,你必须根据不同的标准来限定每个特征族,并根据下面来分类:

     Minimin. 对玩家很重要但是很容易编写的特征。它们主要是在项目时间允许时编写的,用来提高游戏趣味性。如3D冒险游戏中的飞鸟。不需要很多,也容易编写。

     Maximin .很难编写的根据游戏中的经历来提供的特征。明显,他们应该被及时刻画。例如,能在车里看到驾驶员的赛车游戏。实现活体的骨骼系统是一件有意义的事项。但是对一个明确的游戏,这种努力可能最后得不到报答。

     Minimax .由想象组成的特征:必须根据玩家经验来编写的特征。无疑,假设时间允许,这些必须加到游戏中。角色扮演游戏(RPG)中的人物配置有时很容易实现,它给RPG游戏者提供很多的发挥。另外一个Minimax特征是AI通讯。在一个动作游戏中,可以看到AIs相互同步协作来提高用户的经验,他们的编码难度中等。

     Maximax .游戏系统的基石特征。他们定义游戏系统经历,很难编写。飞行模拟器的户外飞行的绘制编写起来人物很重,但是它却突出了游戏。对这些特征,必须分两步来分析。首先,有实现把maximax特征转化为minimax特征的简单实现办法吗?有时,一个不同的编码策略会使算法简单化。第二,你的团队有粗粒这些特征的能力吗?假如这样的话,有多少能被加到游戏中呢?早早认识到你的局限性比在游戏制作时不得不剪除特征要好。所以,你可以会只选择一些maximax特征,而忘记其他可行的原因。

总而言之,这个过程的最后结果是使设计方面和效率方面都有意义的特征表。这些特征必须有效,对开发团队或多或少是合理的。他们必须能很好地定义游戏,并且能在合理的时间被开发人员建立。

 

Production: Milestones Are King

如果你的前期阶段有效完成了,你会开始游戏原型的核心系统。在游戏生产中,你将把游戏原型转化成成熟的游戏。在这里,游戏规则可以随意发挥,但是有些建议必须听从。下面罗列的是源于现实社会的一些例子。

     More is not better.很多时候你会发现你的团队没有在预定的时间进度进行项目。这样情况一般发生在有压力的末期,你恨不得自己会分身术。在这样情形下,你会试图雇佣新的人员来帮助你。在“人月神话”中,Frederick P. Brooks陈述了在紧张的时候增加人手事实上不会改善什么,只会弄得更糟。你现有的团队将被拉后腿,因为你需要停下来训练新人,他们需要时间来赶上进度。结果将会延期。在前期阶段计划好团队的大小,如果你觉得需要“紧急人员”来帮助的话,确保你选择的人预先知道项目的所有信息,这样他们能很快进入工作状态。

     "On time" is better than "more ambitious." 游戏开发需要有制作新产品雄心的团体。但是不管你的编码速度多快,圣诞节是在十二月。每年大约有百分之五六十的游戏在圣诞卖出。有时候,完成一个产品比进入一个总是试图改进当前部分的致命螺旋要好。这就像人们因下个月将出现新的设备而不买了新的机器。然后,下个月过去了,他们会说,“嗨,下个月又会有新的设备出现。”,迟几个月再买。编写游戏是向市场提供产品,而不是无止境的事情。特征一旦确定,换句话说,不要在项目中增加新的东西。

     Surgical teams and key members are a risk .在一个项目中并不是所有的游戏开发人员是同等重要的。事实上,要有一些人来组成不可缺少的诊断队伍(surgical team)。它包括绘画人员,程序员,或者其他重要的人员。但是如果有一个人离开,就会导致麻烦。有些工作室以这些员工而自豪,但是从风险角度考虑,这样的人员配置不被推荐。如果你有一个关键的成员,例如住程序员,确保他和至少一个其他成员接近。这样,如果他离开了公司,你还可以进行另外计划。许多游戏在生产过程中因为关键成员的离开而被取消或延迟,没有人知道该做什么。应该尊重团队中的每一个人,如果有必要,他们能尽快地处于代替的位子。

     Order of execution .并不是所有的游戏特征都是平等的:有些是游戏不可少的,有些是对基本东西的改进。在Quake or Unreal这样的游戏中,室内描绘和道路选择AI程序一样是关键部分。另一方面,死去活体的玩偶物理学很要必要。用你的特征装置试试这样练习:考虑哪个角色必须被包含,哪个应该被包含。别忘了,编码时间常常比当初计划的要长。因此,对关键特征和附件有明确的区分是好的实践。同样地,根据编码的顺序来考虑。一些特征必须在早期编码,因为他们是游戏的支柱,以后的进展离不开他们。例如,在Age of Empires中,路经选择比图像引擎更紧要。没有路经选择,游戏干脆就不存在。有时,用一个图表来显示要编码的成分和它们的执行顺序很有用。每一个节点表示一个组成部分,每一个分支表示需其他节点完成的节点。如下图例:

Maintenance(维护)

   不幸地,随着时间推移,维护阶段很是平常。开发者发行了没有制作完的游戏,用补丁来解决重要Bug,这是一个极坏的经营策略。另一方面,维护阶段给开发者带来很大的潜力,因为关联到消费者的时候是他们的最终点。因此,确保健康维护的第一步是在前期阶段做好维护计划。

维护的目的必须根据产品给消费者带来的乐趣而最佳化,让游戏的生命周期尽可能长。现在是强大的产品支持的时候了。下面我将概要地说说来自一些成功项目中思想。

第一个思想是,发行新的游戏内容,延长玩家的乐趣。新任务和角色很容易制作,而且玩家的user-perceived value也很好。新任务可以放在网页上。记住,好的数据驱使设计(data-driven design )是成功的关键。在游戏中插入新任务需要在制作中甚至前期要仔细的规划。这些年来,许多游戏成功采用了这样办法。一个引人侧目的例是Black and Whiteby Lionhead Entertainment (developer) and Electronic Arts (publisher)),它是一个神话游戏,玩家通过一种创造物来控制人口-这就是地球上的GOD。游戏中的创造物是动物,如母牛和老虎。但是开发者制作了新的创造物捆绑在不同的产品中,包括杂志、网页、邓。然后,通过购买杂志,你可以获得新的创造物。这是很有趣的事情,因为产生了新的公众联系,得益的不仅是玩家,而且还有杂志方面。

另外的一个思想使给万家提供内容创作工具。这样,一个MOD团体产生了。内容共享不应该看成是一个潜在问题,而是作为增加玩家的机会。游戏爱好者在游戏中交到朋友,创造新的东西等。这种策略的一个好的例子是The Sims。当开发者开始发行游戏内容创造工具的时候,吸引了大量的人。能创建你自己的角色皮肤,对玩家来说是一大特色。事实上,许多回头玩家正是因为这些编辑工具。结果,The Sims差点成为历史上保险期最长的游戏。口述机制产生了大量的爱好者,所以今天The Sims是历史上销量最多的PC机游戏。

MOD制作也是一种有意思的商业。许多团队从MOD团体中雇佣了专门的人员,因为他们是万家中使用编辑工具最好的人。他们用扩展包发布制作的游戏内容,增加了游戏的生命周期。

无论你选择哪一种方法,请确保你的编辑工具的风格能被最终用户接受。很多时候,开发者内部使用的编辑工具没有做任何修改就发布出来,用户很难使用和接受。如果你计划发行内容编辑工具,确信它们适用于最终用户。开发者经常雇用熟悉游戏内部技术的编辑人员。别忘了,通过发行编辑工具,你创造了新的消费产品,游戏者不是开发者。使用编辑人员是一个令人轻松的过程,不是继续开发游戏。因此,开发这些工具时应该谨慎,接口必须简单,用户手册不是命令性的等。

大型的多人游戏的维护是完全不同的事情(或者我应该说成是产品开发)。这些游戏是在维护中制作的,因为我们需要用持续的活动保存一个活的社区等。因为他们是即付即玩得游戏,所以,保持源源不断的内容更新是游戏获利最好的办法了。

最后,维护阶段是组织、存档和证明游戏的时候。让我们正确面对它,许多开发者在冗长的琐碎时间中忘记了良好的编程风格。很多时候,在游戏发布后,编码往往被忘却了。但是维护需要修改源代码。保管和归类可重用模块,以在以后的游戏开发中利用,甚至因此减少团队人数。毕竟,维护阶段没有最终的期限,你可以分批配一些团队成员来维护,在结构上保证项目——一个没有Bug的游戏,一个后来项目可以再使用的游戏。

 

(本章完)

  • 0
    点赞
  • 0
    评论
  • 0
    收藏
  • 一键三连
    一键三连
  • 扫一扫,分享海报

©️2021 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值