テキスト整形#

MGLにはゲーム内で表示するテキストやメッセージを対象とした独自の整形機能を備えています。この機能には次のような特徴があります。

  • フォーマット引数配列へのインデックス指定

  • 引数配列の範囲外アクセスで不正なアドレス参照を行わない設計

  • MGL::Render::FontResourceが利用するインデックス文字列への埋め込みに対応

MGLのテキスト整形はゲーム画面に表示するものを対象として設計されており、既存のテキスト整形の代替機能として用意されているものではありません。必要に応じて適宜使い分けてください。

基本的な使い方#

テキスト整形は画面内にフォントを表示するMGL::Render::Font::Printで使用できる他、整形のみを行うMGL::Text::Formatでも利用可能です。ここでは整形のみを行うMGL::Text::Formatを利用して解説を進めていきますが、利用方法はどちらも同じです。

まず、最も基本的な文字列への数値の埋め込みの例を次に示します。

文字列への数値の埋め込みの例
constexpr int kPlayerHP = 1000;

const auto message = MGL::Text::Format("Player HP: {}", {kPlayerHP});

MGL_TRACE(message.c_str());  // MGL_TRACEはコンソールウィンドウに文字列を出力するデバッグ用マクロ
実行結果
Player HP: 1000

MGL::Text::Formatの第1引数が元となるフォーマット文字列を指定で、第2引数にフォーマット引数を指定しています。

基本的なルールとしては、フォーマット文字列の中の波括弧({})で囲まれた部分が、フォーマット引数で指定した値へと置換されます。もし波括弧そのものを表示させたい場合は" {{ "のように連続で{を記述してください。}については対応した{が無ければそのまま表示されます。

注釈

より厳密に言うと、{が置換の開始を表し、}が置換の終了を表すコードとなります。すなわち、{{によって置換の開始が抑制され、その状態では}が特別な効果を持ちません。

第2引数のフォーマット引数の指定は{...}のように波括弧で囲って指定する必要があります。これは、C言語のprintf()のような可変長引数を用いず、フォーマット引数配列を表す1つのクラスオブジェクトでの指定となるためです。第2引数の型であるMGL::Text::FormatArgsは、フォーマット引数単体を表すMGL::Text::FormatArgumentを複数保持したクラスです。{...}のように波括弧指定で複数の変数を指定する事で、その配列を引数としたMGL::Text::FormatArgsのコンストラクタの呼び出しが行われます。

フォーマット引数に値を複数指定する場合は次のようになります。

フォーマット引数を複数指定する例
constexpr const char *kPlayerName = "Player";
constexpr int kPlayerHP = 1000;

const auto message = MGL::Text::Format("{} HP: {}", {kPlayerName, kPlayerHP});

MGL_TRACE(message.c_str());
実行結果
Player HP: 1000

フォーマット引数は変数の型に応じ、文字列、整数、小数点数などの種類をある程度自動で判別します。もし何らかの型に強制したい場合はstatic_cast<>を用いてください。

フォーマット文字列中の{}の中には、置換オプションを指定できます。利用可能なオプションはいくつかありますが、ここでは一例として数値に桁区切りを付与するオプションの利用例を次に示します。

桁区切りオプションの利用例
constexpr const char *kPlayerName = "Player";
constexpr int kPlayerHP = 1000;

const auto message = MGL::Text::Format("{} HP: {,}", {kPlayerName, kPlayerHP});

MGL_TRACE(message.c_str());
実行結果
Player HP: 1,000

2つ目の置換指定の中にある「,」が桁区切りを有効にするためのオプションです。このオプションにより、数値を表示する際に適切な間隔で区切り記号を挿入します。なお、区切り記号と挿入間隔は地域によってルールが異なっており、MGL::System::Localeによってそれらの取得と変更が行えます。

その他の置換オプションについては、置換オプションを参照してください。

フォーマット引数配列へのインデックス指定#

フォーマット文字列中の置換指定には引数配列のインデックスも指定することが可能です。この機能を用いることで、置換の順序を変えたり、同じ引数を複数回使用できるようになります。

この機能は主にローカライズにおいて大きな意味を持つようになります。例えば、日本語と英語では文法が大きく異なるため、同じ意味の文でも置換すべき単語の順が異なる場合が珍しくありません。インデックス指定を用いる事で、この問題をプログラム側ではなくリソース側(メッセージデータ側)で対処できるようになります。

引数配列のインデックスを指定するには、{}の中にインデックスを数値で記述します。この指定は{の直後に記述する必要があり、他の置換オプションよりも先に指定しなければなりません。インデックスは0が先頭になります。

インデックス指定の例
constexpr const char *kNameA = "A";
constexpr const char *kNameB = "B";
constexpr const char *kNameC = "C";

const auto args = MGL::Text::FormatArgs({kNameA, kNameB, kNameC});

const auto message1 = MGL::Text::Format("Message 1: {}, {}, {}", args);
const auto message2 = MGL::Text::Format("Message 2: {2}, {0}, {1}", args);

MGL_TRACE(message1.c_str());
MGL_TRACE(message2.c_str());
実行結果
Message 1: A, B, C
Message 2: C, A, B

この例は、同一のフォーマット引数配列を利用しつつ、フォーマット文字列側で順序を変更して表示しています。

整形処理中は内部にもインデックスを保持しており、この値は置換処理を行う度にインクリメントされます。インデックス指定を省略した場合はこの内部インデックスが使用されるため、message1のように全てのインデックス指定を省略すると、引数配列の順に置換処理が行われます。

内部インデックスのインクリメントを抑制するには、「~」オプションを利用します。このオプションを利用した例を次に示します。

~」オプションの利用例
constexpr const char *kNameA = "A";
constexpr const char *kNameB = "B";
constexpr const char *kNameC = "C";

const auto args = MGL::Text::FormatArgs({kNameA, kNameB, kNameC});

const auto message3 = MGL::Text::Format("Message 3: {}, {2}, {}", args);
const auto message4 = MGL::Text::Format("Message 4: {}, {2~}, {}", args);

MGL_TRACE(message3.c_str());
MGL_TRACE(message4.c_str());
実行結果
Message 3: A, C, C
Message 4: A, C, B

message3はインクリメントを抑制していないため、{2}kNameCを置換した後にもう一度kNameCが置換されています。一方、message4はインクリメントを抑制しており、kNameBがスキップされずにkNameCの後に置換されています。

フォーマット引数配列の範囲外にアクセスした場合、"<!OOR!>"(Out Of Range)という文字列で置換が行われます。

範囲外アクセスが発生した場合の例
constexpr const char *kNameA = "A";
constexpr const char *kNameB = "B";
constexpr const char *kNameC = "C";

const auto args = MGL::Text::FormatArgs({kNameA, kNameB, kNameC});

const auto message5 = MGL::Text::Format("Message 5: {}, {}, {}, {}", args);
const auto message6 = MGL::Text::Format("Message 6: {4}", args);

MGL_TRACE(message5.c_str());
MGL_TRACE(message6.c_str());
実行結果
Message 5: A, B, C, <!OOR!>
Message 6: <!OOR!>

この保護機能により、不正なアドレスを参照するといったプロセスの動作における致命的な不具合に至ることはありません。しかし、ゲームとして正しい挙動ではない事は確実ですので、このような状況を発生させないようにしてください。

値の種類と型の関係#

MGL::Text::FormatArgumentはコンストラクタで受けた値の型から自動で値の種類を判別します。判別できる型と、その値の種類は次の通りです。

種類

符号付き整数

int8_t, int16_t, int32_t, int64_t

符号無し整数

uint8_t, uint16_t, uint32_t, uint64_t

小数点数

float, double

文字列

const char *, MGL::STL::string, MGL::File::Path, MGL::File::PathView

論理値

bool

アドレス

const char *を除くポインタ変数

整数は8ビットから64ビットまでの符号付きまたは符号なし変数を指定可能です。内部では符号の有無を区別していますが、利用する側からはこれらを区別する必要はありません。

小数点数はfloat型またはdouble型を指定可能です。ただし、小数点以下の最大桁数は7桁となっているため、どちらを指定しても精度落ちが発生します。

文字列はconst char *型の他にMGL::STL::string型、MGL::File::Path型、MGL::File::PathView型の指定が可能です。サポートしている文字エンコーディングはUTF-8に限定されます。

bool型を指定した場合は論理値となり、"TRUE"または"FALSE"の文字列で表示します。

いずれかのポインタ変数を指定した場合はアドレスとなり、そのアドレスを大文字の16進数に変換して表示します。ただし、const char *型を指定した場合は文字列として判別されるため、その場合はconst void *型にキャストしてください。

注釈

論理値とアドレスの表示はデバッグ機能の実装のために用意されています。これらをゲームに利用することは好ましくないため避けてください。

置換オプション#

置換オプションはインデックス指定の後に任意の順に指定可能です。オプションはそれぞれ1文字で表現され、引数を持つ場合はその直後に記述する必要があります。

オプション一覧#

文字

内容

引数

+

正の符号を表示

,

桁区切りを表示

<

左寄せ

桁数

>

右寄せ

桁数

f

右寄せまたは左寄せの際に埋める文字の指定

埋める文字

.

小数点以下の桁数の指定

桁数

~

自動インデックスのスキップ

x

小文字で16進数表示

X

大文字で16進数表示

:

効果なし


[ + ]:正の符号を表示#

整数または小数点数において、正の値にも符号を表示するように指定します。値がゼロである場合には符号は表示されません。

サンプル
MGL_TRACE(MGL::Text::Format("{}, {+}, {+}", {100, 100, 0}).c_str());
実行結果
100, +100, 0

[ , ]:桁区切り文字を表示#

整数または小数点数において、ロケール情報に基づいた桁区切り文字を挿入します。挿入される文字とその間隔はMGL::System::Localeにて変更・取得が可能です。

サンプル
MGL_TRACE(MGL::Text::Format("{}, {,}", {12345678, 12345678}).c_str());
実行結果
12345678, 12,345,678

[ < ]:左寄せ#

指定された桁数に基づいて左寄せを行います。引数には0から127の範囲の桁数を指定します。置換後の文字列が指定の桁数を超える場合は未指定と同じ結果になります。

空いた部分に埋める文字はデフォルトでは半角スペース(0x20)となります。この文字はfオプションで変更が可能です。

注釈

この機能は簡易的な位置合わせに利用できますが、使用しているフォントがプロポーショナルフォントの場合は望んだ結果が得られない場合があります。

サンプル
MGL_TRACE(MGL::Text::Format("[{}], [{<8}]", {1234, 1234}).c_str());
実行結果
[1234], [1234    ]

[ > ]:右寄せ#

指定された桁数に基づいて右寄せを行います。引数には0から127の範囲の桁数を指定します。置換後の文字列が指定の桁数を超える場合は未指定と同じ結果になります。

空いた部分に埋める文字はデフォルトでは半角スペース(0x20)となります。この文字はfオプションで変更が可能です。

注釈

この機能は簡易的な位置合わせに利用できますが、使用しているフォントがプロポーショナルフォントの場合は望んだ結果が得られない場合があります。

サンプル
MGL_TRACE(MGL::Text::Format("[{}], [{>8}]", {1234, 1234}).c_str());
実行結果
[1234], [    1234]

[ f ]:右寄せまたは左寄せの際に埋める文字の指定#

左寄せまたは右寄せの空いた部分に挿入する文字を指定します。引数は印字可能なASCIIコードである必要があります。

このオプションを省略した場合は半角スペース(0x20)が使用されます。

サンプル
MGL_TRACE(MGL::Text::Format("[{}], [{>8f0}]", {1234, 1234}).c_str());
実行結果
[1234], [00001234]

[ . ]:小数点以下の桁数の指定#

小数点数において、小数点以下を何桁まで表示するかを指定します。引数には0から7まで指定可能で、それより大きい値を指定した場合は7桁まで表示します。

このオプションが指定されていない場合は最大7桁までの表示となります。

サンプル
auto value = 10.0f / 3.0f;
MGL_TRACE(MGL::Text::Format("{}, {.3}, {.0}", {value, value, value}).c_str());
実行結果
3.3333332, 3.333, 3

[ ~ ]:自動インデックスのスキップ#

インデックス指定を省略した場合に使用する、内部インデックスのインクリメントを抑制します。

詳細はフォーマット引数配列へのインデックス指定にて解説していますので、こちらを参照してください。


[ x ]:小文字で16進数表示#

値が整数である場合に小文字の16進数で表示します。引数には表示桁数を指定可能で、表示結果の値が指定桁数に満たなかった場合は0を挿入します。

サンプル
MGL_TRACE(MGL::Text::Format("{}, {x}, {x4}", {1234, 1234, 1234}).c_str());
実行結果
1234, 4d2, 04d2

[ X ]:大文字で16進数表示#

値が整数である場合に大文字の16進数で表示します。引数には表示桁数を指定可能で、表示結果の値が指定桁数に満たなかった場合は0を挿入します。

サンプル
MGL_TRACE(MGL::Text::Format("{}, {X}, {4X}", {1234, 1234, 1234}).c_str());
実行結果
1234, 4D2, 04D2

[ : ]:効果なし#

このオプションは何の効果もなく、単純に無視されます。複数のオプションを併用する場合に、視認性のための区切り文字として使用することを想定しています。なお、このオプションは半角スペースでも代用可能です。