游戏组件
TetrisGame类也拥有Components属性中的所有游戏组件,该属性继承于game类。你能添加任何继承于GameComponent类的子类到这个列表,并且当你的游戏被启动和被更新的时候,该子类会自动地被调用。当你绘制游戏的时候,因为GameComponent类没有Draw方法,所以GameComponent类的子类不会被调用。不过你可以实现自己的绘制方法,或者仅仅使用DrawableGameComponent类,这个类有Draw方法。对于游戏组件,XNA没有直接的Draw方法支持;你必须亲自调用它,以确保所有组件被按照正确的顺序调用。因为这个以及其他一些原因(强迫你使用这个几乎没有益处的模型,会使得单元测试更难,而你自己的游戏类可能更有效或者更特殊),在本书后面你将不会使用很多游戏组件。这通常是个好主意,不过没有它你也能生活,因为无论如何你必须创建自己的游戏组件,并且你不得不为它们亲自调用Draw方法。仅仅为了Update方法,使用它们还不算太有意义。
就像我在第一章提及的,其基本思路是为了让用户合作,分享用户的游戏组件,从而允许其他人使用他们的游戏引擎的一部分。
例如,一个帧计算组件或者甚至一个充分发展的3D地形渲染引擎都可能被作为一个游戏组件执行,不过仅仅因为某人不使用游戏组件 不意味着难以拷贝。 例如,你有一个复杂的游戏组件,像一个地形渲染模块,它可能也涉及一些其他的类,并且使用它自己的渲染引擎,如果你只是拷贝一个文件上去,这个引擎不会超出你的引擎工作。插入外部代码经常需要相当多次的重构,直到它可以用在你自己的引擎。在XNA Framework的beta 1版本中,XNA Game Studio Express中有一个图形化的设计器可用于游戏组件,你可以拖拽组件到你的游戏类,或者甚至其它组件中,不编写一行代码就给你的游戏添加特性。由于这个特色非常复杂,bug成堆,在Xbox 360上不能运行,在XNA Framework的beta 2 发行版中,就被丢弃了。
游戏组件是否被使用是不能确定的事,也许对大多数程序员来说缺少设计器,以及亲自调用那些Draw方法都不是麻烦。许多游戏组件可能用到,并且知道它们的所有基础也是有用的。在俄罗斯方块的案例中,一些组件需要注意:
-
格子自身有彩色砖块和当前掉落的砖块
-
记分板有当前的level关卡、得分、最高纪录、你销毁的行数
-
游戏中,下一个砖块的模型盒
-
更简单的东西,诸如帧计数器、处理输入等等
我决定只是把俄罗斯方块的网格和下一个砖块特性作为游戏组件来实现;所有的代码刚好以这种简单方式实现,只要为它们几个新类。如果你将复用记分板,例如,你可能一直把它放在游戏组件,但我不考虑任何其他游戏,我喜欢用到记分板就编写一个。
仔细看看 Game 类,以及要添加给它的组件(如图 4-2)。
灰色箭头标明由于TetrisGrid类和NextBlock类被添加到Game类的组件列表,这些方法会自动被调用。在TetrisGame.Draw 中调用TetrisGrid中的Draw 方法,后者又调用NextBlock.Draw 方法。TetrisGame自身拥有的只是TetrisGrid类的一个实例。NextBlock 实例仅仅被用在TetrisGrid类内部.
你能看出对于这三个类使用游戏组件强制你考虑了调用顺序,并且只是由于没有把一切放进一个大类中就使得你的游戏更有组织。这是件好事,如果你是一个有经验的程序员,虽然能自己做所有这些,在XNA中预先考虑游戏组件化对于初学者可能是个好主意。