2.2游戏构思 讨论类似Pong这样的游戏的游戏构思听起来有些可笑,不过我会尽量将事情一般化来处理。在您开始编码之前有一件事非常重要,那就是您要仔细考虑您的游戏外观是什么样子,以及玩起来会有什么感受。游戏项目越大,设计过程就越重要。但我不是说如果您没有任何想法就不能写游戏或者应用程序,实际上,很多初学者都是以写东西开始,然后再一步一步地改进直到它看起来非常棒。问题在于,在第一次尝试的时候很难找到一个最好的解决方案。
Pong游戏逻辑很简单,也不需要太多的文字内容或者图像(如图2-1所示),这使得编写代码以及写游戏构思变得很简单。写下该游戏需要的所有组件(比如球、球拍和屏幕边框等)以及您的最初想法。
![图2-1](https://images.cnblogs.com/cnblogs_com/zoujia867/Trans_XNAPro/Figure2-1.jpg)
图2-1
写下您的想法!
写下您的想法并不是说您必须很疯狂地写下所有想法、画UML图,直到所有东西都准备好了才开始工作,我很反对这种方式,因为您不可能把所有东西都想到。而且,初学者一般都不知道以怎样最好的方式预先设计整个游戏。然而,您可以在您的脑海中对您的游戏有一个非常清晰的画面,当您在脑海中花了足够多的时间来设计之后就可以开工了。
如果您的项目中还有其他人和您一起工作,那么解释游戏的构思以及让每个人的工作都保持同步将变得更加困难。仅仅因为这个原因您就得养成一个习惯,只用一页纸写下您的游戏构思。您不能只把这个展示给其他人看,您还得考虑其他的东西——当您把想法写下来的时候您会发现它比您脑海中想的更加复杂,而且您不得不考虑很多相关的东西。
比如,在Pong游戏中您知道您需要两个球拍和一个在球拍之间运动的球。但或许只有当您写下最初的构思以及要使用的纹理素材之后,您才会想到更加详细的游戏玩法——每个玩家有几条命?怎样检测球碰到了球拍的边缘以及如何处理它?球拍的速度会影响球的速度和运动方向吗?当玩家觉得无聊的时候是不是要每隔一段时间就增加游戏的速度呢?当然,当您完成第一个版本之后这些方面都可以改进,不过在您开始编写复杂的代码之前把所有这些都考虑进去了会更加容易。
作为一个例子,我会写下本章中您将开发的Pong游戏的设计构思。您可能会用5到10页的篇幅来写更加复杂的游戏,但是不要一行代码都没有写就写了100页的想法,这会花很多时间而且很难管理这些想法。使用“敏捷方法学”您将经常更新想法并修改一些细节,我一般都是在代码至少完成了50%之后才开始画UML图,而像概览图和文档直到项目快结束的时候才进行更新。
Pong游戏构思
这个游戏是Pong游戏的一个简单的克隆版本,支持一个或两个玩家。在单人模式中计算机会控制另一个球拍;在双人模式中,两个玩家可以使用GamePad玩,也可以在个人电脑上使用同一个键盘来操作,还可以在Xbox 360上来玩。
游戏会为球和球拍使用一些2D图片,以及一些声音文件来支持当球撞倒球拍或者死了一次的特效,还有一个非常简单的游戏菜单供选择:
游戏特点及玩法
游戏有两个屏幕界面:菜单和游戏。菜单就是之前提到的包含几个菜单选项,游戏主要包含基本的球、两个球拍以及一个简单的计分系统。看一张简单的草图明白您将完成的任务(图2-2):
![](https://images.cnblogs.com/cnblogs_com/zoujia867/Trans_XNAPro/Figure2-2.jpg)
图2-2
使用的技术
很明显,您要使用XNA Game Studio Express,不需要使用其它的类库,因为只是渲染简单的Sprites,所以也不需要3D模型和Shaders,而且游戏要在Windows和Xbox 360平台上都可以运行。
正如您所看到的,我设法将所有东西都放到一页纸的篇幅内,这是我在写游戏构思的时候的一个惯例:首先把所有东西都放在一页篇幅内,然后写一些代码,如果您的小组或者外部合作伙伴(比如,如果您想把您的游戏想法展示给一个游戏出版商)需要的话,可以写一份5到10页的白皮书,包含一些最初的想法和草图。
我从来不会在游戏构思的阶段谈论编码,我只要确定要使用什么技术。现在是时候考虑具体实现的问题了。不要在游戏构思的时候考虑您的实际能力,因为这会限制您,尽管把它们抛之脑后吧!
有关游戏构思的一个完整的例子是Rocket Commander游戏的构思(参见图2-3),您可以查看本章文件夹中的相关PDF文件。它有4页内容和一个封面,它包含了Rocket Commander的所有构想。从封面您可以看到,它和游戏的构思很不同,不过基本的游戏想法都体现出来了。
![图2-3](https://images.cnblogs.com/cnblogs_com/zoujia867/Trans_XNAPro/Figure2-3.jpg)
图2-3
敏捷方法学
对软件开发来说敏捷方法学(Rocket Commander)是个概念范畴,它的主要思想是避免大的计划阶段,因为这会带来很多危险,而且很难遵从不切实际的项目进度表。对于小的项目,您可能只要短时间的反反复复,但随着项目的增大,您会发现您总是在不停地解决问题,而这些问题可能要花费几个月甚至几年才能搞定,所有这些都是很难计划的。
实际上不要把所有东西都尽量设计得特别详细,只要一些粗略的构思就可以了。在整个敏捷开发过程中构思的每个部分都会反复很多次,把它设计的更详细,借助单元测试来设计代码,然后编写实际的代码来实现,最后测试并写文档。这样的反复不应该超过几个星期,而且大项目的所有部分都会重复这样的过程。
敏捷方法还包含其它的一些思想,比如如何让开发人员和客户、测试人员、项目管理人员以及设计人员更好地协作等等。有几条规则可以用到敏捷框架中来避免整体的混乱。因为设计阶段被缩短了,所以看起来好像没有为项目进行详细的设计,很像混乱的强力编码。但实际上设计过程贯穿于更个项目,而且比在开始就设计好一切并解决那个阶段产生的所有错误更有用。
本书中我不会过多地讨论敏捷方法学,不过我会使用单元测试以及简略的概念,单元测试设计,然后反复地编码将贯穿本书的所有项目。如果您想了解单元测试的更多信息我推荐您访问Martin Fowler的站点:http://martinfowler.com 。
Martin也写了好几本书,我推荐《重构:改善既有代码的设计》(Refactoring: Improving the Design of Existing Code)这本书,您也可以在网站上找到它,您还可以在他的站点上找到更多的有关敏捷软件开发过程的链接及信息。
解决一开始的难题
好了,游戏的构思您已经有了,但您还没有花时间在找图片和声音文件上,以及如何实现它们,球撞击球拍的问题又如何处理呢?您是否需要为球而设计一个简单的物理引擎呢?
在设计阶段之后,所有这些问题都会冒出来,而对于更加复杂的项目来说,可能要花几个星期甚至几个月的时间来解决一些基本问题。比如,我做的第一个.Net 2.0游戏Rocket Commander,我想在屏幕上显示上千颗小行星,总体上有数百万个,我还想让它们相互碰撞并相互探测,但如何最优化地实现它并不是很清楚。经过最初的测试我发现,我甚至不能在不明显降低性能的前提下渲染超过100个小行星。此时我并没有重写我的游戏构思,也没有做一个小行星少得可怜令人厌烦的游戏(如果没有很多小行星那它就不是一个真正的游戏——这就是Rocket Commander这款游戏的原则),而是试着来解决这个问题,我把小行星按照Shaders和素材设置(Material Settings)分类,然后渲染它们,如果小行星不可见的话就尽早把它们清空,而且只把物理过程的处理应用于相邻的小行星上。
虽然经过一周的努力和最优化处理,小行星可以渲染出来了,而且性能还不错,但是物理过程的处理仍然很慢。即使应用最好的最优化处理,在每一帧的可视范围内更新5000到10000个小行星来查看它们相互碰撞仍然显得太多,但我依然想让小行星相互撞击并在碰撞后仍能运行正常。如果每一帧我只检查一定数量的小行星,算法经常会没有碰撞效果,而且只有当一个小行星进入渗透到另一个里面的时候才出现碰撞,带来的麻烦比移除物理过程还多。我几乎快要放弃了,因为我已经花了整个项目的50%的时间来解决这一个问题。但后来我又有了一个想法,把整个空间分割成若干个区域,每个区域都有特定数量的小行星,每当一个小行星离开一个区域的时候就把它从这个区域移除,并添加到新的区域中,这样小行星只需要检测它们自己的以及邻近的区域中的碰撞。这听起来好像还有很多碰撞检测,但是使用“区域逻辑(sector logic)”的确把物理过程的性能提高了100倍之多,而且经过最优化处理效果还会更明显。
现在,在我参与的大多数游戏项目中我会花一半还多的时间来处理最初的难题,然后把它们分割成更小的问题来解决,稍后把这些小的碎片再重新组合起来,很快地过一些天之后整个游戏就完成了。虽然这是一个尽可能快的方式来完成一个游戏,但是很长一段时间看不到游戏运行的结果也会有一种挫败感。在过去,我总是反反复复地修改我的游戏直到我对结果满意为止,这也的确会花很长时间。多亏了敏捷开发过程,现在我可以解决这些问题并且再也不用重复几乎没有止境的改进循环了。
这对您的Pong游戏意味着什么呢?游戏很简单,您不需要花很多时间对它不断地改进100次,只要实现一个基础版本的Pong游戏,为计算机硬编码(hard-coded)一个球拍,在屏幕左边随着球的位置来回上下移动,并且还要控制右边的球拍。经过改进并测试单人模式的游戏之后,您或许想支持双人对战的模式,但这非常难,因为您硬编码了计算机球拍的实现部分。您可能会把这部分代码注释掉或者干脆删掉,并重新修改玩法部分的代码。等测试了双人对战模式之后,您可能又想添加一个菜单,您又再一次调整修改代码,这样不断地进行下去。
虽然这是一个好的实践方式,并迫使您不断地重构您的代码(重构的意思是在不改变功能的前提下修改代码的设计使得更容易维护,可以参考前面推荐的Martin Fowler写的有关重构的书),不过如果每次只把重点放在游戏的一个部分上,并且同时使用几个版本进行测试会更加容易。在本章接下来的部分您将要这么做,希望您能领会我的意思。借助单元测试,代码重构会更自然,并让您的代码更易使用,还能让您更快地看到运行结果。
接下来您要解决的一个问题是,怎么处理球的物理运动,并且如何检测球撞击了球拍的哪个部位。
创建纹理
在您开始编码之前,您需要纹理素材在屏幕上显示游戏元素的材质。即使这里您使用虚拟位图(dummy bitmap)或者从其他地方得到的图片,考虑最终的图像效果还是很重要的,比如球和球拍的尺寸等等诸如此类的东西。
再说一次,您的Pong游戏相当地简单,但更加复杂的游戏可能需要一个美术创意、很多草图以及一个能管理这些设计作品并能及时地完成它们以便游戏编程人员实现它们的人。回顾计算机游戏早期,几乎所有的游戏都是靠一个人来完成的,虽然这是最容易的方式,但今天没有一个正规的游戏可以只靠一个人来开发。大多数优秀的美术设计人员都不是编程人员,大多数编程人员又不是好的美术设计。如果您只做一个简单的游戏这倒无所谓,但是您的项目越大,您就需要更多的时间来找合作伙伴、有经验的美术设计人员和模型制作人员,并把他们有效地组织起来。
看一下图2-4的游戏图片,您需要三个纹理文件:(1)SpaceBackground.dds 作为背景;(2)PongMenu.png,它包含了Logo,菜单项以及其他游戏需要的文本信息;(3)PongGame.png,用来修饰球拍和球。
![](https://images.cnblogs.com/cnblogs_com/zoujia867/Trans_XNAPro/Figure2-4.jpg)
图2-4
另外您还要导入两个声音文件(PongBallHit.wav和PongBallLost.wav)到您的项目中,并添加一个简单的XACT项目来播放声音。更多关于声音处理和XACT的内容请参考第九章。
Pong游戏逻辑很简单,也不需要太多的文字内容或者图像(如图2-1所示),这使得编写代码以及写游戏构思变得很简单。写下该游戏需要的所有组件(比如球、球拍和屏幕边框等)以及您的最初想法。
![图2-1](https://images.cnblogs.com/cnblogs_com/zoujia867/Trans_XNAPro/Figure2-1.jpg)
图2-1
写下您的想法!
写下您的想法并不是说您必须很疯狂地写下所有想法、画UML图,直到所有东西都准备好了才开始工作,我很反对这种方式,因为您不可能把所有东西都想到。而且,初学者一般都不知道以怎样最好的方式预先设计整个游戏。然而,您可以在您的脑海中对您的游戏有一个非常清晰的画面,当您在脑海中花了足够多的时间来设计之后就可以开工了。
如果您的项目中还有其他人和您一起工作,那么解释游戏的构思以及让每个人的工作都保持同步将变得更加困难。仅仅因为这个原因您就得养成一个习惯,只用一页纸写下您的游戏构思。您不能只把这个展示给其他人看,您还得考虑其他的东西——当您把想法写下来的时候您会发现它比您脑海中想的更加复杂,而且您不得不考虑很多相关的东西。
比如,在Pong游戏中您知道您需要两个球拍和一个在球拍之间运动的球。但或许只有当您写下最初的构思以及要使用的纹理素材之后,您才会想到更加详细的游戏玩法——每个玩家有几条命?怎样检测球碰到了球拍的边缘以及如何处理它?球拍的速度会影响球的速度和运动方向吗?当玩家觉得无聊的时候是不是要每隔一段时间就增加游戏的速度呢?当然,当您完成第一个版本之后这些方面都可以改进,不过在您开始编写复杂的代码之前把所有这些都考虑进去了会更加容易。
作为一个例子,我会写下本章中您将开发的Pong游戏的设计构思。您可能会用5到10页的篇幅来写更加复杂的游戏,但是不要一行代码都没有写就写了100页的想法,这会花很多时间而且很难管理这些想法。使用“敏捷方法学”您将经常更新想法并修改一些细节,我一般都是在代码至少完成了50%之后才开始画UML图,而像概览图和文档直到项目快结束的时候才进行更新。
Pong游戏构思
这个游戏是Pong游戏的一个简单的克隆版本,支持一个或两个玩家。在单人模式中计算机会控制另一个球拍;在双人模式中,两个玩家可以使用GamePad玩,也可以在个人电脑上使用同一个键盘来操作,还可以在Xbox 360上来玩。
游戏会为球和球拍使用一些2D图片,以及一些声音文件来支持当球撞倒球拍或者死了一次的特效,还有一个非常简单的游戏菜单供选择:
- 单人模式
- 双人模式
- 退出游戏
游戏特点及玩法
游戏有两个屏幕界面:菜单和游戏。菜单就是之前提到的包含几个菜单选项,游戏主要包含基本的球、两个球拍以及一个简单的计分系统。看一张简单的草图明白您将完成的任务(图2-2):
![](https://images.cnblogs.com/cnblogs_com/zoujia867/Trans_XNAPro/Figure2-2.jpg)
图2-2
使用的技术
很明显,您要使用XNA Game Studio Express,不需要使用其它的类库,因为只是渲染简单的Sprites,所以也不需要3D模型和Shaders,而且游戏要在Windows和Xbox 360平台上都可以运行。
正如您所看到的,我设法将所有东西都放到一页纸的篇幅内,这是我在写游戏构思的时候的一个惯例:首先把所有东西都放在一页篇幅内,然后写一些代码,如果您的小组或者外部合作伙伴(比如,如果您想把您的游戏想法展示给一个游戏出版商)需要的话,可以写一份5到10页的白皮书,包含一些最初的想法和草图。
我从来不会在游戏构思的阶段谈论编码,我只要确定要使用什么技术。现在是时候考虑具体实现的问题了。不要在游戏构思的时候考虑您的实际能力,因为这会限制您,尽管把它们抛之脑后吧!
有关游戏构思的一个完整的例子是Rocket Commander游戏的构思(参见图2-3),您可以查看本章文件夹中的相关PDF文件。它有4页内容和一个封面,它包含了Rocket Commander的所有构想。从封面您可以看到,它和游戏的构思很不同,不过基本的游戏想法都体现出来了。
![图2-3](https://images.cnblogs.com/cnblogs_com/zoujia867/Trans_XNAPro/Figure2-3.jpg)
图2-3
敏捷方法学
对软件开发来说敏捷方法学(Rocket Commander)是个概念范畴,它的主要思想是避免大的计划阶段,因为这会带来很多危险,而且很难遵从不切实际的项目进度表。对于小的项目,您可能只要短时间的反反复复,但随着项目的增大,您会发现您总是在不停地解决问题,而这些问题可能要花费几个月甚至几年才能搞定,所有这些都是很难计划的。
实际上不要把所有东西都尽量设计得特别详细,只要一些粗略的构思就可以了。在整个敏捷开发过程中构思的每个部分都会反复很多次,把它设计的更详细,借助单元测试来设计代码,然后编写实际的代码来实现,最后测试并写文档。这样的反复不应该超过几个星期,而且大项目的所有部分都会重复这样的过程。
敏捷方法还包含其它的一些思想,比如如何让开发人员和客户、测试人员、项目管理人员以及设计人员更好地协作等等。有几条规则可以用到敏捷框架中来避免整体的混乱。因为设计阶段被缩短了,所以看起来好像没有为项目进行详细的设计,很像混乱的强力编码。但实际上设计过程贯穿于更个项目,而且比在开始就设计好一切并解决那个阶段产生的所有错误更有用。
本书中我不会过多地讨论敏捷方法学,不过我会使用单元测试以及简略的概念,单元测试设计,然后反复地编码将贯穿本书的所有项目。如果您想了解单元测试的更多信息我推荐您访问Martin Fowler的站点:http://martinfowler.com 。
Martin也写了好几本书,我推荐《重构:改善既有代码的设计》(Refactoring: Improving the Design of Existing Code)这本书,您也可以在网站上找到它,您还可以在他的站点上找到更多的有关敏捷软件开发过程的链接及信息。
解决一开始的难题
好了,游戏的构思您已经有了,但您还没有花时间在找图片和声音文件上,以及如何实现它们,球撞击球拍的问题又如何处理呢?您是否需要为球而设计一个简单的物理引擎呢?
在设计阶段之后,所有这些问题都会冒出来,而对于更加复杂的项目来说,可能要花几个星期甚至几个月的时间来解决一些基本问题。比如,我做的第一个.Net 2.0游戏Rocket Commander,我想在屏幕上显示上千颗小行星,总体上有数百万个,我还想让它们相互碰撞并相互探测,但如何最优化地实现它并不是很清楚。经过最初的测试我发现,我甚至不能在不明显降低性能的前提下渲染超过100个小行星。此时我并没有重写我的游戏构思,也没有做一个小行星少得可怜令人厌烦的游戏(如果没有很多小行星那它就不是一个真正的游戏——这就是Rocket Commander这款游戏的原则),而是试着来解决这个问题,我把小行星按照Shaders和素材设置(Material Settings)分类,然后渲染它们,如果小行星不可见的话就尽早把它们清空,而且只把物理过程的处理应用于相邻的小行星上。
虽然经过一周的努力和最优化处理,小行星可以渲染出来了,而且性能还不错,但是物理过程的处理仍然很慢。即使应用最好的最优化处理,在每一帧的可视范围内更新5000到10000个小行星来查看它们相互碰撞仍然显得太多,但我依然想让小行星相互撞击并在碰撞后仍能运行正常。如果每一帧我只检查一定数量的小行星,算法经常会没有碰撞效果,而且只有当一个小行星进入渗透到另一个里面的时候才出现碰撞,带来的麻烦比移除物理过程还多。我几乎快要放弃了,因为我已经花了整个项目的50%的时间来解决这一个问题。但后来我又有了一个想法,把整个空间分割成若干个区域,每个区域都有特定数量的小行星,每当一个小行星离开一个区域的时候就把它从这个区域移除,并添加到新的区域中,这样小行星只需要检测它们自己的以及邻近的区域中的碰撞。这听起来好像还有很多碰撞检测,但是使用“区域逻辑(sector logic)”的确把物理过程的性能提高了100倍之多,而且经过最优化处理效果还会更明显。
现在,在我参与的大多数游戏项目中我会花一半还多的时间来处理最初的难题,然后把它们分割成更小的问题来解决,稍后把这些小的碎片再重新组合起来,很快地过一些天之后整个游戏就完成了。虽然这是一个尽可能快的方式来完成一个游戏,但是很长一段时间看不到游戏运行的结果也会有一种挫败感。在过去,我总是反反复复地修改我的游戏直到我对结果满意为止,这也的确会花很长时间。多亏了敏捷开发过程,现在我可以解决这些问题并且再也不用重复几乎没有止境的改进循环了。
这对您的Pong游戏意味着什么呢?游戏很简单,您不需要花很多时间对它不断地改进100次,只要实现一个基础版本的Pong游戏,为计算机硬编码(hard-coded)一个球拍,在屏幕左边随着球的位置来回上下移动,并且还要控制右边的球拍。经过改进并测试单人模式的游戏之后,您或许想支持双人对战的模式,但这非常难,因为您硬编码了计算机球拍的实现部分。您可能会把这部分代码注释掉或者干脆删掉,并重新修改玩法部分的代码。等测试了双人对战模式之后,您可能又想添加一个菜单,您又再一次调整修改代码,这样不断地进行下去。
虽然这是一个好的实践方式,并迫使您不断地重构您的代码(重构的意思是在不改变功能的前提下修改代码的设计使得更容易维护,可以参考前面推荐的Martin Fowler写的有关重构的书),不过如果每次只把重点放在游戏的一个部分上,并且同时使用几个版本进行测试会更加容易。在本章接下来的部分您将要这么做,希望您能领会我的意思。借助单元测试,代码重构会更自然,并让您的代码更易使用,还能让您更快地看到运行结果。
接下来您要解决的一个问题是,怎么处理球的物理运动,并且如何检测球撞击了球拍的哪个部位。
创建纹理
在您开始编码之前,您需要纹理素材在屏幕上显示游戏元素的材质。即使这里您使用虚拟位图(dummy bitmap)或者从其他地方得到的图片,考虑最终的图像效果还是很重要的,比如球和球拍的尺寸等等诸如此类的东西。
再说一次,您的Pong游戏相当地简单,但更加复杂的游戏可能需要一个美术创意、很多草图以及一个能管理这些设计作品并能及时地完成它们以便游戏编程人员实现它们的人。回顾计算机游戏早期,几乎所有的游戏都是靠一个人来完成的,虽然这是最容易的方式,但今天没有一个正规的游戏可以只靠一个人来开发。大多数优秀的美术设计人员都不是编程人员,大多数编程人员又不是好的美术设计。如果您只做一个简单的游戏这倒无所谓,但是您的项目越大,您就需要更多的时间来找合作伙伴、有经验的美术设计人员和模型制作人员,并把他们有效地组织起来。
看一下图2-4的游戏图片,您需要三个纹理文件:(1)SpaceBackground.dds 作为背景;(2)PongMenu.png,它包含了Logo,菜单项以及其他游戏需要的文本信息;(3)PongGame.png,用来修饰球拍和球。
![](https://images.cnblogs.com/cnblogs_com/zoujia867/Trans_XNAPro/Figure2-4.jpg)
图2-4
另外您还要导入两个声音文件(PongBallHit.wav和PongBallLost.wav)到您的项目中,并添加一个简单的XACT项目来播放声音。更多关于声音处理和XACT的内容请参考第九章。