大数据

游戏编程设计模式 — 引言

Game Programming Patterns — Introduction

原文地址:http://gameprogrammingpatterns.com/introduction.html

原创翻译,转载请注明出处

五年级的时候,我的朋友们和我被允许进入一间存放着几台非常破旧的TRS-80s电脑的闲置小教室。抱着启发我们的想法,一位老师给我们找来了一些印有简单BASIC程序的打印件让我们对照着来操作。

因为这些电脑的盒式磁带驱动器已经坏了,所以每次我们想要运行一些代码的时候,我们都要从头开始一点点小心地输入这些代码。这让我们更钟爱那些只有几行长度的程序:

10 PRINT "BOBBY IS RADICAL!!!"
20 GOTO 10

可能如果电脑把这些话打印足够次数的话,它们就会神奇地成为现实

即使是这样,这整个过程也是充满着危险的。我们并不懂得如何去编程,所以一个小小的语法错误会困扰我们很久。当程序无法工作的时候(这是经常发生的),我们都会从这段程序代码的开头重新开始。

这叠纸的后面几张是一个真正的怪物:一个代码占据了满满几张纸的程序。我们花了一些时间去鼓起勇气来尝试运行这个程序,即使它看起来是这么的诱人–这段代码的标题是《Tunnels and Trolls》(一个古老的幻想RPG游戏)。我们并不知道这个程序是用来干什么的,但是它听起来像是一个游戏,还能有什么比一个你自己编程的电脑游戏更酷的呢?

我们最终没有让它成功地运行起来,一年之后,我们离开了那间教室(很久之后当我真正了解了一点BASIC语言的时候,我才意识到那段程序只是那个桌面游戏的角色生成器而不是一个完整的游戏)。但是木已成舟的是–从那时开始,我有了成为一个游戏程序员的念头。

在我十几岁的时候,家里迎来了一台可以使用QuickBASIC和THINK C的Macintosh电脑。我花费了几乎整个暑假的时间用它来设计编写游戏。自学的过程是非常漫长和痛苦的。我很轻松地完成了一些小程序–比如一个地图菜单或者一个小拼图之类的–但是随着程序规模的增大,想要成功运行就变得越来越难了。

我也花了很多个夏天的时间在南路易斯安那州的湿地里捉蛇和乌龟。要不是因为外面实在是太热了,我很有可能写出一本两栖爬虫动物学的书而不是这本有关于编程的书。

刚开始的时候,我只是尝试着让一些东西能够运行起来。逐渐的,我想要能够完成一些更大的程序。在有了这种念头之后,我在阅读《How to Program in C++》这类的书之外,开始尝试着去寻找一些有关于如何去组织程序的书。

很多年之后,一个朋友给了我一本书:《Design Patterns:Elements of Reusable Object-Oriented Software》(《设计模式:可复用面向对象软件设计》)。终于出现了!这就是那本我从少年时代一直在寻找的书。我迫不及待地把这本书翻阅了一遍。这么多年了,我一直为我的程序代码焦头烂额,让我欣慰的是,通过这本书我发现还有很多其他人也和我一样,并且他们有了一些不错的解决方案。这感觉就像是我终于有了一些趁手的工具,而不是徒手去修缮我的程序。

这是我和那个朋友第一次见面时发生的事情,在相互介绍5分钟之后,我就坐在他的座位上在完全忽略他的情况下花了几个小时去阅读这本书。我想我现在的社交技能至少不会像当时那样那么糟糕了。

2001年,我得到了我梦寐以求的工作:Electronic Arts的软件工程师。这里有很多图形、AI、动画和视觉特效方面的杰出代码。我们也有那种可以压榨出CPU所有性能并将其妥善使用的人才。很多我觉得不可能出现或完成的事情,这些大神们一顿饭的功夫就解决了。

然而,将这些杰出代码组织起来的架构却往往是事后才想起来要完成的东西。大神们太过于专注功能的开发而忽略了对代码的组织。功能模块之间的耦合层出不穷。新功能被随意地插入任何一处让它们可以正常运作的代码库里。这让我很失望,他们这样和很多阅读了《Design Patterns》,却从来没有看完过Singleton(单例模式,一种比较容易理解的设计模式)这一章的程序员没什么两样。

当然,情况并不是真的如此糟糕。我曾经的想象中的游戏程序员们是坐在一些被白墙包围住的象牙塔中,一连好几周平心静气地进行架构细节上的讨论。但残酷的现实则是,这些我所看到的代码都是那些面临着紧张的项目期限的人们所完成的。他们已经尽了他们最大的努力,而且我也逐渐发现,这些代码其实是非常棒的。随着我在游戏编程上工作的时间越来越多,我也从这些代码的表面情况之下发现越来越多的隐藏着的闪光点。

不幸的是,“隐藏”真的是对我们工作中经常出现的情况的一个很好的描述。在这些代码中埋藏着珍宝,然而很多人都只是匆匆略过而没有发现。我经常看到我的同事们为了找到一个好的解决方案而心力交瘁,而其实他们所需要的东西的例子就躺在他们所正在使用的代码库中。

这正是这本书所要去解决的问题。我挖掘出那些我在游戏开发中所曾看到的设计模式并对它们加以修饰,把它们展示在这里,这样我们才能有更多的时间去研究一些新东西而不是重复地发明轮子。

商店里都有些什么

商店里已经有一大堆关于游戏编程的书了。为什么还要再写一本呢?

大多数我所看过的游戏编程书籍可以归类为以下这两类的其中之一:

  • 关于某个特定领域的书籍。这些专注度很高的书会带你深入到游戏开发的某个特定领域中。它们会教你有关于3D图形学、实时渲染、物理模拟、人工智能或者音频处理等方面的知识。这些领域中的每一个,都是很多游戏开发者需要穷极他们整个职业生涯所去专门研究的。
  • 通览游戏引擎的书籍。相对应的,这些书籍会尝试带读者了解一个完整游戏引擎的每个不同的部分。它们的目标是搭建一个适用于某个特定类型游戏的完整引擎,比较常见的是用于3D第一人称射击类游戏的引擎。

这两种书我都很喜欢,但是我觉得在它们之间有一些空白领域。关于某个特定领域的书籍很少告诉你它们的这些代码块如何与游戏中的其他功能相互作用。你可能是一个物理引擎和渲染方面的大神, 但是你知道如何把它们优雅地联系在一起使用吗?

第二个类型的书包括了这些方面,但是我经常发现,这种引擎类的书籍都过于整体化而且过于专注于某个类型的游戏。因为移动设备和休闲类游戏的崛起,我们处在一个很多不同新的类型的游戏被创造出来的时代。我们再也不是都去克隆Quake(一个3D FPS游戏)。当你的游戏并不符合引擎类书籍中的游戏类型时,这些书对你就没有太大的帮助。

与这两类书所不同的是,我想在这本书里所完成的是让读者更有自主选择性的事情(原文:a la carte,意思是照菜单点菜,与之相对的是regular dinner,配好的套餐)。这本书中的每一章都是一个独立的让你可以在你的代码中使用的点子。这样,你就可以在你想完成的游戏中用你觉得最好的方式去组合和搭配他们。

另一一个这种自主选择式的例子是广受欢迎的《Game Programming Gems》(《游戏编程精粹》)系列。

这本书是如何与设计模式相关联的

任何一本书名中有“设计模式”的编程类书籍很明显都会和经典的《Design Patterns:Elements of Reusable Object-Oriented Software》(《设计模式:可复用面向对象软件设计》)有一定的联系,这本经典的书籍是由Erich Gamma、Richard Helm、Ralph Johnson和John Vlissides合作完成的(他们被戏称为“Gang of Four”–GOF、四人帮)。

设计模式这本书本身就是从它之前的一本书中获取到灵感的。这个创造一种用来描述可扩展性问题解决方案的设计模式语言的点子来自于《A Pattern Language》这本书,作者是Chirstopher Alexander(还有Sarah Ishikawa和Murray Silverstein)。
他们的这本书是关于架构的(真实的,有关于建筑、墙、材料之类的架构),但是他们希望其他人可以使用同样的结构去描述其他领域的解决方案。“设计模式”正是GOF借鉴他们的点子在软件开发领域的尝试。

虽然我把本书称为《Game Programming Patterns》(《游戏编程设计模式》),但是我并没有暗示GOF的书不适用于游戏开发的意思。与之相反的是,本书“再探设计模式”一章中涵盖了很多来自于“设计模式”中的经典模式,不过重点要更倾向于如何在游戏编程中去使用它们。

同样,我认为本书也适合非游戏软件的开发。我也可以把这本书叫做《更多的设计模式》,但是我认为游戏有助于提供更多有吸引力的例子。你确定你真的想要去看又一本用员工记录和银行账号来做例子的书?

话虽这么说,即使本书中介绍的设计模式同样也适合于其他类型软件的开发,但是我认为它们尤为适合解决那些在游戏编程过程中遇到的问题:

  • 时间和定序通常是一个游戏架构中最核心的部分。游戏中的所有事情都必须按照正确的顺序在正确的时间发生。
  • 开发周期是非常紧凑的,项目中的程序员需要在有一个丰富的包括多种不同行为的集合的基础上去保证项目的快速构建和迭代,避免相互干扰和代码库中的肆意修改。
  • 在上述有关行为的集合定义完成之后,就是完成交互的工作了。诸如怪物攻击英雄,药水被混合在一起,还有炸弹炸到了敌人和友军之类的事情。这些交互行为的功能必须保证不会把代码库弄成一团乱麻。
  • 最后,性能表现是游戏的关键。游戏开发者们永远处在一场看谁可以压榨出他们开发的游戏所在平台更多性能的比赛之中。一些提升性能的技巧可能会给你带来一款A级评分和上百万销量的游戏,而不是掉帧和玩家充满愤怒的评论。

如何阅读本书

《游戏编程设计模式》被划分为三大部分。第一部分是引言和对本书整体框架的介绍。它包括你正在阅读的这一章以及接下里的一章。

第二部分是再探设计模式,在这一部分里会遍历一些来自GOF书中的设计模式。在其每一章中,都会给出我对这些设计模式的新的见解,以及介绍它们是如何与游戏编程关联起来的。

最后一部分是本书的真正精髓之处。这里给大家呈现了13种我觉得有用的设计模式。它们被分为了4个类别:用于时序的设计模式、用于行为的设计模式、用于解耦的设计模式和用于优化的设计模式。

所有的这些设计模式都使用了统一的结构来描述,所以你可以把本书当成字典一样,快速地找到你所需要的那部分内容。

  • 意图部分对这一章中的设计模式及其所要解决的问题进行了简短描述。这是第一部分,这样可以帮助你们很快地从本书中找到对你们当前所遇到的困难有帮助的那一个设计模式。
  • 动机部分列举了一个我们可以应用这一章中设计模式的示例问题。和实际的算法不同,一个设计模式在没有应用在一个特定的问题之前,一般是没有具体的形式的。所以,介绍一个设计模式而没有相关的例子,就好像教人烘焙却完全没有提到生面团一样。这一部分正是在提供接下来的烘焙部分中所需要使用到的生面团。
  • 模式部分从前一部分的例子中提取出其使用模式的精髓。如果你想要一个对一个模式枯燥的教科书式的描述,那这部分正是你所需要的。这里也是确认你对一个模式是否已经熟悉的很好的复习,以保证你没有忘记一个模式中的某个组成部分。
  • 到这里为止,我们只在一个单独的例子中解释了其模式的使用。但是怎么能让你知道这种模式是不是适用于你所遇到的问题呢?何时使用部分提供了一些何时应该使用一种模式以及何时是应该避免使用它的一些参考。而谨记部分则指出了一些使用一种模式的后果和风险。
  • 如果你和我一样,需要从一些具体的例子中去真正地学到一些东西,那么示例代码部分就很适合你。它带你一步一步遍历一种模式的一个完整实现,这样你就能知道这种模式是如何在实际中产生作用的。
  • 模式和单一算法不同,因为模式是可扩展的。每次你用到一种模式,可能都会得出一种完全不同的实现。下一部分,设计决策,就是探讨这一部分的内容,给你展示在使用一种模式时需要面临的不同选择。
  • 最后是一个简短的参见部分,这里给你展示了一个模式是如何与其他模式相关联的,以及介绍一些现实中使用这种模式的开源代码项目。

关于示例代码

本书中的代码示例是使用C++编写的,但是这并不是说本书中的模式只适用于C++,也不是说C++在使用这些模式时要比其他编程语言更好。几乎任何一种编程语言都可以很好地使用这些模式,虽然可能其中某些模式需要你所使用的语言中有对象和类的概念。

我选择使用C++是有一些原因的。首先,C++是在商业发行的游戏中使用最广泛的语言。它是这个行业的通用语。其次,C++基于的C语言,同样也是Java、C#、JavaScript等其他很多种语言的基础。即使你不懂C++,只需要付出一点点的努力,就可以很好地理解书中代码示例。

本书的目标不是教你学会C++。示例都是尽可能的简单,因而没有很好地使用C++的特性及用法。阅读时需要更多地关注示例中传递的思想,而不是这个示例的代码本身。

还有一点需要注意的是,这些代码没有使用一些“现代”特性–即C++11或者更新版本中的特性。它们没有使用标准库,也几乎使用到模板。这对于C++代码来说是不够好的,但是不使用这些特性正是我所期望的,因为这样对于使用C语言、Objective-C、Java和其他语言的读者来说,要更友好一些。

那些你曾经在前文中见过的代码,或者与当前模式无关的代码,有些时候在示例代码中会被省略。此时会有省略号取代这些代码的位置,提示你这里有代码被省略了。

想象一个需要完成某些工作然后返回一个值的方法。在这个方法里,我们所要解释的模式只关注其返回值,而不考虑工作的完成过程。在这种情况下,这个方法的示例代码将如下所示:

bool update()
{
  // Do work...
  return isDone();
}

下一步

模式是软件开发中一个不断变化和扩展的组成部分。本书继承了从GOF开始的、记录和分享他们所知软件开发模式的优良传统。相信这个传统在本书完成之后,依然会被一直延续下去。

而你们,是延续这个传统的核心。无论你们是研究出属于自己的模式,或是改善(驳斥也可以哦!)本书中所提到的模式,你们都是在对软件开发行业做出自己的贡献。如果你有任何关于本书的建议、修正或者反馈,欢迎随时联系我!


因为水平有限,翻译的文字会有不妥之处,欢迎大家指正