在XNA中,实现2D文本一般通过添加一个SpriteFont的文件,这个文件是XML格式的,用它可以来控制字符的样式大小等一些属性。

添加方法:

点击Content项目,右键——Add——New Item——选择spritefont。

添加后,默认中的内容如下:

<?xml version="1.0" encoding="utf-8"?>  <XnaContent xmlns:Graphics="Microsoft.Xna.Framework.Content.Pipeline.Graphics">    <Asset Type="Graphics:FontDescription">      <FontName>Segoe UI Mono</FontName><!--字体类型-->      <Size>14</Size><!--字体大小-->      <Spacing>0</Spacing><!--字体间距-->      <UseKerning>true</UseKerning><!--字体布局-->      <Style>Regular</Style><!--字体样式,如加粗,斜体等-->      <CharacterRegions>          <!--字体区间,表示示字符的范围,例如在此设置32到126,则为只显示ASCII字符-->        <CharacterRegion>          <Start>&#32;</Start>          <End>&#126;</End>        </CharacterRegion>      </CharacterRegions>    </Asset>  </XnaContent>

在添加spritefont文件之后,既可以像其他资源一样,通过Content.Load方法来加载到游戏中,并显示在界面上。

//定义类级别变量  SpriteFont spritefont;  //在ContenLoad函数中加载资源  spritefont = Content.Load<SpriteFont>(@"Fonts\SpriteFont");

最后在Draw()方法中绘出即可。这样便可以显示一个文本了。

这是比较通用的做法。其好处就是简便快速。但是缺点也可以看出,在上面的XML中我们需要设置一个字符范围,对于只需要显示字母或一般的字符来说设为32到126确实够用了,但是如果我们要显示汉字,那就不好办了,因为汉字有几万个,我们虽然可以设置它的范围为几万,但是这样太耗资源,而且程序的执行速度和效率也比较低。不适合。所以我们采取另一种方式:通过Font Description Processor来处理。

第一步:

我们在其他位置新建一个txt文档,写上我们的中文字符,保存。

注意:这里保存的时候需要以UFT-8的格式保存。

接着我们将这个txt文件添加游戏Content的Fonts文件夹中。由于文件在Content项目中默认XNAGameFontContent中的文件都需要进行编译,编译时会自动检测里面的文件类型,而txt文件不属于这其中任何类型,因此我们需要修改它的BuildAction属性为None。

第二步:

我们点击解决方案,添加一个新的Project类型为Content Pipeline Extension Library(4.0).

这个时候的解决方案的视图如下:

第三步:

修改ContentProcessor.cs文件(文件名称如上图所示)

首先我们需要将ContentProcessor设置为继承至 FontDescriptionProcessor:

Public  class  ContentProcessor :FontDescriptionProcessor  {…………..}

接着需要重写FontDescriptionProcessor中的SpriteFontContent Process方法。

完整代码如下所示:

 

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;    // TODO: replace these with the processor input and output types.  using System.IO;    namespace ContentPipelineExtension  {      [ContentProcessor(DisplayName = "ContentPipelineExtension.ContentProcessor")]//这个名字用于SpriteFont的Conten Process属性            public class ContentProcessor :FontDescriptionProcessor      {          string txtFilePath = @"../SpriteFontDemoContent/Fonts/SpriteFont.txt";//这里由于.txt文件存在于不同的项目文件          //所以需要先退出当前项目在进入到.txt项目。          //重写基类中的SpriteFontContent Process方法          public override SpriteFontContent Process(FontDescription input, ContentProcessorContext context)          {              string path = Path.GetFullPath(txtFilePath);//获得.txt文件的完整路径              context.AddDependency(path);              string content = File.ReadAllText(path, System.Text.Encoding.UTF8);//注意在.txt保存的时候需要保存为UTF-8的格式,因而此处也许用UFT-8来读取                foreach (char c in content)              {                  //将读取的字符存放到FontDescription中                  input.Characters.Add(c);              }              return base.Process(input, context);          }      }  }

第四步:

点击游戏的Conten解决方案即:SpriteFontDemoContent,右键选择Project Dependencies,此时会弹出如下对话框:

选中SpriteFontDemo.勾选ContentPipelineExtension. 以此添加游戏项目对内容管道扩展的依赖。

第五步:

在Fonts的文件夹中再添加一个SpriteFont类型的文件,命名为SpriteChine,添加之后点击SpriteFontChinese.spritefont文件右键,选择properties.设置Conten Processor为ContentPipelineExtension,如下图所示

下面是SpriteFontChinese.spritefont文件的XML代码:

<?xml version="1.0" encoding="utf-8"?>  <XnaContent xmlns:Graphics="Microsoft.Xna.Framework.Content.Pipeline.Graphics">    <Asset Type="Graphics:FontDescription">      <FontName>微软雅黑</FontName>      <Size>24</Size>      <Spacing>0</Spacing>      <UseKerning>true</UseKerning>      <Style>Bold</Style>      <CharacterRegions>        <CharacterRegion>          <Start>  </Start>  <!—注意这里要添加一个空格-->          <End>~</End>  <!—这里用 ~ 符号-->        </CharacterRegion>      </CharacterRegions>    </Asset>  </XnaContent>

以上的工作完成之后,我们便可以正式的写代码,来显示中文汉字了。

最后一步:

在Game1.cs中添加如下代码:

using System;  using System.Collections.Generic;  using System.Linq;  using Microsoft.Xna.Framework;  using Microsoft.Xna.Framework.Audio;  using Microsoft.Xna.Framework.Content;  using Microsoft.Xna.Framework.GamerServices;  using Microsoft.Xna.Framework.Graphics;  using Microsoft.Xna.Framework.Input;  using Microsoft.Xna.Framework.Input.Touch;  using Microsoft.Xna.Framework.Media;    namespace SpriteFontDemo  {      /// <summary>      /// This is the main type for your game      /// </summary>      public class Game1 : Microsoft.Xna.Framework.Game      {          GraphicsDeviceManager graphics;          SpriteBatch spriteBatch;          SpriteFont spritefont;//英文文本          Vector2 sfPosition;//英文文本显示位置          SpriteFont spriteChineseFont;//中文文本          Vector2 scfPosition;//中文文本显示位置          public Game1()          {              graphics = new GraphicsDeviceManager(this);              Content.RootDirectory = "Content";              graphics.PreferredBackBufferWidth = 480;              graphics.PreferredBackBufferHeight = 800;              // Frame rate is 30 fps by default for Windows Phone.              TargetElapsedTime = TimeSpan.FromTicks(333333);                // Extend battery life under lock.              InactiveSleepTime = TimeSpan.FromSeconds(1);          }            protected override void Initialize()          {              //初始化文本位置              sfPosition = new Vector2(130, 200);              scfPosition = new Vector2(160, 400);              base.Initialize();          }            /// <summary>          /// LoadContent will be called once per game and is the place to load          /// all of your content.          /// </summary>          protected override void LoadContent()          {              // Create a new SpriteBatch, which can be used to draw textures.                spriteBatch = new SpriteBatch(GraphicsDevice);  //加载文本资源              spritefont = Content.Load<SpriteFont>(@"Fonts/SpriteFont");              spriteChineseFont = Content.Load<SpriteFont>(@"Fonts/SpriteFontChinese");          }            /// <summary>          /// UnloadContent will be called once per game and is the place to unload          /// all content.          /// </summary>          protected override void UnloadContent()          {          }            /// <summary>          /// Allows the game to run logic such as updating the world,          /// checking for collisions, gathering input, and playing audio.          /// </summary>          /// <param name="gameTime">Provides a snapshot of timing values.</param>          protected override void Update(GameTime gameTime)          {              // Allows the game to exit              if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed)                  this.Exit();              base.Update(gameTime);          }            /// <summary>          /// This is called when the game should draw itself.          /// </summary>          /// <param name="gameTime">Provides a snapshot of timing values.</param>          protected override void Draw(GameTime gameTime)          {              GraphicsDevice.Clear(Color.CornflowerBlue);              spriteBatch.Begin();  //绘制字母              spriteBatch.DrawString(spritefont, "English SpriteFont", sfPosition, Color.Black);  //绘制中文              spriteBatch.DrawString(spriteChineseFont, "中文字体", scfPosition, Color.Red);              spriteBatch.End();              base.Draw(gameTime);          }      }  }

由于以上的代码没有什么新鲜的东西,就不做过多的说明了,下面是运行效果视图: