1.前回の内容
2.目標
日本語文字列をMonoGameのTexture2Dへ変換してDrawメソッドで描画する。
3.ソース
4.説明
4.1.前回部分の変更点
4.1.1.static変数をメンバ変数へ変更
ImageFontの一部のstatic変数をメンバ変数に変更しました。
これにより、1種類のフォント、サイズしか使えなかった問題を解決しました。
private static FreeTypeLibrary _Library;
private static FreeTypeFaceFacade _FaceFacade;
private static float _Size;
private static FreeTypeLibrary FTL_Library;
private const int INT_FontSize = 12;
private FreeTypeFaceFacade _FaceFacade;
private float _Size;
4.1.2.その他細かな修正
無駄な処理とかを消したり書き直しました。
4.2. 追加したクラス
4,2,1. TextTextureクラス
Texture2Dを管理するクラス。Texture2Dは継承してないので名付けは失敗かも。
GetTextureメソッドでTexture2Dを取得して描画する。
Texture2Dオブジェクトは不要になったらDisposeが必要だが、不要になるタイミングがちょっと掴みにくい&描画毎に生成するのって違うよねっていうところからTextTextureは管理クラスという扱いになりました。
その代わり、文字列の変更も受け付けられるようになったし良いよね…?
public Texture2D GetTexture()
{
if (string.Equals(_ImageData.Value, Text) == false || Color.Equals(_PrevColor, Color) == false || _PrevViewLength != _ViewLength)
{
if (string.Equals(_ImageData.Value, Text) == false)
{
_ImageData = _Font.Render(Text);
}
_Texture?.Dispose();
_Texture = CreateTexture2D(_Device, _ImageData.ToImageData(0, _ViewLength), Color);
_PrevColor = Color;
_PrevViewLength = _ViewLength;
}
return _Texture;
}
public static Texture2D CreateTexture2D(GraphicsDevice device, ImageData image, Color color)
{
var texture = new Texture2D(device, image.Width, image.Height);
var array = new Color[image.Width * image.Height];
var clrTable = new Dictionary<int, Color>(256);
for (int i = 0; i < array.Length; i++)
{
var alpha = image.Datas[i];
if (alpha == 0) continue;
Color clr;
if (clrTable.TryGetValue(alpha, out clr) == false)
{
//clr = new Color(color, color.A * alpha / 255);
clr = Color.Multiply(color, alpha / 255f);
clrTable[alpha] = clr;
}
array[i] = clr;
}
texture.SetData(array);
return texture;
}
ちょっと納得いかないのがColorの扱い
前回の呼出メソッドでは"System.Drawing.Color"構造体、今回は"Microsoft.Xna.Framework.Color"構造体なので違うのが当然と言えばそうなのだが、なにか納得いかない。
以下が2つの比較である。
var baseColor = Color.DarkGreen;
var image = new Bitmap(data.Width, data.Height);
for (int i = 0; i < data.Width; i++)
{
for (int j = 0; j < data.Height; j++)
{
var tmp = data.GetData(i, j);
if (tmp == 0) continue;
image.SetPixel(i, j, Color.FromArgb(tmp, baseColor));
}
}
テストコードなので実行速度とかは気にしないで欲しい。
それよりも、System.Drawing.ColorではColor.FromArgb(int, color)を呼んでいるところに注目して欲しい。
ここではベースカラーに対してAlpha値を変更する方法で色を決定している。
var clrTable = new Dictionary<int, Color>(256);
for (int i = 0; i < array.Length; i++)
{
var alpha = image.Datas[i];
if (alpha == 0) continue;
Color clr;
if (clrTable.TryGetValue(alpha, out clr) == false)
{
//clr = new Color(color, color.A * alpha / 255);
clr = Color.Multiply(color, alpha / 255f);
clrTable[alpha] = clr;
}
array[i] = clr;
}
そしてこちらはMicrosoft.Xna.Framework.Colorの場合だ。
Color.Multiply(Color, int)を呼んでいる。そう乗算しているのだ!!
これには理由があって、比較的System.Drawing.Colorに近いnew Color(Color, int)との実行結果の比較画像が以下のとおりです。
■new Color(Color, int)の場合
■Color.Multiply(Color, int)の場合
………なんでこうなるんでしょうね?
MonoGameさんは何か余計なことしてるのかな?調べてないのでわからないよ。
まぁとにかく挙動が違うので注意が必要。
4.2.2. TextTextureFactoryクラス
TextTextureを作成するクラス
Textureを生成するためにGraphicsDeviceが必要だったり、ImageFontの生成を一元化するために作成。
5.呼び出し側ソース
TextTexture textTexture; // メンバ変数
// (………省略)
protected override void LoadContent()
{
_spriteBatch = new SpriteBatch(GraphicsDevice);
// TODO: use this.Content to load your game content here
TextTextureFactory.Parent = this;
TextTextureFactory.LoadFont("Default", @"JF-Dot-MPlusH12.ttf", 24);
textTexture = TextTextureFactory.Create("Default", "日本語でおk", Color.Violet);
}
// (………省略)
protected override void Draw(GameTime gameTime)
{
GraphicsDevice.Clear(Color.CornflowerBlue);
// TODO: Add your drawing code here
_spriteBatch.Begin();
_spriteBatch.Draw(textTexture.GetTexture(), new Vector2(10, 40), Color.White);
_spriteBatch.End();
base.Draw(gameTime);
}
6. 終わりに
とりあえず日本語表示できるようになったのでよかったですね。
これでやっとゲーム作成の第一歩…
7. 続き