第一部分:基础知识(第一章)一个XNA 手机程序(续)

彭博 发布于 2012/03/09 15:19
阅读 277
收藏 0

SpriteFont1.spritefont 这个文件名起的实在不是十分生动。建议以它描述的实际字体来进行重命名;如果您需要继续使用默认字体设置,你可以将其重命名为 Segoe14.spritefont。右键单击文件名并选择属性可以查看这个文件的属性——您可以看到该资产的名称,也就是不带扩展名的文件名:Sagoe14。该资产名在程序中指向您所加载的字体。如果你将资产名更改成独立于文件名的名字,在将来的使用中你将会混淆,找不到你所对应的字体。

在初始阶段,XNAHelloPhone项目包含2个C#代码文件:Program.cs和Game1.cs。第一个文件非常简单而且和Windows Phone 7游戏程序丝毫无关!只有当定义了WINDOWS或XBOX标记之后,预处理器指令才能激活Program类。当编译Windows Phone程序时,WINDOWS_PHONE标记取而代之。

对很多小游戏来讲,你几乎会将所有时间都花费在Game1.cs文件上。Game1类继承自Game类,在其原始状态它定义了2个字段:graphics和spriteBatch。在这两个字段的基础上我再增加三个:

XNA Project: XnaHelloPhone File: Game1.cs (excerpt showing fields)

namespace XnaHelloPhone

{

public class Game1 : Microsoft.Xna.Framework.Game

{

GraphicsDeviceManager graphics;

SpriteBatch spriteBatch;

string text = "Hello, Windows Phone 7!";

SpriteFont segoe14;

Vector2 textPosition;

}

}

这三个新的字段简单的指出了什么样的文本会显示,显示的字体以及文本在屏幕上显示的位置。该位置是以相对于显示屏的左上角的像素坐标来指定的。Vector2结构有两个字段指定X和Y的浮点类型。出于性能方面的考虑,XNA的所有浮点值是单精度的。(Silverlight 为双精度。)Vector2结构通常用于二维点、大小和甚至载体。

当游戏在手机上运行时,Game1类被实例化,Game1的构造函数也被同时执行。标准代码如下:

XNA Project: XnaHelloPhone File: Game1.cs (excerpt)

public Game1()

{

graphics = new GraphicsDeviceManager(this);

Content.RootDirectory = "Content";

// Frame rate is 30 fps by default for Windows Phone.

TargetElapsedTime = TimeSpan.FromTicks(333333);

}

第一条语句将图形字段(graphics field)初始化。在第二个语句中,内容(Content)是游戏的属性,它属于ContentManager类型,RootDirectory是该类的属性。将此属性设置为"Content",这是符合当前存储14point Segoe字体的内容目录。第三条语句设置该程序的游戏循环时间,它管理程序更新视频显示的频率。Windows Phone 7屏幕刷新频率为每秒30帧。

Game1实例化后,在Game1实例中调用Run方法,游戏类的基类启动“游戏启动”的进程。第一个步骤之一是调用初始化(Initialize)方法,这一游戏的衍生物可以重写。XNA游戏工作室自动生成框架方法,我没有添加任何内容:

XNA Project: XnaHelloPhone File: Game1.cs (excerpt)

protected override void Initialize()

{

base.Initialize();

}

初始化方法不是加载字体或其他内容的地方。这是稍后时,基类调用LoadContent时的方法。

XNA Project: XnaHelloPhone File: Game1.cs (excerpt)

protected override void LoadContent()

{

spriteBatch = new SpriteBatch(GraphicsDevice);

segoe14 = this.Content.Load ("Segoe14");

Vector2 textSize = segoe14.MeasureString(text);

Viewport viewport = this.GraphicsDevice.Viewport;

textPosition = new Vector2((viewport.Width - textSize.X) / 2,

(viewport.Height - textSize.Y) / 2);

}

在此方法中的第一个语句是系统自动提供的。您很快就会看到如何使用此spriteBatch对象来显示子画面(sprites)。

其他语句是我添加的,你会看到我倾向于用this这个关键词作为Content和GraphicsDevice等属性的前缀来提醒自己,他们是属性而不是静态的类。如前所述,Content的属性是ContentManager类型的。通用的 Load 方法允许加载Content到程序,在这种情况下Content的属性是SpriteFont类型。用引号引起来的名称是Content属性中资产的名称。此语句用来存储加载在SpriteFont类型的segoe14字段中的字体。

XNA中,子画面(sprites)(包括文本字符串)通常通过指定相对于左上角或相对于子画面(sprites)左上角的像素坐标来显示。若要计算这些坐标,最好知道屏幕大小和文本显示时特定字体的大小。

SpriteFont类有一个测算大小非常方便的方法,名为MeasureString,以像素为单位返回Vector2对象与特定的文本字符串的大小。(14磅的Segoe UI Mono字体,相当于18-2/3像素的高度,MeasureString调用后返回28的像素高度。)(For the 14-point Segoe UI Mono font, which has an equivalent height of 18-2/3 pixels, the MeasureString call returns a height of 28 pixels.)

XNA程序通常使用GraphicsDevice类的视区(Viewport)属性来获取屏幕的大小。这通过游戏的GraphicsDevice属性来访问,并提供了宽度和高度属性。

然后直接计算textPosition——与Viewport左上角相对应的坐标(point)即为所显示的文本字符串的左上角。(It is then straightforward to calculate textPosition—the point relative to the upper-left corner of the viewport where the upper-left corner of the text string is to be displayed.)

到目前为止该程序的初始化阶段已经完成,真正开始实质性的操作。程序进入游戏循环。在以30 帧每秒刷新率的视频显示同步过程中,程序中的两种方法被不断调用:更新(Update)接着绘制(Draw)不断循环往复: 更新、 绘制、 更新、 绘制、更新、绘制……(如果每次Update方法需要超过1/30秒完成这个过程其实要比前面所述更加复杂,但我们将在后面几章中详细讨论这些时序问题。)

Draw方法可以让你在显示屏上进行绘制。但这就是你想做的。如果您需要执行一些计算为绘图做准备,你就应该先执行Update方法。Update方法是Draw方法的准备程序。很多时候XNA程序会基于用户输入来移动子画面在显示屏上的位置。对手机用户来说,此用户输入主要涉及手指触摸屏幕。所有对用户输入的处理也应该在Update方法中执行。您将会在第3章中看到示例。

你应该编写您自己的Update和Draw方法,以便他们能尽快的执行。这点相当明显,但我猜,有些非常重要的因素却可能不是很明显:

您应该避免Update和Draw的代码定期从该程序的本地堆(heap?)中分配内存。(You should avoid code in Update and Draw that routinely allocates memory from the program’s local heap.)最终,.Net垃圾回收器会收回部分此内存,而在垃圾回收器做这个工作的时候,你的游戏就可能会卡(your game might stutter a bit)。在关于XNA编程的章节中,您将可以看到如何来避免从堆(heap?)中分配内存。

您的Draw方法可能不会包含任何可疑的代码;它通常潜伏在Update方法中从而导致各种麻烦。尽量避免涉及类的任何新的表达式。这些都将导致内存分配。实例化结构是可以的,不过(however有点奇怪,前后不象是转折关系。Instantiating a structure is fine, however, because structure instances are stored on the stack and not in the heap.),因为结构实例化存储在堆栈上,而不是堆(heap?)。(XNA在对象类型的定义中更多的使用结构而不是类,您经常需要在Update中创建。)但在没有明确的新表达式的情况下,也可能发生堆(heap?)分配。例如,连接两个字符串就会在堆(heap?)上创建另一个字符串。如果您需要在Update方法中巧妙的执行字符串操作,则应使用StringBuilder。简而言之,XNA提供使用StringBuilder对象显示文本的方法。

然而,在XnaHelloPhone中,Update方法简直微不足道。程序所显示的文本固定在一个特定的位置。所有必要的计算也已在LoadContent方法中执行完毕。因此,无需对Update方法做任何改动,只需简单的保留XNA 游戏工作室最初创建其时候的样子即可:

XNA Project: XnaHelloPhone File: Game1.cs (excerpt)

protected override void Update(GameTime gameTime)

{

if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed)

this.Exit();

base.Update(gameTime);

}

默认代码使用静态的游戏板(GamePad)类检查手机硬件的后退按钮是否被按下,并使用此功能键退出游戏。

最后,还有Draw方法。以下代码为您创建的是单一背景色淡蓝色的版本:

XNA Project: XnaHelloPhone File: Game1.cs (excerpt)

protected override void Draw(GameTime gameTime)

{

GraphicsDevice.Clear(Color.CornflowerBlue);

base.Draw(gameTime);

}

在XNA编程社区,一种名为CornflowerBlue的颜色成为其标志性的颜色。当您开发一个XNA程序时,淡蓝色屏幕的外观是非常令人舒服的,因为这意味着程序如同已经使用过Draw方法一样。

但如果您想要节省电源而在OLED上显示的话,你会得到较暗的背景。在修改后的版本中,我妥协的将背景设置为深蓝色。而在Silverlight中,XNA支持被视为标准的140个颜色。下面代码执行后文本显示为白色:

XNA Project: XnaHelloPhone File: Game1.cs (excerpt)

protected override void Draw(GameTime gameTime)

{

GraphicsDevice.Clear(Color.Navy);

spriteBatch.Begin();

spriteBatch.DrawString(segoe14, text, textPosition, Color.White);

spriteBatch.End();

base.Draw(gameTime);

}

显示屏上的子画面(Sprites)被捆绑成一个SpriteBatch对象,它在调用LoadContent时被创建出来。在调用开始(Begin)和结束(End)之间可以有多个调用DrawString来绘制文本和位图。这些是仅有的选项。此特定的索绳调用引用字体、 要显示的文本、 屏幕上,和颜色的左上角相对于文本的左上角的位置。


原文链接:http://blog.csdn.net/shyleoking/article/details/6371370
加载中
返回顶部
顶部