目录 ZVViu4]?y
YYN=`ST
1 游戏程序理论 {=pf#E=
1.1 技术基础 {~ VgXkjsC
1.2 游戏底层 Drc\$<9c@
1.3 编写规则 iYR8sg[' #
1.4 程序设计 PbCXcs
1.5 制作流程 AfyEFnY
1.6 程序调式 )0YMi!&j`
1.7 代码优化 8MV=?
'xhX\?mD
4k}u`8 a
1 游戏程序理论 *SLv$A
我做游戏的历史只有三年,我所写的内容都只是我在此期间的感觉和经验,还远远谈不上完整和正确,甚至有些内容我们自己也没有完全达到,我只是试图说明我们曾经是怎样做的和在最近的将来打算怎样做。 5s`NR<|2L
我读过一些关于如何制作游戏的文章和书籍,有外国的,也有国内的。有的过于难懂(那些专业技术有的我根本看不懂),有的过于简单,而且大都着重于程序编写技术。我在这里希望能够提供其它方面的帮助,比如游戏中的程序设计,项目管理和与我们中国人编写游戏相关联的一些问题。 m%ak ]rv([
我希望能够给那些想编写商业游戏和正在编写商业游戏的人们提供一个样本,这不是最好的样本,而是在中国内地游戏制作业的初级阶段中一个普通产品的制作方法和经验。如果是一个初学者,我希望给他一个编写游戏程序的完整印象,让他至少了解编游戏和玩游戏是有区别的。 qpFFvZ
W
我希望阅读本文的人应该是一个程序员,熟练使用C语言和C++语言,曾经编写的单个程序长度应该在5000行(150k)以上。因为我在这里不会介绍任何有关编程语言的语法,编译等内容,如果你连这些内容都还没有弄清楚,请先学习一下,否则将很难与本文引起共鸣。假如你是一位美术设计,游戏策划人员或游戏项目管理人员,虽然你无法看懂里面的大部分内容,但是我也希望你能够了解里面的有关内容,因为如果你能够了解程序员的工作,我们就能够更好地配合。 Gw$U0 HA[,
我更希望那些不了解游戏的人们能够看到本文,我希望他们能够了解编写游戏程序与编写其它程序是一样艰巨和复杂的,制作游戏并不等同于玩游戏。 o^biO!4,
0fwo8NgX
1.1 技术基础 T-uI CMEf
5_#wOz0u$
1.1.1 人员 M{1't
作一个商业游戏所需要的程序员的水平,可以分成三个级别:系统分析员、主模块程序员和外围程序员。 ]=7}Y%6
·系统分析员: l\JoWL
这里的系统分析员也许比不上程序员资格考试中的系统分析员,因为我们制作的游戏程序到目前为止都属于中小型程序,长度一般在50000到200000行之间(1.5M到6M),经过压缩之后源代码可以轻松放在一张软盘上。工作量一般在20到60人月之间。所以游戏程序的复杂程度比一些大型应用程序要小。但是,游戏系统分析员的工作也一样是复杂繁重的。他的主要工作是需求分析,程序设计和进度管理。 )FYz*:f>&
在需求分析阶段,主要是根据游戏设计制定的游戏方案确定那些内容是可以实现的,那些是不能实现的;可以实现的部分如何实现,不能实现的部分该如何修改。最后制定一个可行的游戏程序制作方案。这个方案最后被系统分析员写在程序设计文档中。当设计定稿后,制定开发计划,组织人力开发,监督制作成果。 mKfT4t
·主模块程序员:
nz~3o
过去我们的系统分析员和主模块程序员由同一人担任,但是到后来,我们发现,因为程序的管理设计人员也要担任繁重的程序制作任务,很难面面俱到。所以我建议把主模块程序员独立出来。由他来负责对游戏主要运行部分进行编程。这需要主模块程序员具有非常丰富的编程经验,至少应该具有完成一个游戏的经验。但是其实每个人都是从不会到会,从没有经验到经验丰富的,不能满足条件也没有关系,只要程序难度小一些就可以了。 =T!iM2
eE+zL~CE
·外围程序员: :k#Y|(
游戏的程序一般都会被分割程序若干个模块,如果该模块独立性比较大就可以交给其他人员完成。这些人只要有一定编程经验就可以胜任了。 ,a_\o&V
5e6 f)[}
1.1.2 编程平台 ,_D`0B6o
任何的计算机语言都可以被用来写游戏。游戏可以被运行在任何可能的计算机硬件设备上。但是,因为我的制作经验的局限,我无法向你们介绍用Cobal语言编写AppleII上运行的游戏。同样,我也不会教各位如何使用Visual Basic,Pascal或Java,任何形式的DOS程序也是我不会花时间讲解的。在我后面的讨论中将只涉及在windows95/98下的Visual C++编程。 b~z1%?
现在,编写游戏时不能不提到Microsoft公司的DirectX。它是一套基于Windows95/NT的游戏底层接口,在我们的游戏中使用了它作为底层接口。关于它的使用想必大家已经很熟悉了,现在已经有中文的书籍出版,我不必赘述。 n'K,*
有的书上讲在编译和调试程序时最好使用Windows NT或双显示器,尤其因为DirectDraw中独占显示器。可是我试过之后发现都不实用。对于WindowsNT,它的4.0版只支持DirectX 3.0, 听说5.0版才支持DirectX 6.0,但我总不能等到NT5.0出了之后才开始编吧。对于双显示器,我一直耿耿于怀,外国人都很阔,每个人可以配备两台机器,可是,就算我找到两台计算机,程序装入的速度实在让我不耐烦,调试一遍时间太长,等不起。 YOqGFi~`
我们应该重视的倒应该是计算机的兼容性问题。为我们提供测试的计算机是很少的,如何找到更多的条件才测试我们的程序是后期应该注意的问题,千万不要以为在Windows95时代计算机硬件的差别已经没有了,它们出起错来更危险。 P_
U[OM\
!SMIb(~[z
1.1.3 语言 4,`Yx s)%
在任何一本写游戏编程的外文书籍里都会提到编程语言主要有两种,汇编和C。有的书里甚至不提倡使用C++。不使用C++的原因我猜有两个:第一,速度。据说C++代码比C代码慢10%。这个数字我没有测试过,但C++比C慢大概应该是对的。因为用C++比直接用C编写代码要方便和简单,所以速度应该会慢。第二,便于移植,移植这个词可很时髦,什么PC到PS啦,PS到Saturn啦,Saturn到N64啦,N64到Mac啦,Mac到PC啦之类,一个游戏必须要在所有地方出现才过瘾。如果使用C++就难了,因为象次世代游戏机上的开发环境,一般不支持C++,N64虽然说是支持但仍然建议使用C。 vm_+U*%c
使用汇编的理由也是很简单的,那就是快!因为游戏编程中表现最明显的地方就是游戏的速度。汇编比C快的道理也是很明显的,那就是汇编要比C难写多了,速度自然快些。 k7'_
"l"zbW WOH
那么速度就那么重要么?过去上学的时候,用汇编写出来的程序编译出来的一般十几k,后来用C写的小程序大的有几十k,而现在编译windows程序,一上来就有一百多k。过去的游戏就是绿色屏幕上几个字符,而现在都是真彩色的图象和实时三维。过去的游戏一个人就可以“搞定”,而现在必须分工合作。这些给我的感觉就是游戏越做越大,程序越写越长。是不是有一些方面开始变得与游戏速度一样甚至更加重要了呢? De6WC*trq
有!这其实就是大家经常谈论到的一个问题:程序的Bug。一个程序如果越来越复杂,越来越长,而且多个人共同编写,这时候就越容易出现程序中的臭虫。我认为,以我们目前的程序编写水平而言,程序的正确性要远远高于它的效率。实际上,我在后面的主要篇幅都在讲如何提高程序的正确性上,而只有很少的篇幅介绍程序的优化。 qn5e[Vn
所以我选择编程语言主要是从其正确性和效率两个方面来考虑的。目前我选择C++。 D<$,v(-
g/)mbL>=
这不仅仅因为我对这个语言最熟悉,更因为它的特点,对两个方面都符合得很好。程序是人写出来的,如果程序越容易写,就越容易正确,这方面汇编语言显然不是候选者,Pascal虽然好,但是那些Begin和End写起来也太累。 C语言要省事得多,C++更进一步,而且有了封装,继承,多态的概念。可是编写最方便的首选应该是Visual Basic,Delphi和Java了。程序的框架都已经被准备好,只要在里面填东西就可以了。只是因为它们的效率比较低,而不能成为游戏编写的最佳语言。 fq48>"g*
现在很难想象一个游戏整个都是用汇编写的,因为一个10000行的C程序改用汇编写至少需要50000行,那其中出错的机会至少要多出5倍,而且汇编代码太过抽象,很难读懂。所以应该只在非常需要效率的地方才需要使用汇编语言。 o+r?N5
C语言与C++的差别不大,而且我认为C++所带来的封装的概念足以弥补它们的速度差异。关于移植,有消息说Sega最新一代的游戏机DreamCast要使用Windows CE作为操作系统,那么C++应该也会支持的了。大融合的时代总会到来,不同担心。我的C语言代码也主要用于程序的底层。 r8A
那么,现在大家对我所使用的语言应该有所了解了。使用C++作为主要语言,同时辅助以C和汇编。 AQw1,tGV
(Z fY/
1.1.4 编译环境 }.>( [\q
Microsoft Visual C++2.0/4.0/5.0/6.0是我一直使用的编译器。虽然Borland C++和Watcom C++也可以编译Windows程序,我仍然认为Visual C++是最好的。没有别的理由,就是因为习惯了。如果你已经习惯使用Watcom C或Free C什么的,也没必要改。如果从来没有编过Windows程序,倒是可以先从这里开始。 @2na r<
我听说谁如果不会使用SoftICE调试程序就没有达到编程的顶峰,我一直以此为遗憾。我不会使用SoftICE。我也远远没有达到编程的顶峰。“竟然这样的人也编起游戏来了”,也许有的人会这样想,我想这没关系,因为本来谁都可以编游戏的嘛。 g ]e^;
c_"]AhV~Mg
1.1.5 背景知识 9LI#&\lba
编程序到底需不需要是专业人才,这也是个大家争论的话题。我还是那个观点,这只是一个起点的问题,它只表明入门时候的速度而已。但这并不是说你什么都不需要学。有许多东西需要我们学,甚至需要我们不停地学。 S-NKT(H)c
·硬件知识: s3Pr$h
你总不能连自己的计算机是什么CPU都不知道吧。虽然我们不需要知道每个芯片的内部构造,但如果我们知道了Pentium CPU有两条并行流水线同时处理指令的话,我们就会特别注意这方面的优化了。 ?Id3#+-O
·操作系统: HZX(kYV
我们的程序不一定需要多任务,多线程,但是了解我们的程序何时被调用是很重要的。 Kc$j<MRtv
·数学: kj{z;5-dl
数学是我最头痛的科目了。好在我们在这里所接触的大多是离散数学(我也很头痛!)。最基本的要算是数理逻辑,就是程序中最普通的判断语句中的那些“与”,“或”,“非”。其次是那些图论,概率。知道这些编写二维游戏就足够了。如果编写三维游戏,那就赶快复习一下线性代数吧。 -W38#_y/\
·专业课: omevF>b;
这是计算机系中学到的有关课程。虽然数据库,网络的概念在游戏编写中都可能会用到,但是因为都是间接的,就不在这里说了。数据结构大概是最重要的课程了,这里所提到的数组,队列,堆栈,链表,表,树,图等概念在我们的程序中几乎都会或多或少地用到。另外,对于三维编程,还有一门叫计算机图形学,也是很重要的。 MqDz cB]
·文件: *<c, x8\s9
与游戏密切相关的文件有图象文件,声音文件,视频文件等。这些知识越丰富对我们编写游戏就越有用。可是,这也并不是绝对的,我在刚开始写游戏时连BMP图的格式都不知道。 0Ihp`QGU:
·美术: [+\=x[q
大概每个游戏程序员都有过当“助美”(助理美工)的体验吧。当我们做程序测试,或美术师们没有时间完成裁图,切图等“体力”劳动时,自然由我们来做这些工作。但是,请记住,程序员永远是程序员,就算我们画得再好,也比不过他们的。 G>&Ta p>
9)9p<(b$
1.2 游戏底层 R*|y:T,H
在我们编写每一个游戏时,都希望能有一部分代码可以重用,这样我们才能有更多的时间做更多的事情。这些重用的代码就慢慢形成了游戏底层。游戏底层多了,自然就形成了一些类别。 q$L=G
显示底层: >x]b"@Hkw
用于基本显示的函数。把常用的功能制作成函数,统一出错管理。对于Windows95/98下的游戏编程,我们一般使用DirectX作为大部分底层的底层。因为这部分是我们可以用于编程的最底层函数。对于二维显示,我们使用的是DirectDraw部分。为什么还需要在DirectDraw外面再包一层函数呢?原因有三:第一,DirectDraw提供的函数扩充性很强,函数参数较多,而我们使用时参数比较固定,包裹成类库后,参数更加可以封装起来;第二,DirectDraw提供的函数比较分散,而有许多操作是需要多个函数按照一定顺序执行的,包装起来后编写程序比较简单;第三,DirectDraw函数的返回值比较复杂,而且没有对错误进行系统的处理,我们需要自己制作一套错误处理函数,对错误代码进行解释。 CoO..
显示底层有许多种做法,可以完全自己开发,也可以直接使用别人完成的函数库,比如Internet上比较流行的CDX类库就是对DirectDraw进行了很好封装的底层。 (NR8B9qLN
显示底层主要分成下面几个部分: :m#[V7
窗口处理:窗口的创建、管理、删除。 %
P
.(L
图形接口处理:图形设备的初始化、退出、显示模式的设定、主显示面、后缓冲区的建立。 K%h9'}pq>1
二维图形处理:游戏使用的二维图象的装入、显示、剪裁、逻辑操作。 @~,&E*X! .
外围工具:游戏使用的鼠标,刷新率计算和显示,显示内存斟测,面丢失(Surface Lost)处理(在DirectX 6.0后据说可以不用),错误处理,调试函数。 V3r1|{Z(
关于显示底层还有两个问题需要说明: lI~T>Lel2
第一,显示部分的调试。因为我们所做的游戏一般为独占模式,在游戏运行时不能跟踪(除非使用双显示器),所以我们只能另想办法,一个比较常用的办法是使用窗口模式和全屏模式切换。不一定在游戏运行中随时切换显示,在调试时事先指定也可以实现该功能。 _4Z|O]
第二,多媒体播放底层: jM]B\cvN
播放多种声音,音乐,视频等多媒体效果。比较古老的一种办法是使用Windows 3.1就开始使用的MCI调用。它是一种通用接口,使用不同的参数调用同一个函数就可以实现基本相同的功能。用MCI可以实现的主要媒体类型有MIDI,WAVE,CDAUDIO,和VIDEO。它的优点是使用简单。缺点则是缺乏交互性,而且同种类型的媒体不能同时播放。这样就很难实现多种音效的混合。 h8B:}_Cu
一个基于DirectX的调用是使用DirectSound。它是基于WAVE文件的一套播放函数。非常适合混音。缺点就是使用较复杂,而且如果想实现分段读取较长的WAVE文件,编程有些难度。 _IYd^c
另外一个针对MIDI的DirectX是DirectMusic。它是DirectX系列中的一个新成员。 wFI2(cQ
对于播放高质量的VIDEO(视频)文件,则有另外一套底层。比如Microsoft的ActiveMovie和Intel的Indeo Video。有了它们我们就可以放心地全屏播放AVI文件了。 }tJRBb
对于这些底层,我们所做的只是简单地封装上一层函数,使我们在使用该功能时达到最简便。 n,/eT,48`
但是假如你对这些现成的播放程序不满意,也可以完全自己开发。只要你知道这些文件的格式,再把它们存储成你需要的格式,用自己更加优化的算法将它显示出来就可以了。用这种方式你可以自己播放AVI VIDEO, MPEG, MPEG2或FLI/FLC。 }-jS0{i
Xo[j*<=0
文件封装底层: DLggR3K_\
对使用的文本,图象等数据进行封装。我们也常用这种方式把多媒体文件封装起来,防止别人事先查看。这种工作往往是循序渐进的,我们会根据自己的时间和实力,封装相应的部分。比如先封装比较简单的文本,图象。然后是较为复杂的声音文件,最后是最复杂的视频和动画文件。 .
7*k}@k
有的时候封装也是为了压缩。不仅再安装时少占用硬盘,装入时也可以节省时间,有时在运行时也会减少内存占用而不会对速度有太大的影响。 .,[NJ:l
根据文件类型的不同,我们可以这样分类:文本、图象、声音、视频、动画等。对于每种类型,我们还必须有打包函数和解包函数之分。 +}1h
&\6Buw_
界面底层: gCfAy=-,V
自己编制的类Windows风格的界面类库。我总有这样的习惯,希望在我的游戏里没有一点Windows的风格,于是我不会使用任何一点的有Windows风格的界面(游戏工具除外)。所以游戏的界面必须我们自己做。然而我又使用过MFC那一套界面类库函数,很佩服Microsoft的本事。也希望我们的游戏中有一套自己的界面系统。这套系统将会包含Windows用到的主要界面元素:位图(Bitmap)、按钮(Button)、检查框(Checkbox)、滚动条(Scrollbar)、滑块(Slider)等等。 5ar2Y$bY
虽然Windows允许我们在使用标准界面时自己画图,但是因为它只支持256色,是基于消息响应机制的显示,而且无法支持位图的封装,所以我们使用一套完全由自己制作的界面底层。这套底层建立在显示底层和文件封装底层之上,形成了一套比较完整的系统。 Qf|x]x*5
!8YZ;l
上面这些只是一般游戏所需要的底层,对于某些特殊类型的游戏,可能还会有战略游戏的显示,操作底层,RPG游戏的事件处理底层,战棋游戏的显示,智能底层,动作游戏的场景底层等。如果编写三维程序,则还会有三维显示和操作底层。这些底层都不需要也不可能在一开始就全部完成,而必然是在不断的修改和检验中完善的。它们也不可能一成不变,必然随着我们编程经验的积累和开发环境的改变而改变和完善。 k@:M#?(F
每个编写游戏的程序员都会慢慢积累出符合自己游戏特点的底层函数库,这些函数库如果得到不断的发展和扩展将会越来越大,越来越好,这对于每个游戏制作公司和集体来讲都是一笔不可忽视的财富。 Bu_/yKW
而这套函数库越来越复杂,再加上辅助的一整套工具,就可以被称之为游戏的引擎。有了引擎在手之后,不仅意味着我们有了一套低Bug的现成代码,程序员的工作量将会大幅度下降,而且他们可以把更多的精力放在游戏的优化、游戏的人工智能、游戏的操作、以及游戏性上面了。到了这个层次,我才可以想象一个好的游戏将会是什么样子。 Ya~*e;CW2
M~/7thP{
1.3 编写规则 R<(kiD\?]
现在,只需要一个程序员编写的程序已经很少了,大部分的程序都需要多个程序员的协作才能完成。这期间必然需要程序员之间互相阅读代码。而代码的编写规则就是非常重要的环节。一段代码如果写的浅显易懂,不但在程序交流时非常方便,而且对于寻找程序错误也时很有帮助的。程序员也是人,是人就会犯错误,不管他多么地不愿意犯这个错误。所以我们也希望能制定下一个规则,使他们在一开始想犯错误也不行。于是就出现了程序的编写规则。 {;mT.[
如果不按照这些规则编写代码编译程序是不会报出错误的。但是如果按照规则编写,则对我们的思路的整理、笔误的减少、错误的查找和代码的阅读都是极有好处的。 t7#lRp&
下面我只把规则中几个重要的部分加以简单的描述,在后面附录中会有一份我们已经使用了一段时间的完整规则。 R. :~e
$.HZz
命名规则: ^#i3JMq
程序中对变量和函数的命名看似简单,可是名字起得好与不好有很大的不同。对于变量我们一般采用匈牙利命名法和微软命名法的结合。对于函数我们一般要把模块名写在函数名的开始。对于类库我们一般要求类的成员和成员函数要紧密对应。 9lXjB_wG>
W;L<zFFbU)
变量使用: d?[gd(O
对于程序中所使用的所有变量,我们都需要其有明显的定义域。对于全局的变量要尽量封装。对于类库,所有成员必须封装。 0#Ivo<V
^EtBo7^t
函数使用: v<0\+}T1R
我们对函数的参数和返回值做了要求。对于函数的使用范围也做了规定,要求范围越小越好。 5>CmWMQ
C.]\ 4e
注释: 4gD;X NrV
对于程序注释,我们虽然不需要很多,但是在函数声明、变量声明、主要算法部分、源程序文件的开始都需要有注释。 zy8Z68%E`*
Dnk}
调试: E3hql3=
我们的底层和Visual C++都提供了一些供调试用的代码,在我们的代码中需要适时地使用这些代码,以提高程序的纠错机会。 *ay&&S*
&k53*Wo
这里有几个问题需要特别说明: Bk)E]Fk|
第一,关于链表的使用。 a9LK}xc={
我在编程中所遇到的所有死机错误中,有90%来自于指针,9%来自于字符串(其实也是指针的一种),1%来自死循环。而链表这种把指针的优势发挥到极限的方式我是不提倡使用的。有人讲如果不会使用链表就不是C程序员,那么现在我连个合格的程序员的资格都够不上了。的确,对许多人来讲,编写没有Bug的链表是很容易的事情,甚至对某些人而言,一个没有链表存在的程序几乎不能称得上是商业程序。我承认有许多天才的程序员在 =f~8"j
自己的链表程序中表现得十分出色,他的程序效率既高又没有Bug,但是我也知道还有更多的程序员并不是天才。就算是天才的程序员如果在他的十万行代码中有五万行于某些链表有关,这其中的某个指针出了一个错误,而这个错误又不是每运行一次都出现的时候,他又有多大的把握很迅速地找到这个错误呢?链表的复杂性在于它的地址的随机性,这使我们在调试跟踪时非常困难,我们很难随时观察每一个节点的信息,尤其当节点多的时候。所以我把链表的使用完全限制在算法的需要和该代码被某个独立模块完全封装的情况下。 -nK\+bTL}
实际上我在设计程序时,要求尽量减少指针在主要模块之间的传递,而使用效率较低但是不易出错的句柄ID的方式。 lQ&"p+n
\G4L+Q/13
第二,关于类库的成员。 A$ 2 AYQ
既然我们使用了类库,其中一个主要目的就是要把其中的成员完全封装起来。首先把它设置为私有,然后定义标准的一套Set和Get函数。这虽然使我们在写代码时比较烦琐,但是只有这样我们才可以严格规定该成员的只读属性和可写属性,减少对某些重要变量的无意修改。 B|I9Ex~L
Z2P DT
第三,关于变量和操作符之间的间隔 ;@ <E
许多人对于变量和操作符之间的距离并不在意,认为多个空格少个空格只是看起来好不好看而已。而实际上,这对我们是很重要的。因为如果我们把间隔固定下来,在查找某项操作时就可以使用带空格的字符串,而大大增加找到的机会。 &BOq%*+
Df hu
1.4 程序设计 I'h|7y\
我们在编写自己第一个游戏时,往往是凭借自己的兴趣,想到哪就编到哪,有时连自己都没想到会编出这么大的程序来。那时侯我们对代码的修改往往是很随意和彻底的,因为本来都是自己的东西,而且更希望它更好。可是,程序越写越大,有一个问题就会越来越明显,那就是程序的错误。为了减少错误的产生,我们必须采取一些办法,所以也就产生了程序设计。程序设计的必要性还表现在另一个方面,那就是程序员间的合作。为了让每个程序员对我们所要做的事和我们将如何做这件事有个明确的了解,也为了让管理人员能够从量的角度了解任务的完成状况和进度,我们也必须有程序设计。否则我们就只能象一群乌合之众一般,鬼才知道这游戏要做到什么时侯。 Sjb[v
一般,我个人认为,当一个程序需要写到10000行以上时就应该有比较独立的程序设计文档了。可是程序设计文档应该是什么样子呢?很抱歉,我也一直在寻找这个问题的答案。在我刚开始做游戏的时侯,没有人教,于是我翻出上学用的课本,打算来个生搬硬套,写来写去,发现,不行。这样的写法太累了。比如在软件工程课上所讲的数据流图,我好不容易写满了一张八开的纸,马上就发现自己已经糊涂了。就算不马上糊涂,过一段时间我也会认不出那些条条框框是什么了。就算我能记得这些图表的意义,我能够保证所写的代码和图表上的一样么?我写完后,还需要和这些表对一下。就算程序和文档中的内容一样,那如果我的设计方案改了呢?又需要先改文档,再改程序,再对程序。这样对我来讲可太累了。 vC#_PI
后来,我才发现,原来书上所写确实有道理,可是它并不是面向我们这些人的,它所面向的是写大型程序的人。这些程序大到需要专门有这样的人来写文档。这些人只要把文档写好再确认完成的程序是否符合要求就可以了,根本不用去写一句的代码。是啊,如果编游戏也有这样的工作就好了。可是很遗憾,目前我尚未遇到可以聘有专职程序设计人员的公司。所以程序设计者就是程序编写者的这个事实起码在我们的目前阶段是很难避免的了。 fl@=h[g#t
那么怎么设计文档对我们用处又大又比较节省时间呢?每个公司和每位程序设计人员的习惯都不一样,我只介绍一下我们曾经使用过的一些方法和文档的类型。 x)}.@\&%
)\aCeY8o
程序设计总纲: ce56$L8[
无论我们做什么,最先要清楚的一个问题就是:我们做什么?很弱智是不是?但是我发现有些人在很多时侯都“不知道”自己在做什么。程序设计总纲就是要告诉自己和自己的伙伴们,我们要做的是什么样的游戏程序。它包括: W0-KFo.'
游戏概述:游戏的背景、类型、操作方法、特点等; 1 sJtkge:
程序概述:游戏程序的编译平台、硬件运行需求、语言、编程重点和难点、技术能力分析等; v[l={am{/
程序员概述:参与编程的人的能力和在整个程序制做中的作用和地位; meF.`fh
程序模块划分概述:有本游戏所需要的所有程序,和程序内部的功能模块的划分。这部分只要有比较概括的说明就可以了。 ,]Gi942
这篇文档的作用就是让所有人都能够了解本游戏的程序将会是什么样子,它的特点是什么,完成它的难点又是什么。以及我们为什么要这样做,有谁来做等这样基本的问题。它所面向的对象除了有自己和本组内的所有程序员以外,还应该有本游戏的游戏策划,美术设计、项目负责人、项目经理、制片人等。如果有可能也应该和本公司的市场、销售、广告公关、出版、甚至公司总经理充分接触,听取各个方面的意见,也充分表达自己的意见。如果能做到制做本游戏项目的所有人员从上到下对本游戏的程序制做都能有一个基本的了解,理解和信心那是最好的。 };{Qx
Th.Mn}1%L
程序模块划分: RKi11z
这是对程序设计总纲中最后一个部分的详细说明。说明的内容主要有:该模块的功能、接口、技术要点、所用到的软件底层等。它所面向的对象一般是所有的程序员,但是如果让与程序合作的人员(美术、策划、经理)也能了解则更好,这样可以加强我们之间的勾通,让不懂编程的人也能了解编程人员的苦衷。
eeMeV>
一般而言,一个游戏按功能划分(我们很少用其它的分类方法)可以有下面几个部分: sOVbz2\yb
;15j\{r
工具部分:在游戏的制做过程之中,从游戏策划的文档,游戏美工的图片到游戏完成,中间必须经过游戏程序的转换和加工。这些数据在进入程序之前必须经过规整,排序等操作。这部分工作过去一般都是由程序员来做的。在人手紧张和工作量比较小的时侯这样做还是可行的。但是如果这样的工作过于复杂,工作量太大,就不行了,我们需要一种方法来提高工作效率。这种方法就是工具。其实我们在计算机所做的任何工作都是在使用工具,唯一的区别只是这些工具都不是我们自己做的。因为游戏本身的特殊性,我们不可能总能找到我们所需要的工具,自己制做就是十分必要的工作。 TKH!,Ow9A
这些工具也可以分成三个级别:别人的工具,我们自己的可以重用的工具,我们自己的只在本游戏使用的工具。别人的工具是最好的工具。原因很简单,因为它不用我们再去编了,不仅程序Bug较少,操作界面友好和简单,而且开发时间是零。所以如果可能一定要使用别人做好的工具。如果实在没有满足要求的工具,或者我们还需要该工具所没有的功能,我们只能自己编。那么如果这个工具可以被以后用到也不错。所以我们在编写任何工具的时侯,其通用性是我们考虑的首要因素。 %>io$ o
工具与游戏其它部分的程序有一个最大的区别,那就是工具程序是独立的可执行文件,它与游戏的唯一交互方式就是存在磁盘上的文件。所以它比较适合交给其他的程序员制做。另外一个特点就是它一般都是针对非程序员的,所以如果工具做好了,也可以很大程度上帮助他们提高工作效率。 npCiqO
,vcg%~-
底层部分:这部分程序代码的最大特点是可重用性。它是游戏本身与计算机通用系统之间的桥梁,它与游戏的具体细节关系并不大,可以被使用到多种游戏类型和多个游戏之上。这对我们是非常重要的。实际上,我们编写程序所积累的最直接的财富就是可重用的工具和游戏底层。 Q$bi:EyJXc
游戏底层从与游戏的相关性可以有大概三个层次: 1`& Yg(
最底层:它们向下直接调用系统函数和功能,向上与游戏几乎完全无关,是游戏程序与计算机系统的直接桥梁。它包括: Sgp1p}
·显示底层:提供游戏显示所需要的所有函数; tRZA`&
·文件底层:提供对游戏所需的各种文件处理的接口; fvE:'( #?
·多媒体底层:提供游戏所需的多媒体效果,如声音,视频等; /`kM0=MMa
·对于三维游戏有模型管理底层:专门保存,处理和显示三维数据; <Jc
:a?ICe
中间层:它们向下调用最底层的游戏底层,向上为游戏提供函数。与游戏无关。它可能: 18eB\4NlD
·界面底层:使用显示和文件底层,提供界面类库和操作函数; 9B)<7JJX!J
最高层:它们向下调用中间层和最底层,向上为某种类型的游戏提供特殊的逻辑支持。它可能有: 0 k(su
·战略游戏的命令执行底层,显示底层,地图底层。 7jS`4,
·RPG游戏的事件处理底层,场景底层。 HuI?kLfj\
UwtL vd
这三个层次的制做难度是越来越难的。它往往需要我们在制作过几个游戏之后提炼和总结出来(实际上我们到现在也还没有发展出第三个层次的底层)。但同时,这三个层次是越来越重要的,因为它距离一个成品游戏更进一步。我们在写游戏时可重用的部分越多,重用的层次越高,我们的工作量就越少。我们也就越可能有时间研究和开发新的技术,好的算法,我们的游戏也就会越做越精。 5mqwNAv
相比之下,前面的两个底层都与计算机系统有关,与游戏本身无关;第三个底层与计算机系统无关,与某种类型的游戏有关。 N+N98~Y`P
底层部分与工具部分也是密切相关的。一般每一套底层都需要一些工具来与之对应。比如,我的显示底层需要某种格式的图片,我们就要制做一个格式转换和图片观察的工具。把工具和底层结合起来分类,然后把它较给独立的程序员去完成,是一种比较常见的模块划分方法。 Dve+ #H6N
当然,我们在做游戏以前也不一定非要什么工具和底层不可,这些东西完全都可以“自然”产生。假如你能够坚持制做几个游戏,把可以反复使用的程序归纳起来,修改一下,就是一套底层了。如果我们在编写程序时,每时每刻都在想:这段代码是否可以在以后使用,怎样的写法可以使我在以后使用,那么我们在后来的修改工作中就会轻松很多。而假如我们在编写程序以前多花一些时间,把想要做的东西设计清楚,在一开始就可以把它写成独立的底层代码。这样积少成多,我们的财富就会越来越丰富。 "L9yG:
aam6R/4
游戏本身:这才是我们每次都要做的游戏。所有体现游戏特色的内容全在这里。当然也有的人认为如果我们的经验足够丰富,想得足够全面,连这部分的程序也可以重用起来。以后我们编游戏只要改一下图片,换几个名字,编一段故事,一个新游戏就产生了。实事上,也有公司制做了一些类似游戏工具的东西,其中做得最好的应该是象Director、Autherware那样的多媒体编辑工具吧。但是,它们都有其局限。最明显的一点就是:它们必须针对某一个类型的游戏或只能是多媒体程序,不同类型游戏的制做是有很大区别的,几乎很难用一个固定的模式将其完全函盖。其次,它们创新困难,现在游戏几乎成了创新的代名词,哪一个游戏做出来没有它自己的特点呢?而使用一个固定的工具是很难做到这一点的。第三,难以对它们进行优化。程序的优化往往和程序本身的特点有关,优化的程度越高,它的特殊性也会越大,所以,通用工具只能牺牲一些性能。我到目前还没有看到一个游戏制做工具可以达到商业效果,而多媒体工具之所以流行正因为制作多媒体产品所需的模式比较单一,对速度要求也不高。 kVRh/<s
所以,制作一个完全可以通用的游戏工具到目前仍然还只是一个梦想。但是,我想这是一个很美的梦,如果真的到那个时侯,我们制做游戏将会和拍电影一样简单(我指的只是制做的过程),只需要一个程序员根据策划的意思把图片和声音放在一起就可以了。 Ht,+KbB
每个游戏都是不一样的,但它们都有一些相似的地方,比如: b'O>qQ
游戏的操作:游戏者如何操作和控制游戏中的一个或多个主角; \cx==[&(
游戏的显示:不是游戏的底层,而是与具体游戏有关的显示部分; <*Bk.>f!
游戏的运动:游戏中有的内容是要不断变化的,包括位置变化,图片变化和形式变化; QKHAN{hJ
游戏的智能;能够与人对抗的思考; 1F,>siuh ,
游戏的存储;把所有当前游戏需要的数据保存在磁盘上,然后再重新装入游戏; Z1dLC'/b]
游戏状态的转换:从界面到游戏,从游戏到动画,在不同的时侯有不同的操作和显示管理。 VN/v]
我们通常也会按照这样的规律对游戏进行模块划分。 huat,zLS
%G`GdG}T
游戏数据结构和算法: ^'G,sZ6'Nh
有人讲程序就等于数据结构加算法。这句话很有道理。我们所编的程序其实就是把某种格式的数据(图片,主角的参数)经过一系列的转换,成为计算机屏幕上的数据(游戏本身)。所有的数据都需要以某种方式存储,这就是数据结构。而算法因为是与数据结构密切相关的,所以虽然它们不属于同一个概念,我把它们放在一起设计。 Vi*HG &DD
我们通常所使用的数据一般可以分成两个部分:数据库结构和当前结构。 (3VV(18
这里的数据库不是什么商业数据库软件,而是游戏中所使用到的所有数据的集合。我还没有见到有哪个游戏在存储这些数据时使用商业数据库软件(比如FoxBase)。也许它们都被隐藏起来了吧,我猜想。但不管怎样,其意义是一样的,我们需要把所有在游戏中用到的数据都以一定的形式储存起来。同商业数据库一样,数据库的组成也是由字段组成,即数据元素。每个数据元素一般是一个类库或结构,有若干的成员。数据元素所组成的数组就是数据库结构中最主要的组成部分。 =O
o4O CF2
数据库的内容很多,而且按照一定的规则排列,而真正需要显示和当前需要计算的数据却很少,有些数据还是计算的中间结果,比如主角的位置,主角的动作等,这时侯就需要我们另外存储一些数据。它往往是数据库中的某个字段(数据元素),或一些数据的组合。 7[I%UP
如果把数据结构按照功能分类,我们还可以把它分成主数据结构,模块数据结构。它们的定义域是不一样的。 '$0~PH&
在我们的游戏中,会有许多数据结构的类库,它们之间也必然有数据结构之间的数据传递和保存。如何处理好这些数据之间的关系是编写这部分内容最重要的考虑。 Jfs_9g5
首先是数据结构定义域的问题。在无数的教学课本上都在不断提醒大家,要尽量避免使用全局量。为什么要这样做呢?第一个原因显然是因为这样做的人太多了,我们使用全局量直接,方便,快捷,有什么理由不用呢?而答案也很简单,因为它暗含了错误,因为它不易于阅读和修改。于是我们用两个规则来限止全局量的使用:第一,尽量使用局部全局量,使该全局量的定义域最小,比如基于源程序文件的全局量,只有整个游戏的核心结构是全局范围内有效。第二,对于所有的全局量都对其内容进行全面的封装,如果是类库则使用成员函数,如果是结构,则定义专门的函数对其内容进行处理。 dd-`/A@
其次是数据的传递。我们经常遇到数据结构之间的互相调用和嵌套,很多人都喜欢使用指针(又是指针)做这项工作,因为它象全局量一样:直接、方便、快捷。但是我因为个人水平的问题在使用指针时总遇到一些不便,比如,易于出错,而且一旦出错就是很严重的当机,程序非法退出。要知道现在每启动一次计算机仍然是很耗费时间的。还有另外一个问题,那就是指针不能存盘,我必须另外制作一套程序对结构的存盘做特殊处理。所以我一般使用索引ID号来代替指针。具体的使用是这样的:数据库结构是以数据元素为单位的数组,数据元素的索引(ID)就是它在数据库数组中的下标。所有其它结构对该数据元素的保存都只保存了该元素的ID号。指针只在从数据库根据索引得到元素时使用。这样的好处很明显,ID号是整型数据,可以通过该值的范围判断出是否有效,只要在其有效范围内,再通过指针取值肯定是有效的,几乎跟本不会出现非法指针的现象。 !Y,*Zc$R
&