§4.3更多的辅助类

在上一章谈论的辅助类还不够么?是的。我们将在Tetris游戏中使用的两个新类在这里还没有被进一步深入讨论,它们只是本书中我真实使用的类的精简版。不过它们仍然很有用,并且有助于你使得游戏编写过程更加容易。

TextureFont 类你已经知道了XNA中缺少字体支持,你也知道位图字型是在XNA中显示文本的唯一选择(也许使用某些自定义3D字体渲染除外)。

在本书第一个游戏中,你正是使用某些精灵来显示游戏或者菜单中的固定文本。这种方式非常容易,不过对于记分板,你在游戏中肯定需要一种方式,提供一个允许你写下任何文本和数字的动态字型。

让我们继续前进,看一看TetrisGame 类中的TestScoreboard单元测试,渲染了记分板的背景盒,然后写下所有的文本行以显示当前的level级别、分数、最高分纪录,当前游戏中你销毁的行数:

int level = 3, score = 350, highscore = 1542, lines = 13;

TestGame.Start("TestScoreboard",delegate

{

// Draw background box

TestGame.game.backgroundSmallBox.Render(new Rectangle((512 + 240) - 15, 40 - 10, 290 - 30, 190));

// Show current level, score, etc.

TextureFont.WriteText(512 + 240, 50, "Level: ");

 TextureFont.WriteText(512 + 420, 50, (level + 1).ToString());

TextureFont.WriteText(512 + 240, 90, "Score: ");

TextureFont.WriteText(512 + 420, 90, score.ToString());

 TextureFont.WriteText(512 + 240, 130, "Lines: ");

TextureFont.WriteText(512 + 420, 130, lines.ToString());

TextureFont.WriteText(512 + 240, 170, "Highscore: ");

TextureFont.WriteText(512 + 420, 170, highscore.ToString());

}

);

 你可能注意到你目前在使用类TestGame 类启动单元测试。对于这个测试你使用了一系列变量(level、score等等),这些变量被游戏代码中的实际值所替换。在渲染循环中,你首先绘制背景盒(background box),并立刻显示它以避免后面绘制精灵时的显示错误。接下来你借助于新的TextureFont 类的WriteText 方法在指定的屏幕位置,写下四行文本。你实际上调用了8次WriteText 方法,以正确对齐所有的数字在你的背景盒右边,这样比只写4行文本看上去好很多。

在编写这个单元测试之后,你将得到一个编译错误,告诉你TextureFont 类还不存在。在创建了一个带有空的WriteText 方法的空类之后,你应该能够编译和启动测试了。就像展示背景盒,借助于你在上一章学过的SpriteHelper类,背景盒被在屏幕右上部。

甚至在你考虑实现TextureFont 类之前,你将会需要一个带有字型的位图纹理,以便于在屏幕上渲染文本。没有这样一个纹理,你就只能做理论工作,单元测试是要做游戏功能的实际测试的。你需要一个像图 4-3这样的纹理来显义所有的字母、数字和符号。你甚至可以在更大的纹理中使用更多的Unicode字母,或者用多个纹理实现它,不过这就离本章太远了。请查看源代码中,我在TextureFont 类顶部的注释提供的网站,你可以学到更多关于这个话题。

 图4-3

看一看TextureFont类的实现(见图 4-4)。调用TextureFont类非常容易;你只要像不久前在单元测试中那样调用WriteText 方法 。不过内部的代码不是很容易。该类保存了GameFont.png纹理的每一个字母的矩形,这个纹理被用在WriteAll方法中,通过在屏幕上绘制一个接一个的字母来渲染文本。该类也包含GameFont.png这个字体纹理、一个sprite batch对象来帮助渲染屏幕上的字体精灵,以及几个辅助变量来决定字型的高度。要检查屏幕上的文本占了多少宽度,你就要使用GetTextWidth 方法了。

图4-4

FontToRender 是一个内部类,它负责你想在每一帧渲染的所有文本,这个过程非常类似于SpriteHelper类在每一帧最后渲染屏幕上的所有精灵。SpriteHelper.DrawAll方法被BaseGame类以同样的方式调用,TextureFont.WriteAll方法也被调用,绘制了屏幕上的一切,并且清空所有列表。要更深入学习TextureFont类,察看源代码,运行单元测试或者尝试逐步调试 WriteAll 方法。

Input Class

 另一个用在Tetris 游戏中的新类是Input,这个类封装了所有的输入处理,检查,并且更新了你在前几章所做的。第十章进一步深入谈论Input类,一些优秀的类确实需要来自于Input类的所有特性。(如图 4-5)

图4-5

如你所见,Input类有相当多的属性,和一些辅助方法来访问键盘、gamepad和鼠标数据。借助于Update 静态方法,每一帧将被更新,这个方法直接被BaseGame 类调用。对于这个游戏,你主要使用键盘和gamepad 的press方法,诸如 GamePadAJustPressed 或者 KeyboardSpaceJustPressed。和RandomHelper 类很相似,Input 类如何工作不再难以理解,你已经在上一章从功能上实现了很多了。更多细节和应用你可以查看第十章。

Sound Class

 好了,你已经在第二章的第一个游戏中接触了声音,在第三章的Breakout 游戏也用到了。为了保持事情的简单性,为了后面不修改任何游戏类,就允许你添加更多的声音功能,sound management现在被移到了Sound类。快速浏览一下Sound类(如图 4-6)。

图4-6

这个版本看起来很简单,但是在第九章,还会深入谈论XACT,你将大幅度扩展Sound类,使得在本书最后它能为你那个伟大的竞速游戏准备就绪。 如你所见,所有的声音变量目前在Sound类中了,Game类不再包含任何声音变量。Sound构造器是静态的,并且当你第一次使用Play方法播放声音文件时将自动地被调用。Update方法自动地被BaseGame类调用。

Sounds枚举值和TestPlayClickSound 单元测试方法依赖于你当前游戏的实际内容。这些值对于你所写的每一个游戏而言都是变化的, 但是修改Sounds枚举值是非常简单的。你也许问为什么不正好借助于保存在XACT中的cue名称播放声音 ? 好的,可能由于错误打印一个声音的Cue名称而发生许多错误,万一你删除、重命名,或者改变一个声音的Cue名称就很难跟踪。Sounds枚举也使得快速添加一个声音特效、以及从IntelliSense查看可用的声音特效非常容易。 Tetris 游戏使用下列一些声音:

 BlockMove 向左、右、下移动砖块时播放。这是非常安静的声音特效。

BlockRotate 用于你旋转当前砖块的时候,并且听起来非常“速度感”。

BlockFalldown 用于当前砖块到达地面的时候,并且最终着陆。

LineKill 在游戏中每一次你决定销毁一行的时候被播放。

Fight 在每一次玩家启动游戏的时候被播放。

Victory 用于当玩家到达下一level关卡的时候,并且包含一阵鼓掌声。

Lose 一个老面孔了,当玩家游戏失败时播放。


发布时间:2008/6/26 上午8:26:20  阅读次数:5433

2006 - 2024,推荐分辨率 1024*768 以上,推荐浏览器 Chrome、Edge 等现代浏览器,截止 2021 年 12 月 5 日的访问次数:1872 万 9823 站长邮箱

沪 ICP 备 18037240 号-1

沪公网安备 31011002002865 号