
【C++】UE5でテキストのはみ出し検出をしてみよう!
こんにちは。プログラマのAです。
今回は、Unreal Engine 5(以下UE5)を使って、WidgetBlueprint内にあるテキストブロックが、テキストを入れた際にはみ出していないかチェックを行いたいと思います。
UIの表示枠からテキストがはみ出す……
ゲーム開発、あるいはUIに携わる仕事をしていると、次のような不具合を一度は目にしたことがあるのではないでしょうか?

そう、「テキストのはみ出し」です。
今回の画像のように、実装時の想定よりも多い文字数が入力された結果、デザインが崩れて悲しい見た目になってしまいます。この問題は、翻訳の切替によって文字数が超えてしまった、フォントの設定がきちんと設定されていなかったなど、さまざまな理理由から発生してしまう不具合でもあります。仕様変更等によって入るテキストが変わってしまう、などもありますね。
このような状態ではクオリティの低い作品だと評価されてしまう可能性があります。かといってメニューに入るテキストを全部確認する、となると、ゲーム内を隈なく、全言語で確認するには膨大な手間がかかります。
そこで今回は、UE5 のWidgetBlueprintに使用して、簡易的にテキストのはみ出しがないかを確認するツールを作成してみたいと思います。なお、今回利用するUE5のバージョンは5.4.3になります。
いざ、テキストはみ出し検出
それではテキストのはみ出しの検出を行っていきます。まずはみ出し検出のテストをするために、以下のテスト用ウィジェットを用意しました。
CanvasPanelの下に、フキダシの画像と、テキストを表示するテキストブロックが設定されたシンプルなものです。テキストブロックはサイズ(画像緑枠)が指定されていて、このサイズ内に収まって入ればフキダシ内に問題なく収まるようになっているのですが、1行目の文字数が多すぎてテキストがはみ出してしまっています。まずはこちらの検出を行っていきましょう。
テキストブロックのサイズについては、親となるウィジェットの種類によって異なります。デフォルトではCanvasPanelになっており、そのままサイズを設定することができますが、親がBorder等に設定している場合は、テキストブロック側では直接設定できず、親ウィジェット依存になるため、そのままサイズの取得ができません。
そのため、はみ出しているかの判定は下記2点を元に行っていきます。
テキストブロックで設定されているサイズに収まっているか
テキストブロックが配置されている親ウィジェットで設定されているサイズに収まっているか
また、実際に描画されているテキストブロックのサイズと、設定されているサイズを比較することではみ出しの有無を検出します。実際の描画サイズを元に検出を行うので、チェック対象のWidgetを作成する必要があります。
そのため今回は、EditorUtilityWidetを使用して、WidgetBlueprintクラスを選択して起動できるツールになるように作成しました。
設定されているサイズに収まっているか
まず、テキストブロックで設定されているサイズに収まっているかの判定について考えます。
テキストブロックに限らず、ウィジェットには、UPanelSlotという、レイアウトを簡単にインライン編集するためのスロットが存在します。CanvasPanel, Horizontal Box, ScaleBox等がそれにあたります。
テキストブロックは、デフォルトでCanvasPalelSlotが設定されており、CanvasPanelSlotにはサイズや座標などを設定するためのパラメータが存在します。
そのため、CanvasPanelSlotが親スロットに設定されている場合は、ここに設定されている値を元に判定を行います。

検出を行う際の、具体的なフローは以下になります
チェック対象のWidgetを作成する
作成したWidgetから、配置されているテキストブロックを抽出する
抽出されたテキストブロックの「描画サイズ」「設定されているCanvasPanelSlotのサイズ」を取得し、比較する
結果を出力する
また、ざっくりですがコードも記述してみました。
void CheckerClass::Exe()
{
// 1. チェック対象のWidgetを生成
UUserWidget* CheckWidget = Cast<UUserWidget>(CreateWidget(this, 【CheckWidgetClassPath】));
// 2. テキストブロックを検出
// 2-1. 親のパネルウィジェットから、子ウィジェット一覧を取得
UPanelWidget* PanelWidget = Cast<UPanelWidget>(CheckWidget->GetRootWidget());
for(UWidget* ChildWidget : PanelWidget->GetGetAllChildren())
{
// 2-2. 子ウィジェットからテキストブロックを取得
if(UTextBlock* TextBlockWidget = Cast<UTextBlock>(ChildWidget))
{
// 3. 実際の描画サイズと、CanvasPanelSlotのサイズを取得し比較
// 3-1. 実際の描画サイズ
TextBlockWidget->ForceLayoutPrepass();
FVector2D DrawingSize = TextBlockWidget->GetDesiredSize();
// 3-2. CanvasPanelSlotのサイズ
UCanvasPanelSlot* CanvasPanelSlot = Cast<UCanvasPanelSlot>(TextBlockWidget->Slot);
FVector2D PanelSize = CanvasPanelSlot->GetSize();
// 3-3. 比較
if (DrawingSize.X > PanelSize.X || DrawingSize.Y > PanelSize.Y)
{
// 4. ログを出力
}
}
}
実際には、nullチェックであったり、CanvasPanelSlotのAutoSize(親のWidgetに合わせてサイズが拡縮するフラグ)がTrueの時はスキップする、等の処理がさらに入りますが、おおむね上記の流れで検出できるかと思います。
今回のテストウィジェットでは以下のような値が取得され、問題なく検出ができました。(小数点以下省略)
DrawingSize = (1056, 82), PanelSize = (830, 210)
今回使用したテストウィジェットのように、CanvasPanelの下に直接おかれたテキストブロックについてはこの方法で問題なく検出できました。
配置されている親ウィジェットで設定されているサイズに収まっているか
上記までの実装だと、テキストブロックのSlotがCanvasPanelSlotの時しか検出することができません。
CanvasPanelの直下ではなく、OverlayやBorderが親に配置されている場合は UCanvasPanelSlot のキャストに失敗してしまうためです。

そのため、UCanvasPanelSlotのCastに失敗した場合は、テキストブロック自体が配置されている親パネルの描画サイズをチェックするように処理を修正しましょう。
// テキストブロックが配置されている親ウィジェットを取得
UWidget* RootWidget = Cast<UWidget>(TextBlockWidget->Slot->Parent);
// 親ウィジェットから、描画されているSlateWidgetを取得する
if(SWidget* SlateWidget = RootWidget->GetCachedWidget().Get())
{
// 最後にSlateWidgetが描画されたときの情報を取得
FSlateWidgetPersistentState WidgetState = SlateWidget->GetPersistentState();
return WidgetState.AllottedGeometry.Size;
}
こちらの処理を追加することで、画像のようにBorder等の親ウィジェットが設定されているテキストブロックに対して、親ウィジェットそのものの描画サイズを元にはみ出し検出できるようになります。これで、テキストブロックのはみ出し検出を行えるようになりました。
実装したはみ出し検出機能をツールにするには
ここまでで、テキストブロックのはみ出し検出は行うことができました。あくまで検出までのフローのみなので、ここからツールとして使えるようにするまでには、まだまだ機能が不足しています。
以下に私が考える必要機能等を記載しておきますので、もし同様のツール作成を検討している方がいらっしゃれば、参考にしていただければと思います。
RichTextBlockの検出
ウィジェット内に配置されている子ウィジェットの中にあるテキストブロックの検出
複数のテキスト、ウィジェットをまとめてチェックする機能
エラーログの出力や、エラーが出たテキストのプレビュー機能
X方向、Y方向それぞれの検出結果の識別
その他機能など……
最後に
いかがでしたでしょうか?今回は、UE5におけるテキストのはみ出しチェックを行う手法について検討し、実装してみました。このようなツールを実装することで、QA効率や開発効率の向上が期待できると思います。
皆さんもより便利で、開発に役立つツールの作成をしてみてはいかがでしょうか。
※本記事はトイロジックのゲーム開発技術ブログ「トイログ」からの転載です。他にも記事を読みたい方はぜひトイログをチェックしてみてください!
トイロジックでは現在、一緒に働くプログラマーを募集しています。
不明点などもお気軽にお問い合わせください。ご応募お待ちしております!