第一部分:基礎知識(第一章)一個XNA 手機程序(續)

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來繪製文本和位圖。這些是僅有的選項。此特定的索繩調用引用字體、 要顯示的文本、 屏幕上,和顏色的左上角相對於文本的左上角的位置。

發佈了199 篇原創文章 · 獲贊 8 · 訪問量 59萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章