4.4更多辅助类


     上一章我们讨论了很多辅助类的知识,本章并不会特别详细地讲解Tetris游戏中要用到的两个新辅助类,因为它们是本书后面要用到的真正的类的精简版本。但它们还是很有用的,可以让您的游戏开发过程更加容易。

TextureFont

     您已经了解到XNA本身不支持字体,唯一一个显示文本的方式就是使用位图字体(也可能使用自定义的3D字体渲染方式)。在本书的前几个游戏中,您只是使用了一些sprites在游戏或者菜单中显示固定的文本内容。这种方式很简单,但在实现记分板功能的时候,您很显然需要动态字体支持,这样才可以显示任何游戏中需要的文本和数字内容。

     现在让我们稍微超前一点,先看看TetrisGame类中的TestScoreboard单元测试,它的作用是渲染记分板的背景区域,并显示游戏的当前级别、得分、最高分以及消除的行数:

int level = 3, score = 350, highscore = 1542, lines = 13
TestGame.Start(
"TestScoreboard",
    
delegate
    
{
        
// Draw background box
        TestGame.game.backgroundSmallBox.Render(new Rectangle(
            (
512 + 240- 1540 - 10290 - 30190));
        
// Show tetris grid in center of screen
        TestGame.game.tetrisGrid.Draw(new GameTime());
        
// Show current level, score, etc.
        TextureFont.WriteText(512 + 24050"Level: ");
        TextureFont.WriteText(
512 + 42050, (level + 1).ToString());
        TextureFont.WriteText(
512 + 24090"Score: ");
        TextureFont.WriteText(
512 + 42090, score.ToString());
        TextureFont.WriteText(
512 + 240130"Lines: ");
        TextureFont.WriteText(
512 + 420130, lines.ToString());
        TextureFont.WriteText(
512 + 240170"Highscore: ");
        TextureFont.WriteText(
512 + 420170, highscore.ToString());
    }
);

     请注意此处使用的是TestGame类来启动单元测试。该测试中使用了几个变量(levelscore等等),在具体的游戏代码中它们会被真实的数据取代。在渲染循环中,首先画出背景区域并立即显示出来,以避免稍后绘制sprites时出现错误。然后,使用TextureFont类中的WriteText方法在指定的屏幕位置上画出四行文字。实际上WriteText被调用了8次,是为了让数字在背景区域内靠右对齐,这样看起来更好看一点。

     写完该单元测试,会得到一个编译错误,告诉您TextureFont类还不存在。创建一个包含WriteText伪方法的伪类(即TextureFont类)之后,就可以编译并开始测试了,不过此时只在屏幕的右上角显示背景区域。

     在考虑实现TextureFont类之前,您要有包含字体的位图素材,这样才能在屏幕上渲染文本。没有素材的话您仅仅是在做理论工作,而单元测试却是关乎游戏功能的实践性测试。您需要一个如图4-3所示的素材,来显示所有的字母、数字和符号。您也可以使用更大的包含更多Unicode字符的素材,或者使用多个素材来达到同样的目的,不过这已经超出了本章要讨论的范围。有关这个高级主题,您可以查看一下源代码TextureFont类的注释中所列出的几个网址,来了解更多的相关知识。

(译者注:这几个网址是:

http://blogs.msdn.com/garykac/archive/2006/08/30/728521.aspx

http://blogs.msdn.com/garykac/articles/732007.aspx

http://www.angelcode.com/products/bmfont/

 

4-3

     接下来看一下TextureFont类的实现(见图4-4)。TextureFont类的调用很简单,就像前面的那个测试一样,只需调用WriteText方法。但其内部的代码实现并不那么简单。该类为素材GameFont.png中的每个字符都生成一个Rectangle对象实例,然后在WriteAll方法中把要渲染的文本逐个字符地画到屏幕上。它还包含了其它几个变量,比如字体素材即GameFont.png,用于渲染spritesSpriteBatch实例,以及用于确定字体高度的相关变量。如果要获取给定文本的宽度,可以使用GetTextWidth方法。
图4-4

4-4

     其中FontToRender类用来保存每一帧要渲染的文本内容,它很像SpriteHelper类在每一帧结尾时渲染sprites的过程。与BaseGame调用SpriteHelper.DrawSprites方法一样,TextureFont.WriteAll也会被BaseGame调用,把所有内容绘制到屏幕上,然后清空所有列表。如果想进一步了解TextureFont类,请查看本章的源代码,运行单元测试,或者试着一步步调试WriteAll方法。

Input

     Tetris游戏中要使用的另一个新辅助类是Input类,它封装了前几章中您做过的所有输入处理、检测以及更新操作。第十章将更详细地讨论Input类,而且有些类非常需要Input类提供的相关操作(如图4-5)。
图4-5

4-5

     可以看到,Input类拥有非常多的属性以及一些用于访问键盘、gamepad和鼠标的方法。BaseGame类直接调用Input类的静态Update方法,这样每一帧它都会被更新。而本游戏中您将主要使用键盘和gamepad被按下时的相关处理方法,例如GamePadAJustPressedKeyboardSpaceJustPressed。和RandomHelper类一样,您也会很容易地理解该类如何工作,而且前一章您已经实现了大部分功能了。有关该类更多内容请参照第十章。

Sound

     关于声音的处理,在第二章的第一个游戏以及第三章的Breakout游戏中您已经接触过了。为了简单起见,以及今后在添加更多声音功能时不至于修改已有的代码,本章中把声音的管理都转移到了Sound类中。如图4-6显示了该类的信息。该版本看起来很简单,但是到了第九章,会进一步讨论XACT的内容,您将对Sound类进行大量的扩展,以便为本书最后要做的赛车游戏做好准备。
图4-6

4-6

     可以看出,所有的声音变量都被放到了该类中,Game类中不再包含任何声音变量。Sound类的构造函数是静态的,它会在您第一次调用Play方法播放声音的时候自动被调用。其Update方法将被BaseGame类自动调用。

     其中Sounds枚举值以及TestPlayClickSound单元测试取决于您要做的当前游戏的实际内容。这些值将随着每一个游戏而改变,不过更改Sounds的枚举值也很简单。您可能会问为什么不直接使用保存在XACT中的cue names来播放声音?理由是,仅仅是误输入sound cue的话就会产生很多错误,而且万一您移除、重命名或者变更sound cue的话会很难跟踪所有的变化。使用Sounds枚举可以很容易地添加一个新的声效,而且通过编辑器的智能感知系统可以知道都有哪些声效可用。

Tetris游戏使用了如下一些声音:
  • BlockMove:用于方块向左、向右或者向下移动时,该声效非常非常小
  • BlockRotate:用于旋转方块时,它听起来非常地“whooshy.
  • BlockFalldown:用于方块落地时
  • LineKill:用于消除一行时
  • Fight:用于游戏开始时激励玩家
  • Victory:用于玩家升级时,伴有鼓掌欢呼声
  • Lose:用于玩家输掉游戏时

图4-3