在五年级的时候,我和我的小伙伴们获准使用一个放置着几台非常破旧的TRS-80s 的闲置教室。为了激励我们,一位老师找到了一份印有一些简单BASIC程序的打印文档给我们。
当时,计算机上的音频磁带驱动器是坏掉的,所以每次我们想要运行一些代码的时候,都不得不仔细地从头开始键入代码。这使得我们更喜欢那些只有几行代码的程序:
10 PRINT "BOBBY IS RADICAL!!!"
20 GOTO 10
即便如此,整个过程还是充满了艰辛。我们不懂得如何编程,所以一个小的语法错误便让我们感到很费解。程序出毛病是家常便饭,而此时我们只能重头再来。
如果计算机打印足够多的次数,或许它会神奇的变成现实哦 。
在这叠文档的最后部分,是一个真正的“怪物”:一个代码量占据几页篇幅的程序。我们思量良久,这才鼓起勇气去尝试它,不过它极为诱人—标题写着“巨魔洞穴”。我们不知道它是做什么的,不过听起来像是个游戏,还有什么能比亲手写一款计算机游戏更酷呢?
我们从没让这个程序真正运行起来过。一年后,我们搬出了那个教室(后来当我了解了一点BASIC时,才知道那只是一个供桌面游戏使用的角色生成器,而并非一款完整的游戏)。命中注定,从那之后,我立志要成为一个游戏程序员。
在我十几岁时,我的家人搞了一台装有QuickBASIC的Macintosh,之后又装了THINKC。我几乎整个暑假都在那上面倒腾游戏。自学是缓慢而痛苦的。我能轻松地让一些代码运行起来(也许是一张屏幕地图或者一个小型猜谜游戏),但随着程序增大,编码变得越来越难。
我的许多夏天都是在路易斯安那州南部的沼泽中捕蛇和乌龟来度过的。如果户外不是那么酷热的话,这将很可能是一本爬虫学的书,而不是讲游戏编程的书。
起初,我的挑战在于让程序运行起来。后来,我开始琢磨如何编写超出我大脑思考范围的更大些的程序。我开始试图寻找一些关于如何组织程序的书籍,而不只是读一些关于“如何用C++编程”之类的书籍。
几年很快过去,一位朋友给了我一本书:《设计模式:可复用面向对象软件的基础》(Design Patterns: Elements of Reusable Object-Oriented Software)。终于来了!这就是我从青少年开始便一直寻找的那本书!我一口气将它一字不漏地读完了。虽然我仍纠结于自己的程序,但是看到别人也如此挣扎并提出了解决方案,也如释重负。原本赤手空拳的我终于有工具可使了。
在2001年,我得到了自己梦寐以求的工作:EA(Electronic Arts)的软件工程师。我迫不及待地想看一下真正的游戏,以及工程师们是如何组织它们的。像Madden Football这样的大型游戏到底是个什么样的架构?不同系统之间是怎么交互的?他们是怎么让一套代码库在不同平台上运行的?
这是我和这位朋友第一次见面,在5分钟自我介绍之后,我坐在他的沙发上,在接下来的几个小时里,我聚精会神地阅读而完全忽视了他。我感觉从那以后自己的社交能力还是至少有那么一丁点儿提升的。
分解阅读源码是一种震撼人心且令人惊奇的体验。图形、人工智能、动画和视觉效果方面,都有十分出众的代码。我们公司有人懂得如何榨取CPU的每一个周期并加以善用。一些我甚至不知道能否实现的东西,这些家伙一个早上就能搞定。
但是这些优秀代码所依托的架构往往是事后想出来的。他们太专注于功能以至于忽视了组织架构。模块之间的耦合现象很普遍,新功能往代码库里见缝插针,而不顾其是否契合。这些所见令我幻想破灭,看起来许多程序员,就算他们心血来潮地翻开过《设计模式》一书,恐怕能看完单例就很不错了。
当然,也不是真的那么糟糕。我曾设想游戏程序员们坐在放满白板的象牙塔中,连续几周冷静地讨论代码架构的细节。实际情况是,我眼前这份代码是别人在紧张的期限里赶工出来的。他们尽了自己最大的努力,同时,我逐渐认识到,他们竭尽全力的结果通常是编写出了十分优秀的代码。我写游戏代码的时间越长,就越能发现隐藏在这些代码之下的可贵之处。
遗憾的是,“隐藏”一词往往说明了问题。宝藏埋在代码深处,而许多人正在它们之上路过(优秀的代码被许多人视而不见)。我看到过同事努力想改造出一个好的解决方案,那时,他们所需要的示例代码就隐藏在他们脚下的代码库之中。
这个问题正是本书力图解决的。我挖掘并打磨出自己在游戏代码中所发现的最好的设计模式,在此一一呈现给大家,以便我们将时间节省下来创造新事物,而不是重新造轮子。
市面上已有的书籍
目前市面已经有数十多本游戏编程的书籍。为什么还要再写一本?
我见过的大多数游戏编程书籍无非两类。
· 关于特定领域的书籍。这些针对性较强的书籍带领你深入地探索游戏开发的一些特定方面。它们会教你3D图形、实时渲染、物理仿真、人工智能或音频处理。这些是众多游戏程序员在自己的职业生涯中所专注的领域。
· 关于整个游戏引擎的书籍。相反,这些图书试图涵盖整个游戏引擎的各个部分。它们的目标是构建一整套适合某个特殊游戏类型的引擎系统,这类通常是3D第一人称射击游戏。
我喜欢这两类书,但我觉得它们仍留下了一些空白。讲特定领域的书很少会谈及你的代码块如何与游戏的其他部分交互。你可能擅长物理和渲染,但是你知道如何优雅地将它们拼合起来吗?
第二类书籍涵盖了这类问题,但我往往发现这类书通常都太过庞大、太过空泛。特别是随着移动和休闲游戏的兴起,我们正处在众多类型的游戏共同发展的时代。我们不再只是照搬Quake 了。当你的游戏不适合这个模型时,这类阐述单一引擎的书籍就不再合适了。
这种分类讲解风格的另外一个例子,就是广受大家喜爱的《游戏编程精粹》系列。
相反,这里我想要做的,更倾向于分门别类。本书的每个章节都是一个独立的思路,你可以将它应用到你的代码里。你也可以针对自己制作的游戏来决定以最恰当的方式将它们进行混搭。
本书和设计模式有什么联系
任何名字中带有“模式”的编程书籍都和经典图书《设计模式:可复用面向对象软件的基础》有所联系。这本书由Erich Gamma、Richard Helm、Ralph Johnson和John Vlissides编著(这4人也称为“Gang of Four”,即本书所提到的“GoF”四人组)。
本书的英文原名是Game Programming Design Patterns,并不是说GoF的书不适用于游戏。恰恰相反,在本书第2篇中介绍了众多来自GoF著作的设计模式,同时强调了在它们游戏开发中的运用。
设计模式一书本身也源自前人的灵感。创造一种模式语言来描述问题的开放性解决方案,该想法来自《A Pattern Language》,由 Christopher Alexander(和Sarah Ishikawa、Murray Silverstein起)完成。
从另一面说,我觉得这本书也适用于非游戏软件。我也可以把这本书命名为《More Design Patterns》,但我认为游戏开发有更多迷人的例子。难道你真的想要阅读的另外一本关于员工记录和银行账户例子的设计模式图书吗?
也就是说,尽管这里介绍的模式在其他软件中也是有用的,但我觉得它们特别适合应对游戏工程中普遍会遇到的挑战,例如:
这是一本关于框架结构的书(就像真正的建筑结构中建筑与墙体和材料之间的关系),作者希望他人能够将其运用作其他领域问题的解决方案。设计模式(Design Patterns)正是GoF在软件领域的一个尝试。
· 时间和顺序往往是一个游戏的架构的核心部分。事情必须依照正确的顺序和正确的时间发生。
· 开发周期被高度压缩。众多程序员必须在不牵涉他人代码、不污染代码库的前提下对一套庞大而错杂的行为体系进行快速的构建与迭代。
· 所有这些行为被定义后,游戏便开始互动。怪物撕咬英雄,药水混合在一起,炸弹炸到敌人和朋友„„诸如此类。这些交互必须很好地进行下去,可不能把代码库给搅成一团毛线球。
· 最后,性能在游戏中至关重要。游戏开发者永远在榨取平台性能这件事上赛跑。多削掉一个CPU周期,你的游戏就有可能从掉帧和差评迈入A级游戏和百万销量的天堂。
如何阅读本书
本书大致分为三大部分。第一篇是介绍和框架。这包括前言和第1章。
第二篇,再探设计模式,回顾了GoF中的一些设计模式。在这个部分的每一章中,我都会试图给出自己对该模式的认识,以及对模式与游戏开发之间关联的看法。
最后部分是这本书的重头戏。这部分呈现了我认为十分有用的13种设计模式。它们分为4篇:序列型模式、行为型模式、解耦型模式和优化型模式。
这些模式使用一致的文本组织结构来讲述,以便你将该书作为参考并能快速找到你所需要的内容。
· 目的部分简单介绍了该模式以及其力图解决的问题。以此作为开篇,以便你能够快速翻阅本书并根据自己眼下的问题对号入座。
· 动机部分描述了一个可引用该模式的示例问题。不同于具体的算法,模式只有运用到具体问题中时方能见其真章。教模式而不举具体例子,就像教烤面包而不提面团一样。这个部分提供“面团”,之后的部分将会教你如何“烘培”。
· 模式部分会提炼出前面示例中的模式本质。如果你想了解该模式枯燥的书面描述,就是这部分了。如果你已经熟悉了该模式,这部分也是一个很好的复习,确保你没有忘记该模式的要素。
· 到目前为止,该模式只是就一个单一的例子来解释的。但你怎么知道该模式是否适用于其他问题呢?使用情境对模式何时使用以及何时不该使用提供了一些指导。使用须知部分会指出使用该模式时带来的后果和风险。
· 如果你也像我一样,需要借助具体的实例才能真正的理解,那么示例部分正满足你的需要。它一步一步地展示这个模式的完整实现,以便你可以看到模式究竟是如何工作的。
· 模式和单一的算法不同,因为模式是开放式的。每次使用模式的时候,你实现的方式有可能会有不同。接下来设计决策部分,会探讨这个问题,并告诉你在应用模式时可供考虑的不同选项。
· 每章以一个短小的参考部分作为结束,它会告诉你该模式和其他模式的关联并指出使用该模式的一些真实的开源代码。
关于示例代码
这本书中的示例代码用C++编写,但是这并不意味着这些模式仅能在C++下发挥作用或者说C++比其他语言要好。几乎所有的语言都适用,虽然有些模式确实倾向于有对象和类的语言。
我选择C++有几个原因。首先,它是现行商业游戏中最流行的语言,是该行业的通用语言。另外,作为C++基石的C语言的语法也是Java、C#、JavaScript和许多其他语言的基础。即使你不懂C++,也没有关系,这里的示例代码基本上是你无需花太多力气就足以能够理解的。
这本书的目的不是教你学习C++。示例会尽可能保持简单,但它可能并不符合优良的C++编码风格或用法。阅读代码时要理解代码所传达的思想,而不是代码本身的表达。
特别一提的是,示例代码没有采用“现代”C++(C++11)或更高版本风格。它没使用标准库并很少使用模板。这是“糟糕”的C++代码,但我仍希望保留这一特色,这样会对那些从C、Objective-C、Java和其他语言转来的读者更加的友好。
为了避免浪费篇幅,你已经看过的或者和模式不相关的代码,有时会
在例子中省略,通常用省略号来表示省去的代码。
例如有一个函数,它完成某项工作并返回一个值。同时讲解的模式只关心返回值,不关心其具体的工作内容。在这种情况下,示例代码看起来会像这样:
何去何从
设计模式是软件开发中一个不断变化和扩展的部分。这本书延续了GoF的文献所开启的过程,并分享他们眼中的那些软件设计模式,而这一进程也不会因本书的完成而就此终止。
你是这个过程的核心之一。只要你开发了你自己的模式或提炼(或者反驳!)这本书中提到的模式,你就是在为软件社区贡献力量。如果你对书中的内容有任何建议、修正或者其他反馈,请与我联系。