main() blog

プログラムやゲーム、旅、愛する家族について綴っていきます。

【UE5】ImGuiを日本語対応してみよう!

概要

ImGuiの日本語表示の対応方法についてです。

ImGuiはデフォルトでは日本語の表示が行えません。
ImGui側で機能はいろいろと用意されているので、プロジェクト側で対応していく必要があります。

最初はプラグイン側の変更は行わずにゲーム側で対応を検討していましたが、フォントサイズの変更などでフォントデータの再生成を行う必要もあるためプラグイン側での対応ですすめてみます。
今回はImGuiのプラグイン自体がゲーム側に組み込んでいるものなので、プロジェクトカスタマイズでの対応でも問題はないと思います。

※本記事はC++での開発を前提としています。

基本的な導入方法などは以下を参考にしていただければと思います。

www.main-function.com

動作環境

Windows11
UnrealEngine 5.4.4

フォントデータをソースに埋め込む

結論としてはこちらの対応が良さそうです。
ttfファイルの読み込みなども検討してみましたが、ソースコードにバイナリを変換したコードを埋め込むことにより、リソースの管理や読み込みなどを行う必要がなくフォントの追加を行うことができるのでパッケージ環境などでも問題が少なくなると思われます。

binary_to_compressed_c.exeを使用して、ttfファイルをソースに埋め込めるコードに変換することができます。
binary_to_compressed_c.cppを自分でビルドしても良いですし、以下のリンクのimgui-demo-binaries-20240105.zipをダウンロードし解凍するとexeが入っているのでそちらを使用することもできます。

github.com

試しにNotoSansJPを使用して日本語対応を進めてみたいと思います。
※半角も等幅の方が良いのでプロジェクトに応じて適切なフォントを選択してください

コマンドプロンプトから以下を実行します。

>binary_to_compressed_c.exe NotoSansJP-Medium.ttf NotoSansJP > NotoSansJP.cpp

実行すると以下のようなcppのファイルが出力されます。

// File: 'NotoSansJP-Medium.ttf' (5729568 bytes)
// Exported using binary_to_compressed_c.cpp
static const unsigned int NotoSansJP_compressed_size = 4127143;
static const unsigned int NotoSansJP_compressed_data[4127144/4] =
{
    0x0000bc57, 0x00000000, 0x206d5700, 0x00000400, 0x00010025, 0x82130000, 0x00042e04, 0x53414230, 0x6a57fb45, 0x020000c0, 0x081582b4, 0x4447e437, 
    :
    :
};

このソースをプロジェクトに追加します。
パスが参照できるところが良いので、今回はImGuiのプラグインのThridPartyのフォルダに入れておきます。
ここはモジュール側の設定でprivateで参照できる場所となっているので、プラグイン側の実装で完結できるのであればここに置いても問題と思われます。

ImGuiContextManager.cppのBuildFontAtlas()の中で日本語フォントを初期化してみます。
この関数はDPIScaleが変更されフォントがリビルドされた際にも呼び出されるので、このタイミングでフォントの初期化を行う必要がありそうです。
ですので、今回はプラグイン側に直接実装してみたいと思います。

圧縮時の引数のデフォルトでは圧縮がかかっているので以下の関数を使用して初期化できます。
コードのデータ配列の変数とサイズを引数で渡すことで初期化することができます。
また、日本語の文字コードの範囲の指定はFontAtlas::GetGlyphRangesJapanese()を使用して引数で指定することもできます。

ImFontAtlas::AddFontFromMemoryCompressedTTF()

以下が実装例です。
ImGuiContextManager.cppの最初の方でフォントデータのソースを直接インクルードします。
FImGuiContextManager::BuildFontAtlas()で日本語フォントの初期化を行います。
今回はAddFontDefault()でデフォルトフォントを生成していますが、いったんクリアして再生成を行っています。
このあたりはプロジェクトに併せて適宜修正を行っていただければと思います。

#include "Fonts/NotoSansJP.cpp"

 :
 :

void FImGuiContextManager::BuildFontAtlas(const TMap<FName, TSharedPtr<ImFontConfig>>& CustomFontConfigs)
{
    if (!FontAtlas.IsBuilt())
    {
        ImFontConfig FontConfig = {};
        FontConfig.SizePixels = FMath::RoundFromZero(13.f * DPIScale);
        FontAtlas.AddFontDefault(&FontConfig);

        :
        :
        :

#if 1
        // デフォルトフォントの置き換え.
        FontAtlas.Clear();
        FPlatformString::Strcpy(FontConfig.Name, sizeof(FontConfig.Name), "NotoSansJP");
        FontAtlas.AddFontFromMemoryCompressedTTF(NotoSansJP_compressed_data, NotoSansJP_compressed_size, 16.0f * DPIScale, &FontConfig, FontAtlas.GetGlyphRangesJapanese());
        FontAtlas.Build();
#endif

        unsigned char* Pixels;
        int Width, Height, Bpp;
        FontAtlas.GetTexDataAsRGBA32(&Pixels, &Width, &Height, &Bpp);

        OnFontAtlasBuilt.Broadcast();
    }
}

デフォルトフォントを残しつつ、マージで追加したい場合は以下のような記述となります。

     // マージで追加.
        FontConfig.MergeMode = true;
        FPlatformString::Strcpy(FontConfig.Name, sizeof(FontConfig.Name), "NotoSansJP");
        FontAtlas.AddFontFromMemoryCompressedTTF(NotoSansJP_compressed_data, NotoSansJP_compressed_size, 16.0f * DPIScale, &FontConfig, FontAtlas.GetGlyphRangesJapanese());

実行

日本語を含めた表示が行えていることが確認できます。

参考

qiita.com