文章目錄
我最近勤快地連自己都有些不可思議。昨天有朋友在上一篇文章裡留言,批評Windows Phone 7暫時沒有支援中文版的問題。凡事都有個過程,在中文版出來前,咱們想自己想點辦法吧。Silverlight for Windows Phone那邊就不管了,肯定會有人想出辦法來的。如何讓Windows Phone 7遊戲顯示中文?把說“貼圖”的那個人拖出去打死!因為XNA 4.0中支援中文的辦法倒是現成的,這與XNA字型支援的方式有很大關係。
範例程式碼:
http://files.cnblogs.com/aawolf/XNA_aawolf_SIP_Chinese.rar
繪製字型
我們先來看一下XNA中如何繪製字型,MSDN上的描述很好:
http://msdn.microsoft.com/en-us/library/bb447673.aspx
關於字型授權的問題咱們就不糾結了,提醒一句,使用某種字型前首先確認是否能夠使用、再分發。繪製字型的第一步是,建立Sprite Font字型。XNA中使用的字型檔叫做Sprite Font,副檔名為.spritefont,XNA支援從.ttf將字型轉換為.spritefont。
首先,我們在VS 2010的Solution Explorer中找到WindowsPhoneGame1Content項目,右鍵菜單點擊“Add”-“New Folder”,將新檔案夾命名為Font,然後在Font上右鍵點擊,選擇“Add”-“New Item”,然後在對話方塊中選擇建立“Sprite Font”,將字型檔命名為StartFont。
在Solution Explorer中雙擊StartFont.spritefont檔案,我們會開啟一個XML檔案,我們省去XML注釋部分:
<?xml version="1.0" encoding="utf-8"?><XnaContent xmlns:Graphics="Microsoft.Xna.Framework.Content.Pipeline.Graphics"> <Asset Type="Graphics:FontDescription"> <FontName>Kootenay</FontName> <Size>30</Size> <Spacing>0</Spacing> <UseKerning>true</UseKerning> <Style>Regular</Style> <CharacterRegions> <CharacterRegion> <Start> </Start> <End>~</End> </CharacterRegion> </CharacterRegions> </Asset></XnaContent>
按照XML的注釋,我們可以很容易的瞭解每一項的功能,只看高亮部分:FontName,字型的名稱;Size,字型的大小;Style,指定字型是否為粗體、斜體等;CharacterRegion,字型區間,目前的設定為只顯示ASCII字型。這一點也是非常適合遊戲開發的,遊戲沒有必要提供完整的字元集支援。
接下來就是繪製代碼了,首先在類中增加SpriteFont的變數:
public class Game1 : Microsoft.Xna.Framework.Game { GraphicsDeviceManager graphics; SpriteBatch spriteBatch; SpriteFont StartFont; SpriteFont YaheiFont; static string Text = "";
我們還增加了一個Text,可以用這個變數從SIP軟鍵盤中擷取使用者輸入的字串。然後是LoadContent函數:
/// </summary> protected override void LoadContent() { // Create a new SpriteBatch, which can be used to draw textures. spriteBatch = new SpriteBatch(GraphicsDevice); // TODO: use this.Content to load your game content here StartFont = Content.Load<SpriteFont>(@"Font\StartFont"); YaheiFont = Content.Load<SpriteFont>(@"Font\Yahei"); }
請大家注意字型檔的路徑:將Content資源放到另外一個DLL裡可以方便遊戲替換資源,而路徑方面,只需要將Folder指定對就可以了。這裡順便把中文微軟雅黑字型也加了上了。因為要擷取SIP的輸入,所以還要修改 Update方法:
protected override void Update(GameTime gameTime) { // Allows the game to exit if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed) this.Exit(); // TODO: Add your update logic here if (Text == "" && !Guide.IsVisible) Guide.BeginShowKeyboardInput(PlayerIndex.One, "Here's your Keyboard", "Type something...", "", new AsyncCallback(GetTypedChars), null); base.Update(gameTime); } private static void GetTypedChars(IAsyncResult asynchronousResult) { Text = Guide.EndShowKeyboardInput(asynchronousResult); Debug.WriteLine(Text); }
我們修改了update方法,只有Text為空白時,SIP才會彈出,SIP部分的代碼上次已經說過了。最後一部分就是繪製Draw函數了:
protected override void Draw(GameTime gameTime) { GraphicsDevice.Clear(Color.White); // TODO: Add your drawing code here spriteBatch.Begin(); spriteBatch.DrawString(StartFont, Text, new Vector2(10, 10), Color.Black); //spriteBatch.DrawString(StartFont, "中國", new Vector2(10, 50), Color.Black); spriteBatch.End(); base.Draw(gameTime); }
運行程式,會首先實現一個IME對話方塊,輸入”Hello,xna”之後,會顯示下面的介面:
大家注意到,我將第二個繪製“中國”的DrawString注釋掉了,如果不注釋掉會怎麼樣呢?產生一個Exception,因為我們Sprite Font的CharacterRegion只包含了ASCII字元,所以,中文字型顯然超過了字元範圍。
添加中文支援
MSDN上的另一篇文章描述了這個問題:
http://msdn.microsoft.com/en-us/library/bb447751.aspx
我們可以Font Description Processor來添加對於指定字元的支援,而不需要擴大CharacterRegions,讓很多無用的字元也被增加到字型檔中來。
首先,我們在Solution Explorer中找到遊戲的Project,在本例中,就是WindowsPhoneGame1,右鍵菜單“Add”-“New Item”,選擇“Text File”,命名為messages.txt。雙擊開啟messages.txt,在裡邊添加遊戲中要支援的所有中文字元。因為要使用File.ReadAllText,所以確保文字檔是以’\r’或’\n’結尾。
接下來要建立一個新的Content Processor Project,在Solution Explorer中選擇Solution,右鍵點擊”Add”-“New Project”,選擇”Content Pipeline Extension Library(4.0)”,命名為FontProcessor。下面是ContentProcessor1.cs中修改後的所有代碼:
using System;using System.Collections.Generic;using System.Linq;using Microsoft.Xna.Framework;using Microsoft.Xna.Framework.Graphics;using Microsoft.Xna.Framework.Content.Pipeline;using Microsoft.Xna.Framework.Content.Pipeline.Graphics;using Microsoft.Xna.Framework.Content.Pipeline.Processors;using System.IO;using System.ComponentModel;namespace FontProcessor{ /// <summary> /// This class will be instantiated by the XNA Framework Content Pipeline /// to apply custom processing to content data, converting an object of /// type TInput to TOutput. The input and output types may be the same if /// the processor wishes to alter data without changing its type. /// /// This should be part of a Content Pipeline Extension Library project. /// /// TODO: change the ContentProcessor attribute to specify the correct /// display name for this processor. /// </summary> [ContentProcessor(DisplayName = "FontProcessor.ContentProcessor1")] public class ContentProcessor1 : FontDescriptionProcessor { public override SpriteFontContent Process(FontDescription input, ContentProcessorContext context) { string fullPath = Path.GetFullPath(MessageFile); context.AddDependency(fullPath); string letters = File.ReadAllText(fullPath, System.Text.Encoding.UTF8); foreach (char c in letters) { input.Characters.Add(c); } return base.Process(input, context); } [DefaultValue("messages.txt")] [DisplayName("Message File")] [Description("The characters in this file will be automatically added to the font.")] public string MessageFile { get { return messageFile; } set { messageFile = value; } } private string messageFile = @"..\WindowsPhoneGame1\messages.txt"; }}
首先,增加兩個引用,用於讀取檔案:
using System.IO;using System.ComponentModel;
然後增加MessageFile的屬性:
[DefaultValue("messages.txt")] [DisplayName("Message File")] [Description("The characters in this file will be automatically added to the font.")] public string MessageFile { get { return messageFile; } set { messageFile = value; } } private string messageFile = @"..\WindowsPhoneGame1\messages.txt";
請注意其中的檔案路徑,因為檔案包含在WindowsPhoneGame1的目錄中,而本工程位於FontProcessor目錄中,所以我們要修改其路徑,否則會出現檔案無法找到的編譯錯誤。因為FontProcessor是在編譯時間使用的,所以Excepiton都是以編譯錯誤展現出來的。
我們還需要將ContentProcessor1的基類ContentProcessor替換為FontDescriptionProcessor。為messages.txt註冊Content Pipeline,增加依賴關係,告訴Content Pipeline,如果messages.txt變化,則字型需要重新編譯。最後是讀取這個檔案,為其中的每一個字元增加字型的支援。另外,確保你的messages.txt檔案,採用了UTF-8的編碼方式。
完成這些之後,我們要首先編譯一下FontProcessor,然後在Solution Explorer中,右鍵點擊WindowsPhoneGame1Content的References目錄,選擇“Add references”,在Project Tab頁中,選擇FontProcessor。接下來,在Solution Explorer中,右鍵點擊Project Dependencies,將FontProcessor前的CheckBox選中。
然後,建立一個新的Sprite Font字型,叫做YaheiFont,字型名稱為“Microsoft Yahei”,選中yahei.spritefont,在屬性頁面中的Content Processor項中,將“Sprite Font Description - XNA Framework”切換為“FontProcessor.ContentProcessor1”。
最後,在遊戲中增加雅黑字型,將Game中的繪製函數改為:
protected override void Draw(GameTime gameTime) { GraphicsDevice.Clear(Color.White); // TODO: Add your drawing code here spriteBatch.Begin(); spriteBatch.DrawString(StartFont, Text, new Vector2(10, 10), Color.Black); spriteBatch.DrawString(YaheiFont, "中國", new Vector2(10, 50), Color.Black); spriteBatch.End(); base.Draw(gameTime); }
最後的效果就是:(向毛主席保證,這不是貼圖!)
相關資源
馬寧的Windows Phone 7開發教程(1)——Windows Phone開發工具初體驗
馬寧的Windows Phone 7開發教程(2)——Windows Phone XNA 4.0 3D遊戲開發
馬寧的Windows Phone 7開發教程(3)——XNA下使用MessageBox和軟鍵盤