📄 g2.htm
字号:
<html>
<head>
<title>游戏制作</title>
<meta http-equiv="Content-Type" content="text/html; charset=gb2312">
<style type="text/css">
<!--
body { font-size: 14px}
td { font-size: 14px}
a:active { text-decoration: none}
a:link { text-decoration: none}
a:visited { color: #FFFFFF; text-decoration: none}
a:hover { text-decoration: underline}
-->
</style>
</head>
<body bgcolor="#e8ffe8">
<br>
<table width="85%" border="0" cellspacing="0" cellpadding="0" align="center">
<tr>
<td>1.2 游戏底层 <br>
在我们编写每一个游戏时,都希望能有一部分代码可以重用,这样我们才能有更多的 <br>
时间做更多的事情。这些重用的代码就慢慢形成了游戏底层。游戏底层多了,自然就形成 <br>
了一些类别。 <br>
显示底层: <br>
用于基本显示的函数。把常用的功能制作成函数,统一出错管理。对于Windows95/98 <br>
下的游戏编程,我们一般使用DirectX作为大部分底层的底层。因为这部分是我们可以用 <br>
于编程的最底层函数。对于二维显示,我们使用的是DirectDraw部分。为什么还需要在 <br>
DirectDraw外面再包一层函数呢?原因有三:第一,DirectDraw提供的函数扩充性很强, <br>
函数参数较多,而我们使用时参数比较固定,包裹成类库后,参数更加可以封装起来;第 <br>
二,DirectDraw提供的函数比较分散,而有许多操作是需要多个函数按照一定顺序执行的 <br>
,包装起来后编写程序比较简单;第三,DirectDraw函数的返回值比较复杂,而且没有对 <br>
错误进行系统的处理,我们需要自己制作一套错误处理函数,对错误代码进行解释。 <br>
显示底层有许多种做法,可以完全自己开发,也可以直接使用别人完成的函数库,比 <br>
如Internet上比较流行的CDX类库就是对DirectDraw进行了很好封装的底层。 <br>
显示底层主要分成下面几个部分: <br>
窗口处理:窗口的创建、管理、删除。 <br>
图形接口处理:图形设备的初始化、退出、显示模式的设定、主显示面、后缓冲区的建 <br>
立。 <br>
二维图形处理:游戏使用的二维图象的装入、显示、剪裁、逻辑操作。 <br>
外围工具:游戏使用的鼠标,刷新率计算和显示,显示内存斟测,面丢失(Surface <br>
Lost)处理(在DirectX 6.0后据说可以不用),错误处理,调试函数。 <br>
关于显示底层还有两个问题需要说明: <br>
第一,显示部分的调试。因为我们所做的游戏一般为独占模式,在游戏运行时不能跟 <br>
踪(除非使用双显示器),所以我们只能另想办法,一个比较常用的办法是使用窗口模式和 <br>
全屏模式切换。不一定在游戏运行中随时切换显示,在调试时事先指定也可以实现该功能 <br>
。 <br>
第二,多媒体播放底层: <br>
播放多种声音,音乐,视频等多媒体效果。比较古老的一种办法是使用Windows 3.1 <br>
就开始使用的MCI调用。它是一种通用接口,使用不同的参数调用同一个函数就可以实现 <br>
基本相同的功能。用MCI可以实现的主要媒体类型有MIDI,WAVE,CDAUDIO,和VIDEO。它 <br>
的优点是使用简单。缺点则是缺乏交互性,而且同种类型的媒体不能同时播放。这样就很 <br>
难实现多种音效的混合。 <br>
一个基于DirectX的调用是使用DirectSound。它是基于WAVE文件的一套播放函数。非 <br>
常适合混音。缺点就是使用较复杂,而且如果想实现分段读取较长的WAVE文件,编程有些 <br>
难度。 <br>
另外一个针对MIDI的DirectX是DirectMusic。它是DirectX系列中的一个新成员。
<br>
对于播放高质量的VIDEO(视频)文件,则有另外一套底层。比如Microsoft的 <br>
ActiveMovie和Intel的Indeo Video。有了它们我们就可以放心地全屏播放AVI文件了。 <br>
对于这些底层,我们所做的只是简单地封装上一层函数,使我们在使用该功能时达到 <br>
最简便。 <br>
但是假如你对这些现成的播放程序不满意,也可以完全自己开发。只要你知道这些文 <br>
件的格式,再把它们存储成你需要的格式,用自己更加优化的算法将它显示出来就可以了 <br>
。用这种方式你可以自己播放AVI VIDEO, MPEG, MPEG2或FLI/FLC。 <br>
<br>
<br>
文件封装底层: <br>
对使用的文本,图象等数据进行封装。我们也常用这种方式把多媒体文件封装起来, <br>
防止别人事先查看。这种工作往往是循序渐进的,我们会根据自己的时间和实力,封装相 <br>
应的部分。比如先封装比较简单的文本,图象。然后是较为复杂的声音文件,最后是最复 <br>
杂的视频和动画文件。 <br>
有的时候封装也是为了压缩。不仅再安装时少占用硬盘,装入时也可以节省时间,有 <br>
时在运行时也会减少内存占用而不会对速度有太大的影响。 <br>
根据文件类型的不同,我们可以这样分类:文本、图象、声音、视频、动画等。对于 <br>
每种类型,我们还必须有打包函数和解包函数之分。 <br>
<br>
界面底层: <br>
自己编制的类Windows风格的界面类库。我总有这样的习惯,希望在我的游戏里没有 <br>
一点Windows的风格,于是我不会使用任何一点的有Windows风格的界面(游戏工具除外)。 <br>
所以游戏的界面必须我们自己做。然而我又使用过MFC那一套界面类库函数,很佩服 <br>
Microsoft的本事。也希望我们的游戏中有一套自己的界面系统。这套系统将会包含 <br>
Windows用到的主要界面元素:位图(Bitmap)、按钮(Button)、检查框(Checkbox)、滚动 <br>
条(Scrollbar)、滑块(Slider)等等。 <br>
虽然Windows允许我们在使用标准界面时自己画图,但是因为它只支持256色,是基于 <br>
消息响应机制的显示,而且无法支持位图的封装,所以我们使用一套完全由自己制作的界 <br>
面底层。这套底层建立在显示底层和文件封装底层之上,形成了一套比较完整的系统。 <br>
<br>
上面这些只是一般游戏所需要的底层,对于某些特殊类型的游戏,可能还会有战略游 <br>
戏的显示,操作底层,RPG游戏的事件处理底层,战棋游戏的显示,智能底层,动作游戏 <br>
的场景底层等。如果编写三维程序,则还会有三维显示和操作底层。这些底层都不需要也 <br>
不可能在一开始就全部完成,而必然是在不断的修改和检验中完善的。它们也不可能一成 <br>
不变,必然随着我们编程经验的积累和开发环境的改变而改变和完善。 <br>
每个编写游戏的程序员都会慢慢积累出符合自己游戏特点的底层函数库,这些函数库 <br>
如果得到不断的发展和扩展将会越来越大,越来越好,这对于每个游戏制作公司和集体来 <br>
讲都是一笔不可忽视的财富。 <br>
而这套函数库越来越复杂,再加上辅助的一整套工具,就可以被称之为游戏的引擎。 <br>
有了引擎在手之后,不仅意味着我们有了一套低Bug的现成代码,程序员的工作量将会大 <br>
幅度下降,而且他们可以把更多的精力放在游戏的优化、游戏的人工智能、游戏的操作、 <br>
以及游戏性上面了。到了这个层次,我才可以想象一个好的游戏将会是什么样子。 <br>
<br>
1.3 编写规则 <br>
现在,只需要一个程序员编写的程序已经很少了,大部分的程序都需要多个程序员的 <br>
协作才能完成。这期间必然需要程序员之间互相阅读代码。而代码的编写规则就是非常重 <br>
要的环节。一段代码如果写的浅显易懂,不但在程序交流时非常方便,而且对于寻找程序 <br>
错误也时很有帮助的。程序员也是人,是人就会犯错误,不管他多么地不愿意犯这个错误 <br>
。所以我们也希望能制定下一个规则,使他们在一开始想犯错误也不行。于是就出现了程 <br>
序的编写规则。 <br>
如果不按照这些规则编写代码编译程序是不会报出错误的。但是如果按照规则编写, <br>
则对我们的思路的整理、笔误的减少、错误的查找和代码的阅读都是极有好处的。 <br>
下面我只把规则中几个重要的部分加以简单的描述,在后面附录中会有一份我们已经 <br>
使用了一段时间的完整规则。 <br>
<br>
命名规则: <br>
程序中对变量和函数的命名看似简单,可是名字起得好与不好有很大的不同。对于变 <br>
量我们一般采用匈牙利命名法和微软命名法的结合。对于函数我们一般要把模块名写在函 <br>
数名的开始。对于类库我们一般要求类的成员和成员函数要紧密对应。 <br>
<br>
变量使用: <br>
对于程序中所使用的所有变量,我们都需要其有明显的定义域。对于全局的变量要尽 <br>
量封装。对于类库,所有成员必须封装。 <br>
<br>
函数使用: <br>
我们对函数的参数和返回值做了要求。对于函数的使用范围也做了规定,要求范围越 <br>
小越好。 <br>
<br>
注释: <br>
对于程序注释,我们虽然不需要很多,但是在函数声明、变量声明、主要算法部分、 <br>
源程序文件的开始都需要有注释。 <br>
<br>
调试: <br>
我们的底层和Visual C++都提供了一些供调试用的代码,在我们的代码中需要适时地 <br>
使用这些代码,以提高程序的纠错机会。 <br>
<br>
这里有几个问题需要特别说明: <br>
第一,关于链表的使用。 <br>
我在编程中所遇到的所有死机错误中,有90%来自于指针,9%来自于字符串(其实也是 <br>
指针的一种),1%来自死循环。而链表这种把指针的优势发挥到极限的方式我是不提倡使 <br>
用的。有人讲如果不会使用链表就不是C程序员,那么现在我连个合格的程序员的资格都 <br>
够不上了。的确,对许多人来讲,编写没有Bug的链表是很容易的事情,甚至对某些人而 <br>
言,一个没有链表存在的程序几乎不能称得上是商业程序。我承认有许多天才的程序员在 <br>
自己的链表程序中表现得十分出色,他的程序效率既高又没有Bug,但是我也知道还有更 <br>
多的程序员并不是天才。就算是天才的程序员如果在他的十万行代码中有五万行于某些链 <br>
表有关,这其中的某个指针出了一个错误,而这个错误又不是每运行一次都出现的时候, <br>
他又有多大的把握很迅速地找到这个错误呢?链表的复杂性在于它的地址的随机性,这使 <br>
我们在调试跟踪时非常困难,我们很难随时观察每一个节点的信息,尤其当节点多的时候 <br>
。所以我把链表的使用完全限制在算法的需要和该代码被某个独立模块完全封装的情况下 <br>
。 <br>
实际上我在设计程序时,要求尽量减少指针在主要模块之间的传递,而使用效率较低 <br>
但是不易出错的句柄ID的方式。 <br>
<br>
第二,关于类库的成员。 <br>
既然我们使用了类库,其中一个主要目的就是要把其中的成员完全封装起来。首先把 <br>
它设置为私有,然后定义标准的一套Set和Get函数。这虽然使我们在写代码时比较烦琐, <br>
但是只有这样我们才可以严格规定该成员的只读属性和可写属性,减少对某些重要变量的 <br>
无意修改。 <br>
<br>
第三,关于变量和操作符之间的间隔 <br>
许多人对于变量和操作符之间的距离并不在意,认为多个空格少个空格只是看起来好 <br>
不好看而已。而实际上,这对我们是很重要的。因为如果我们把间隔固定下来,在查找某 <br>
项操作时就可以使用带空格的字符串,而大大增加找到的机会。 </td>
</tr>
</table>
<p align="center"> </p>
</body>
</html>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -