テキストの描画
スプライトフォント
XNA Framework ゲームにテキストを表示する作業は、他の一般的なアプリケーションには存在しない厄介な問題をいくつか抱えています。XNA Framework では、システムにインストールされているフォントを使うのではなく、コンパイル時にフォントからスプライトを事前に生成して、実行時にはスプライトとして文字を描画します。つまり、ゲーム実行時には画像ファイルと同じようにテクスチャ化されていることになります。つまり、ビットマップフォントと同じ手法です。
これには、いくつかのメリットがあります。フォントはすべて、事前に画像化されているため、システムによって表示されるフォントが異なるといったことはなくなります。どのようなシステムでも、フォントが存在しないということはなくなります。アウトラインフォントの場合は、パラメータに従って文字を再現するためにラスタライザと呼ばれる描画処理が行われます。しかし、スプライトフォントは画像化されているため、高速に文字を表示できます。
デメリットは、実行時にピクセルを生成するのではなく、すでにピクセルかされた画像を使うため、既定のサイズから伸縮を行うと文字が荒くなってしまいます。このデメリットは、ビットマップフォントと同じです。加えて、フォントに含まれるすべての文字をスプライト化すると、それだけで途方もないデータ量になってしまいます。ゲーム内で使われる漢字を事前に洗い出し、選択しなければならないのです。当然、このような作業をするのは現実的ではありません。ラテン文字圏のゲーム開発者にとっては些細な問題かもしれませんが、漢字文化を持つ国では致命的な問題です。
コンパイル時に、表示する文字を決定できないゲームでも問題となります。使用する文字を事前に設定し、スプライト化しなければならないため、ゲーム実行時に動的に得られる文字列を描画できません。特に XNA Framework 2.0 からはネットワークやキーボード入力機能が備わったため、コンパイル時に予期できない文字列を取得できるようになりました。本書執筆時点では、この問題の根本的な解決方法はありません。
もう 1 つ、法的に複雑な問題も抱えています。アプリケーションが、システムにインストールされているフォントを利用するのは問題ありませんが、フォントをスプライト化してゲームに付属して配布すると、フォントそのものを再配布するライセンスが必要になります。そのため、ゲームを配布する場合は、再配布可能なフリーフォントなどを選択しなければなりません。
文字の描画
テキストを描画するには SpriteBatch クラスの DrawString() メソッドを使います。
public void DrawString ( SpriteFont spriteFont, string text, Vector2 position, Color color )
spriteFont パラメータに、フォントのテクスチャを提供する Microsoft.Xna.Framework.Graphics.SpriteFont クラスのオブジェクトを指定します。text パラメータには、描画するテキストの文字列を指定します。ここに指定する文字列は spriteFont パラメータに指定したスプライトフォントに含まれる文字だけで構成されなければなりません。position パラメータには、テキストを表示する座標を指定します。color パラメータには、テキストの色を指定します。
public sealed class SpriteFont
このクラスのオブジェクトは、テクスチャと同じようにコンテンツパイプラインの処理でアセットに変換したデータを ContentManager クラスの Load() メソッドから読み込むことで取得できます。
フォントの追加
SpriteFont オブジェクトを取得するには、プロジェクトのコンテンツに SpriteFont を追加します。コンテンツプロジェクトを右クリックして表示されるメニューから「追加」→「新しい項目の追加」を選択してください。
「新しい項目の追加」ダイアログが表示されるので、表示されたリスト内にある「スプライト フォント」項目を選択します。このファイルは spritefont という拡張子が付けられていますが、実際には XML 形式のテキストファイルです。
追加されたスプライトフォントを開くと、XML が表示されます。XML を編集して目的のフォントを選択し、フォントサイズや使用する文字コードの範囲などを入力しなければなりません。この場では、次のようにコードを編集します。
<?xml version="1.0" encoding="utf-8"?> <XnaContent xmlns:Graphics="Microsoft.Xna.Framework.Content.Pipeline.Graphics"> <Asset Type="Graphics:FontDescription"> <FontName>MS Pゴシック</FontName> <Size>30</Size> <Spacing>0</Spacing> <UseKerning>true</UseKerning> <Style>Regular</Style> <CharacterRegions> <CharacterRegion> <Start> </Start> <End>~</End> </CharacterRegion> </CharacterRegions> </Asset> </XnaContent>
FontName 要素に、目的のフォント名を指定します。本稿のサンプルでは、日本語の Windows 環境で標準にインストールされている「MS Pゴシック」を指定していますが、このアセットを再配布することはできません。フォントを再配布する場合は、再配布可能なライセンスのフリーフォントを用いるか、再配布可能なライセンスを購入する必要があります。
Size 要素には、フォントのサイズをポイント単位で指定します。スプライトフォントは、スケーラブルフォントではないので、特定のフォントサイズに固定しなければなりません。実行時に伸縮する方法は用意されていますが、テクスチャを伸縮するため荒くなってしまいます。
Spacing 要素は、文字の間隔をピクセル単位で指定します。
UseKerning 要素は、文字間隔を調整するカーニングを有効にするかどうかを表します。true であればカーニングを有効にし、false であれば無効にします。
Style 要素は、フォントのスタイルを設定します。この要素には、標準スタイルを表す "Regular"、太字を表す "Bold"、斜体を表す "Italic"、 または太字と斜体の両方を表す "Bold, Italic" のいずれかを指定します。
CharacterRegions 要素には、使用するフォントの範囲を指定します。この要素内には、任意の数の CharacterRegion 子要素を指定できます。CharacterRegion 要素内には、使用する文字の開始文字コードを Start 要素に、終端文字コードを End 要素に指定します。デフォルトでは、ASCII 文字の範囲が指定されているため、半角のアルファベットや数字、記号などを使えます。日本語文字を含める場合は、CharacterRegion 要素を追加して、使用する文字を指定します。
using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; public class TestGame : Game { public static void Main(string[] args) { using (Game game = new TestGame()) game.Run(); } private GraphicsDeviceManager graphicsDeviceManager; private SpriteBatch sprite; private SpriteFont font; public TestGame() { graphicsDeviceManager = new GraphicsDeviceManager(this); Content.RootDirectory = "Content"; } protected override void LoadContent() { sprite = new SpriteBatch(GraphicsDevice); font = Content.Load<SpriteFont>("TestFont"); base.LoadContent(); } protected override void Draw(GameTime gameTime) { GraphicsDevice.Clear(Color.White); sprite.Begin(); sprite.DrawString(font, "Stand by Ready!", Vector2.Zero, Color.Black); sprite.End(); base.Draw(gameTime); } }
コード2を実行すると、実行結果のようなテキストが表示されます。これで、ようやく XNA Framework 版の "Hello world." プログラムが作れたことになります。LoadContent() メソッド内で ContentManager を用いて SpriteFont オブジェクトを取得しています。この方法は、テクスチャの場合と同じです。
日本語を含める場合、使用する平仮名や片仮名、漢字などの文字コードをスプライトフォントに追加しなければなりません。文字コードには Unicode が用いられているため、フォントが対応していれば日本語以外の言語も使用できます。次のようなコードを、スプライトフォントの CharacterRegions 要素内に追加します。
<CharacterRegion> <Start>぀</Start> <End>ゟ</End> </CharacterRegion>
上記のコードは、平仮名の範囲を表しています。これで、日本語のうち平仮名を描画できるスプライトフォントを取得できます。当然、このコード範囲では漢字が含まれていないので、漢字やカタカナ、記号など、コードに対応していない文字が含まれる場合は例外となります。
上の実行結果はコード2を書き換えて日本語を表示した場合です。
テキストの演出
オーバーロードされている DrawString() メソッドを使って、テキストを回転させたり、伸縮することができます。基本的な効果はテクスチャと同じです。テキスト全体に対してテクスチャと同じように回転させたり、反転、伸縮、並べ替えといった操作ができます。このような、より高度なテキストの描画には、次の DrawString() メソッドを使います。
public void DrawString ( SpriteFont spriteFont, string text, Vector2 position, Color color, float rotation, Vector2 origin, float scale, SpriteEffects effects, float layerDepth )
public void DrawString ( SpriteFont spriteFont, string text, Vector2 position, Color color, float rotation, Vector2 origin, Vector2 scale, SpriteEffects effects, float layerDepth )
spriteFont パラメータから color パラメータまでは、前述の DrawString() メソッドと同じです。
rotation パラメータには、テキストを回転させる角度をラジアン単位で指定します。origin パラメータは、テキストの中心となる座標です。テキストの描画や回転は、この origin パラメータの座標を軸に行われます。scale パラメータは、テキストの伸縮率を表します。float 型の場合は X 軸、Y 字句の両方の同じ倍率が用いられますが、Vector2 型の場合は個別に設定できます。effects パラメータには、テキストを反転させる方向を表す SpriteEffects 列挙型の値を指定します。そして、最後の layerDepth パラメータには、テキストの深度を指定します。
using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; public class TestGame : Game { public static void Main(string[] args) { using (Game game = new TestGame()) game.Run(); } private GraphicsDeviceManager graphicsDeviceManager; private SpriteBatch sprite; private SpriteFont font; public TestGame() { graphicsDeviceManager = new GraphicsDeviceManager(this); Content.RootDirectory = "Content"; } protected override void LoadContent() { sprite = new SpriteBatch(GraphicsDevice); font = Content.Load<SpriteFont>("TestFont"); base.LoadContent(); } protected override void Draw(GameTime gameTime) { GraphicsDevice.Clear(Color.White); sprite.Begin(); sprite.DrawString( font, "Stand by Ready!", new Vector2(50, 10), Color.Black, MathHelper.ToRadians(20), Vector2.Zero, 2, SpriteEffects.None, 0 ); sprite.End(); base.Draw(gameTime); } }
コード3では、テキストを 20 度回転させ、元のフォントサイズの 2 倍に拡大して描画するように設定しています。事前に追加したスプライトフォントのサイズは 14 に設定しているため、小さなテキストで作られたテクスチャを引き伸ばして描画しています。実行結果から確認できるようにテキストが荒くなっています。このように、XNA Framework のフォントはスケーラブルフォントではないので、伸縮するとテキストが荒くなってしまいます。
行間と間隔
フォントの行間や文字の間隔といった情報は、SpriteFont クラスのオブジェクトから取得できます。ゲーム内で動的に、フォントのサイズを取得して、テキストの座標を決定したい場合に利用できます。テキストを 1 文字ずつ流れるように描画する演出などで応用できます。
フォントの行間は LineSpacing プロパティから取得できます。複数の文字列を改行して並べたい場合に利用できます。
public int LineSpacing { get; }
このプロパティは、フォントの行間をピクセル単位で返します。行間には、文字の高さに加えてわずかなスペースが含まれています。
フォントの間隔は Spacing プロパティから設定、または取得できます。この値は、Spacing 要素の値と同じです。
public float Spacing { get; set; }
Spacing プロパティは、設定も可能化です。実行時に、動的に文字の間隔を調整できます。
using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; public class TestGame : Game { public static void Main(string[] args) { using (Game game = new TestGame()) game.Run(); } private GraphicsDeviceManager graphicsDeviceManager; private SpriteBatch sprite; private SpriteFont font; public TestGame() { graphicsDeviceManager = new GraphicsDeviceManager(this); Content.RootDirectory = "Content"; } protected override void LoadContent() { sprite = new SpriteBatch(GraphicsDevice); font = Content.Load<SpriteFont>("TestFont"); base.LoadContent(); } protected override void Draw(GameTime gameTime) { GraphicsDevice.Clear(Color.White); sprite.Begin(); sprite.DrawString(font, "Stand by Ready!", Vector2.Zero, Color.Black); sprite.DrawString(font, "Barrier Jacket, set up.", new Vector2(0, font.LineSpacing), Color.Black); sprite.End(); base.Draw(gameTime); } }
コード4は、2 つの文字列を 2 行に分けて描画するプログラムです。行間を LineSpacing プロパティから取得し、2 行目の文字列の高さを実行時に決定しています。ただし、単純に改行だけが目的であれば DrawString() メソッドは改行文字 \n を認識してくれるため、改行文字を加えて文字列を連結させた方が、コードとしては簡素です。
個々の文字列の幅や高さを計測する MeasureString() メソッドも用意されています。
public Vector2 MeasureString (string text)
text パラメータには、幅と高さを調べる文字列を指定します。メソッドは、指定した文字列がテキストとして描画された時の幅と高さを返します。