下载本文 PDF 版本 PDF

更好的脚本,更好的游戏

更智能、更强大的脚本语言将提高游戏性能,同时使游戏玩法开发更高效。

Walker White, Christoph Koch, Johannes Gehrke 和 Alan Demers,康奈尔大学

视频游戏产业在 2007 年创造了 88.5 亿美元的收入,几乎与电影票房收入相当。其中大部分收入来自大型团队制作的热门游戏。虽然大型开发团队在软件行业中并不罕见,但游戏工作室往往拥有独特的开发者组合。软件工程师在游戏开发团队中占比较小,而团队的大部分由内容创作者组成,例如艺术家、音乐家和设计师。

游戏中的内容创作

由于内容创作是游戏开发的重要组成部分,游戏工作室花费大量资源开发工具,以将内容集成到他们的软件中。例如,入门级程序员通常制作工具,以允许艺术家管理资源,或允许设计师在游戏中放置挑战和奖励。这些工具以软件工程师可用的格式导出信息,可以是自动生成的代码,也可以是标准化的数据文件。

这种内容创作“管道”尚未得到很好的理解,每个工作室都有自己的理念和工具集。许多工具取自电影行业,或与电影行业协调开发。然而,与电影不同,游戏需要互动性。玩家的动作需要视觉反馈;游戏角色应该对玩家的选择做出反应。添加交互功能通常需要某种形式的编程。这些功能也是艺术内容的一种形式,游戏工作室更希望它们由设计师(了解玩家如何与游戏互动,以及是什么使游戏有趣的开发者)而不是软件工程师来创建。

游戏软件作为艺术内容的想法促使许多游戏工作室将其软件开发者分成两个小组。软件工程师负责游戏的将在多个游戏中重用的技术方面。他们致力于动画、网络或运动规划等核心技术,并构建构成内容创作管道的工具。另一方面,游戏玩法程序员创建特定于单个游戏的行为。他们身兼设计师和程序员,实现和调整挑战和奖励玩家的互动功能。

游戏玩法程序员应该制作有趣而不是复杂的算法。游戏工作室设计他们的编程工作流程,以减轻游戏玩法程序员的任何技术负担,使他们能够专注于制作乐趣。这通常涉及游戏玩法程序员和工程师之间的迭代过程。游戏玩法程序员开发功能原型以在添加到游戏之前进行游戏测试。然后,软件工程师使用这些功能原型来设计支持库,这些库用于构建另一轮原型。这是一个有效的工作流程,但游戏公司一直在寻找加速甚至自动化此过程的方法。

除了支持游戏玩法程序员和软件工程师之间的互动外,工作室还在不断寻找将设计师整合到编程过程中的方法。设计师通常编程经验很少,但他们对游戏应该如何玩有最好的直觉。因此,工作室希望工具能够允许设计师,即使不能实际编程行为,至少可以微调其背后的参数。

脚本语言的作用

许多游戏工作室依靠脚本语言来使游戏玩法程序员和设计师能够编程游戏的某些部分。这些语言允许开发者轻松指定对象或角色应该如何表现,而无需担心如何将此行为集成到游戏本身中。脚本语言对于大型多人在线游戏尤为重要,在大型多人在线游戏中,任何代码片段都必须与从应用层到网络层再到数据库的多个子系统进行交互。

用户创建的内容是游戏支持脚本的另一个原因。像 Second Life 这样的开放式虚拟世界使玩家脚本成为一个常见的话题。甚至在此之前,游戏就有玩家开发模组的悠久传统。给定工具(官方或第三方)来修改游戏附带的数据文件,玩家已经能够创造全新的体验。一般来说,模组一直被视为延长旧游戏寿命的一种方式。然而,在某些情况下,它可以创造全新的游戏:商业上成功的《反恐精英》是游戏《半条命》的玩家模组,并且在很大程度上依赖于其母游戏中存在的脚本功能。

脚本语言允许玩家在不访问代码库的情况下修改游戏行为。同样重要的是,它们提供了一个沙箱,与传统的编程语言不同,它限制了玩家可以引入的行为类型。如果游戏具有多人游戏组件,则游戏开发者不希望玩家创建脚本来给自己带来不应有的优势。过于强大的脚本语言促成了许多机器人(执行重复性任务的自动化玩家),这些机器人目前充斥着大型多人在线游戏。沙箱甚至在内部也很有用。通过限制设计师可以创建的行为类型,工作室可以减少他们可以引入的错误数量——这些错误会花费宝贵的时间来查找和消除。

对游戏特定脚本语言的需求

脚本语言的首要标准是它应该使游戏玩法开发快速高效。通常,游戏对象(岩石、植物,甚至智能角色)共享许多共同属性。游戏脚本语言通常是 IDE(如图 1 所示)的一部分,IDE 提供用于快速修改这些属性的表单。然而,脚本语言本身相当传统。许多公司使用传统的脚本语言,如 Lua 或 Python 进行脚本编写。即使是设计自己语言的公司,通常也坚持传统的格式和控制结构。很少有人努力为游戏量身定制这些脚本语言。

传统脚本语言的主要问题之一是程序员必须明确意识到与游戏玩法无关的底层处理问题。性能是此类底层问题的经典示例。动画帧速率对开发者来说非常重要,以至于他们通过计算代码中的乘法或加法运算次数来进行优化。然而,这种类型的分析超出了大多数设计师的技能范围。此外,现有语言几乎没有提供任何工具来帮助设计师提高脚本性能。

设计师在创建内容时也必须考虑性能。如果游戏运行速度太慢,他们可能不得不减少游戏中的对象数量,这反过来会显着改变游戏体验。当《模拟人生》移植到游戏机时,就发生了这种情况。在这个游戏中,玩家通过购买家具或其他财产来间接控制角色(模拟市民)。每件家具都经过脚本编写,以定期向模拟市民宣传其功能。然后,模拟市民将这些功能与其需求进行比较,以确定其下一步行动。然而,家具并非孤立存在;电视机前的沙发比房间里单独的沙发用途更广泛。因此,家具还会定期轮询房间内的其他家具,以更新其功能。由于每件家具都可能与其他家具通信,因此处理房间的成本可能会随着房间内对象数量的增加而呈二次方增长。当该游戏移植到游戏机时,性能问题变得如此突出,以至于设计师不得不引入“风水仪”来防止玩家在房间里堆满太多财产。

游戏开发者有许多技术可用于提高性能。空间索引是处理游戏对象之间以低于二次方成本进行交互的一种流行方法。并行执行是另一种可能性;许多游戏都具有令人尴尬的并行性,开发者可以利用这一事实来使用多核 CPU 和分布式多人环境。然而,这些技术超出了典型游戏设计师的技能范围,留给了软件工程师。

脚本语言的另一个底层问题是大型多人在线游戏缺乏事务支持。单个脚本通常并发执行,尤其是在大型多人在线游戏中,因此设计师需要某种形式的事务来避免游戏状态的不一致更新。事实上,脚本级并发违规是多人游戏环境中错误的主要原因之一。

为了使脚本编写对设计师来说更容易,我们必须为他们提供简单的工具来解决这些底层问题。这些问题都不是真正的新问题;多年来已经开发了许多编程语言来解决这些问题,但这些语言中的大多数使编程变得更加困难,而不是更容易。幸运的是,设计师不需要任意的脚本语言;他们只需要一种语言来帮助他们编写游戏。

从模式到语言特性

尽管存在这些问题,游戏仍在开发中。游戏开发者提出了许多想法,即使不是完整的解决方案,也确实缓解了这些问题。这些想法通常以编程模式的形式出现,这些编程模式随着时间的推移被证明是成功的。虽然开发者在创建游戏行为时使用这些编程模式,但脚本语言通常不显式支持它们。面向对象编程语言如此成功的原因之一是,面向对象编程模式在支持它们的语言出现之前很久就存在了。类似地,通过检查游戏开发中现有的编程实践,我们可以设计出脚本语言,这些语言只需要对开发者进行非常少的再培训。开发脚本语言的挑战在于识别这些模式并创建语言特性以最有效地支持它们。

状态-效果模式

游戏开发中一种流行的模式是状态-效果模式。每个游戏都包含一个长时间运行的模拟循环。游戏对玩家输入的响应速度完全取决于模拟循环的处理速度。在状态-效果模式中,模拟循环的每次迭代都包含两个阶段:效果和更新。在效果阶段,每个游戏对象选择一个动作并单独确定此动作的效果。在更新阶段,所有效果都被组合并更新游戏的当前状态,从而为模拟循环的下一次迭代创建新状态。

由于这两个阶段,我们可以将游戏对象的属性分为状态和效果。状态属性表示模拟循环的上次迭代之后的世界快照。它们仅在更新阶段被更改,并且在效果阶段是只读的。另一方面,效果属性包含游戏对象的新动作,并且在更新阶段使用效果更新游戏的状态。由于游戏对象之间的交互在逻辑上是同时发生的,因此在更新阶段之前永远不会读取效果值。因此,在某种意义上,效果值在效果阶段是只写的。

游戏物理学提供了许多这种模式的例子。在模拟循环开始时,每个游戏对象都有一个当前位置和速度,记录为状态属性。为了计算新的速度,每个对象计算作用在其上的所有力的矢量和,例如碰撞、重力或摩擦。换句话说,力属性在模拟循环期间可能会被多次写入,但在循环结束时将所有力值加在一起之前,永远不会读取它。图 2 中的示例说明了使用状态-效果模式来模拟在势场中移动的对象。变量 force 是此计算中的一个效果。在效果阶段,我们只增加其值,从不读取它来确定控制流。虽然大多数实现会读取 force 的旧值来执行此增量,但这并不是必需的;我们也可以将所有这些力值收集在一个列表中,并在效果阶段结束时将它们加在一起。

大多数时候,游戏开发者使用状态-效果模式来手动设计非常特定情况下的高性能算法。这是因为它具有几个属性,可以让他们显着提高模拟循环的性能。效果阶段可以并行化,因为效果赋值互不影响。更新阶段也可以并行化,因为它仅包含效果的聚合和对状态变量的更新。这不需要手动完成;如果脚本语言知道哪些属性是状态属性,哪些是效果属性,那么它可以自动执行大部分并行化,即使是在没有经验的设计师编写的脚本中也是如此。这类似于 Google 使用其 Sawzall 语言和 MapReduce 模式实现的目标;特殊的聚合变量执行与效果属性非常相似的功能,并且该语言允许 Google 的程序员处理数据,而无需了解程序是如何并行化的。2

自动并行化是另一种执行模型的示例;游戏使用与程序员指定的控制流不同的控制流来运行脚本。由于模拟循环在逻辑上同时处理所有游戏对象,因此我们可以按任何顺序处理它们,前提是我们始终产生相同的结果。因此,替代执行模型是优化游戏脚本的最简单方法之一。康奈尔大学正在开发的 SGL 脚本语言使用了另一种不寻常的执行模型。1 该语言基于以下观察:以状态-效果模式编写的游戏脚本通常可以使用数据库技术进行优化和处理。脚本编译器将所有脚本收集在一起,并将它们转换为单个内存查询计划。它不是使用显式线程,而是构建一个数据管道,该管道允许代码以自然的方式并行化。这些数据管道中的许多与游戏程序员在图形处理单元上编程时创建的管道相似,只是这些管道是自动生成的。

受限迭代模式

迭代是游戏开发中问题的另一个常见来源。允许任意迭代可能会迅速导致模拟循环的性能显着下降。迭代在没有经验的设计师手中可能更加危险。《City of Heroes》的开发过程中,Cryptic Studios 发现许多脚本具有相互依赖性,从而产生难以找到的无限循环。为了防止这种情况,开发者从脚本语言中删除了无界迭代。

虽然这是一个相当极端的解决方案,但大多数游戏在其脚本中不需要任意迭代。脚本只需要对一组有限的对象执行计算;此类脚本遵循受限迭代模式,这显然保证了所有循环的终止。此外,它还可以实现代码分析和编译时代码转换,从而提高性能。例如,SGL 可以采用产生二次方行为的嵌套循环,并从中生成索引结构1;然后,它用执行查找索引的单个循环替换嵌套循环。

受限迭代模式的示例在《魔兽争霸 III》的脚本中随处可见,《魔兽争霸 III》是一款实时策略游戏,必须处理大量个体单位的军队。图 3 中的 NudgeObjectsInRect 脚本出现在 Blizzard.j 文件中。此函数接受一个矩形,并循环遍历该矩形中出现的所有军事单位;在该循环中,它使用函数 NudgeUnitsInRectEnum 来推开单位,以便单位对之间存在最小距离。

此脚本中的所有操作都是软件工程师提供的外部函数。脚本语言不知道这些函数实现了相当于 for-each 循环(对一组固定对象的循环);否则,编译器将能够对其执行循环优化。考虑到此模式在《魔兽争霸 III》脚本中出现的次数,这可能会带来显着的性能改进。

并发模式

迭代不是开发者可以从替代控制结构中受益的唯一情况。许多游戏并行执行脚本,这要求脚本编写者意识到并发问题。例如,考虑在线游戏中的库存管理,这是一个众所周知的有问题的场景,一致性违规会导致对象丢失或重复。考虑以下为将物品放入容器(如麻袋或背包)而编写的简单脚本

// Test a container, and insert an object if okay 
  success = TestPutItem(me, container, item)
  if (!success):
  Bail()
  else:
  PutItemInContainer(item, container)

此脚本测试容器是否有容量容纳物品,如果有空间则添加物品。脚本中没有任何内容表明此操作必须原子执行,因此在分布式或并发设置中,容器可能会在测试时间和将物品添加到容器之间填满。显然,可以通过向脚本语言添加锁或同步原语来消除这种情况。然而,锁可能很昂贵且容易出错,因此游戏开发者希望尽可能避免使用它们。它们在设计师手中尤其危险。

此外,基于锁的同步与状态-效果模式不兼容。在状态-效果模式中,容器的状态由模拟循环的上次迭代结束时的内容组成,而效果属性用于收集添加到容器中的物品。效果变量无法读取,即使使用锁也是如此,因此脚本无法测试同时添加的冲突物品。

与其尝试使用传统的并发方法来解决此问题,不如退后一步,了解程序员在此模式中试图做什么。程序员想要更新一个对象,但在某些条件下,此更新可能会导致不一致的状态。函数 TestPutItem 定义了哪些状态是一致的。如果语言知道这是 PutItemInContainer 的一致性函数,它可以延迟检查以确保一致性,而无需锁。该语言可以首先收集所有要添加到容器中的物品,然后使用一致性检查来放置容器可以容纳的尽可能多的物品。在某些情况下,该语言甚至可以通过单个一致性检查放置多个对象。

当然,这种方法并不能解决并行执行的任意问题,但游戏公司使用的语言几乎没有并发支持,他们依靠编码约定来限制一致性错误。为游戏中更常见的设计模式添加提供并发保证的功能将允许游戏开发者信任他们的脚本编写者编写更多种类的脚本,从而增加他们的艺术自由。

游戏感知运行时

语言特性为运行时提供了关于如何最好地执行代码的线索,但某些游戏具有脚本语言之外的运行时也可以利用的属性。例如,一组脚本的正确优化策略取决于游戏的当前状态。如果游戏正在控制一支庞大的军队向敌人行进,那么游戏应该优化士兵的移动;另一方面,如果军队正在防御攻击,游戏应该优化个人感知。游戏通常只有少量此类高级状态,并且它们之间的变化发生得相对缓慢。如果运行时可以识别游戏所处的状态,它可以切换到优化的执行计划并提高性能。

在某种程度上,游戏开发者已经在他们的性能调整中利用了这一事实。目前,他们记录游戏在游戏测试期间的运行情况,然后在以后对这些日志进行数据挖掘以查找重复出现的模式。如果这些模式易于检测,开发者可以利用它们。然而,这种类型的优化对于设计师或玩家开发用户创建的内容非常困难。理想情况下,游戏感知运行时应该对常见模式有一些了解,并且能够自动进行调整。

性能不是运行时监控游戏随时间变化的唯一原因;它对调试也很有用。调试游戏不像单步执行单个脚本那么简单。每个对象都是单独脚本化的,这些脚本可以以微妙的方式相互交互。一个脚本中的错误数据值可能是完全不同的脚本中的错误造成的。此外,许多错误是用户输入造成的,而用户输入并不总是容易重现。脚本设计师需要某种方式来可视化哪些脚本修改了哪些对象以及这些对象如何随时间变化。这是数据沿袭的应用,数据沿袭是科学计算领域中一个活跃的开发领域。与设计师一样,数据沿袭工具的目标科学家通常编程经验很少;相反,沿袭技术模拟了他们自然而然地思考数据的方式。到目前为止,还没有游戏脚本语言支持数据沿袭。

如果脚本运行时具有不寻常的执行模型,则数据沿袭甚至更重要。在之前将物品放入容器的脚本中,高效执行涉及重新排序脚本的某些部分。与其让程序员在与错误出现时不同的执行模型中调试脚本,不如给他或她提供一个更高层次的可视化,以了解该错误可能如何发生。

游戏感知运行时比语言特性更难实现。语言特性通常可以逐步实现;随着编程模式被识别出来,可以添加新的语言特性,而不会对旧的特性产生不利影响。运行时一旦架构化,就可能非常相互依赖且难以更改。例如,对操作处理顺序的任何更改都会影响调试器。因此,虽然语言可以采取“看看什么有效”的态度,但运行时需要从一开始就得到很好的理解。

结论

脚本语言是游戏开发和模组的组成部分,它们的设计对最终游戏的正确性和性能都有巨大的影响。游戏开发者从他们发布的游戏中赚钱,而不是他们解决的工程问题。因此,任何可以减少开发者技术挑战并允许他们创建更多内容的东西都是值得欢迎的创新。设计模式和脚本语言的进步将影响未来几年游戏的编程方式。问

参考文献

  1. White, W., Sowell, B., Gehrke, J., Demers, A. 2008. 用于计算机游戏的声明式处理。载于 2008 SIGGRAPH Sandbox Symposium 会议论文集。http://doi.acm.org/10.1145/1401843.1401847
  2. Dean, J., Ghemawat, S. 2008. MapReduce:大型集群上的简化数据处理。《 通讯》51(1): 107-113。 http://doi.acm.org/10.1145/1327452.1327492

WALKER WHITE 是康奈尔大学游戏设计倡议的负责人,该倡议是一个跨学科本科生课程,旨在培养学生计算机游戏的设计和开发能力。他一直积极与游戏公司合作,以确定如何更好地将数据库技术与计算机游戏集成。他目前的研究兴趣包括数据驱动的设计和计算机游戏的数据管理。

CHRISTOPH KOCH 是康奈尔大学计算机科学副教授。他对数据管理的理论和系统导向方面都感兴趣,目前致力于管理不确定数据、社区数据管理系统、数据驱动的游戏以及 Web 信息提取和管理。JOHANNES GEHRKE 是康奈尔大学计算机科学系副教授。他的研究兴趣领域是数据挖掘、数据隐私、可扩展性以及计算机游戏和虚拟世界。他是本科教材《数据库管理系统》(McGraw Hill,2002 年)的合著者,该教材目前已出第三版。

AL DEMERS 是康奈尔大学计算机科学系首席研究科学家。他在数据复制和普适计算方面做出了开创性的工作,并在这些领域拥有多项专利。他目前的研究兴趣是计算机游戏和虚拟世界的数据管理。

acmqueue

最初发表于 Queue 第 6 卷,第 7 期
数字图书馆 中评论本文





更多相关文章

Jim Waldo - 游戏和虚拟世界中的扩展
我曾经是一名系统程序员,致力于银行、电信公司和其他工程师使用的基础设施。我从事操作系统方面的工作。我从事分布式中间件方面的工作。我从事编程语言方面的工作。我编写工具。我做了硬核系统程序员所做的一切事情。


Mark Callow, Paul Beardow, David Brittain - 大型游戏,小型屏幕
在创建和分发移动 3D 游戏时,立即显而易见的一件事是,手机市场与更传统的游戏市场(例如游戏机和掌上游戏设备)之间存在根本差异。其中最引人注目的是交付平台的数量;设备的严重约束,包括可以更改方向的小屏幕;有限的输入控件;需要处理其他任务;非物理交付机制;以及手机性能和输入能力的变化。


Nick Porcino - 游戏图形:革命之路
从瓷砖块背景上的彩色精灵到现代游戏的沉浸式 3D 环境,这经历了一段漫长的旅程。曾经是单个游戏创作者的工作现在已成为一项多方面的制作,涉及来自各个创意学科的工作人员。下一代游戏机和家用计算机硬件将带来可用计算能力的革命性飞跃;商品硬件将提供每秒万亿次浮点运算(teraflop)或更高的运算能力。


Dean Macri - 可扩展性问题
早在 20 世纪 90 年代中期,我就为一家开发多媒体信息亭演示的公司工作。我们最大的客户是英特尔,我们经常创建在主要计算机零售商(如 CompUSA)终端销售的新 PC 中展示的演示。当时,从商业到消费者的所有应用类别都需要性能。我们创建的演示例如展示了在新型处理器上重新计算电子表格(当时必须手动执行)比在前一年的处理器上快多少。即使是随便的观察者也能立即注意到这些差异——而且这很重要。





© 保留所有权利。

© . All rights reserved.