最近帰りが遅くて、駅から家までの経路に田んぼがあるんですが、毎晩カエルの大合唱を聞きながら歩いて帰宅しています。 月の映る水面をチラ見しながら歩きつつ、ダンジョンメーカーに勤しんでる今日この頃です。はい、歩きスマホはダメですよ~。
さてさて、訳あってUMGのテキスト入力を試すことにしました。公式のWebドキュメントを見ていると、何やらいくつか種類がある様子。とりあえず最近のエンジンのバージョンで見てみるとこんな感じ。特に変わった様子はない。
公式ではPrimitiveの中にカテゴライズされた画像が貼ってある。いつのVerだろうか。
見た目
とりあえず、Text で Box なやつをピックアップして並べてみた。
一見すると背景有り無しと、改行ありの複数行タイプか、改行なしの1行のみのタイプ。名前的に EditableText と TextBox この2タイプに分けられている理由が分かれば話が早いんだけど、公式ではこれといって言及されてないっぽい。公式以外で触れている情報が少ない。まぁだいたい理由は想像つくけど。
実際に表示してみて試してみると、ほとんど差が分からない。大きな差があるのは、Style という設定項目を持っている TextBox(Multi-Line) 。こいつはスクロールバーも持っている。それ以外の3つは、いつもお世話になっているTextBlock と同じような Appearance を備える程度。
機能要件の差とコンポーネントとして作られ実装された時期によるものだと思うけど、さすがにこれはちょっと躊躇う。TextBox(Multi-Line)だけは、Appearance の項目スッキリしていて、見た目の調整は Style という設定カテゴリにまとめてある。
自分の作りたい見た目が実現可能かどうか、実際に試してみながら判断するしかなさそう。
ちなみに、
キャンバスに置くと、is Variable に最初からチェックが付いてて、どちらもブループリントで触れるようになるんだけど、デフォルトの名前とアイコンがこれ↓
ほとんど同じ。Box 付きは 実線で、 Box無しは 破線のアイコン。
イベント
用意されているバインドできるイベントは4種類とも OnTextChanged と OnTextCommitted の2つ。
全部置いてみたらこうなった↓
OnTextChanged~ は内容が変更されるたびに呼ばれるイベント。
OnTextCommitted~ は内容が確定(編集終了という意味合いだと思う)した際に呼ばれるイベント。このボタンからイベントを作って試してみたら、このWidgetのフォーカスが外れた際に、確定したと認識されるらしい。
変数化したオブジェクトから、 イベントを検索してみても専用のやつは上の2種類だけみたい。
Widget Event というカテゴリにある。
ところがTextBox だけは違ってて、Text Box というカテゴリが用意されてる。さすがCommonカテゴリに分類されるだけのことはあるとうことか・・・
ブループリントからも見た目をいじることができて、Get Style ノードが用意されているけれど、EditableText (Multi-Line) だけは なぜか、 Get Widget Style という名前のノードで別のカテゴリに分類されている。グラフに取り出してみるとこの通り。
こうやって比較してみると、ややこしい事この上ない。まさに 混ぜるなキケン!
というやつか。
ここまできて、今更ながら思ってたより深い沼だったことが判ってしまった。そっ閉じしてあとはEpicの猫のひとかhistoria様のブログに期待を寄せる方がいいかもしれない。などと弱音を吐いてみる。
とりあえず、このまま比較していくと、全て検証を終えた時には夏休みも過ぎ2学期が始まってしまっているかもしれないので、どれかに決めて次に進もう。
改行処理を試したいので複数行が扱える EditableText(Multi-Line) に決める。
いじる
背景があった方が大きさが分かりやすいので、テクスチャを用意。
この64x64のテクスチャを Image にセットして、Box で描画する。
とりあえず必要そうなパーツを置いてそれっぽくする。
ヒエラルキーは ↓のような状態。
今回、EditableText(Multi-Line) が主人公なのでサイズが固定。これに合わせて他のパーツを調整することになるので、背景(Image)のサイズをEditableTextと一緒にしておき、マージン設定で内側に余白をつくり、見た目の枠と距離をとるようにしています。
EditableTextのサイズが変わっても、サイズをコピペするだけなので、頭使わなくていいというメリットがあります。この辺は好みの分かれるところかもしれないですね。
改行(Wrapping)の設定もチェック。
Default Wrapping だと、単語と単語の区切りで改行してくれるけど、日本語のように半角のスペースを入れない文字列だと、意図的に改行しない限り、ず~と右にはみ出してしまう。なので、 Allow Per Character Wrapping に変更します。
ボタンをクリックしたときに実行されるOnClickedイベントをバインド。PrintString につないで確認してみる。
改行文字を置換できるか実験。Get Text ノードから PrintStringノードまでを関数にする。
文字列をゴニョゴニョするには、Text型を 一旦 String型にしてやる必要がある。関数にすると、ローカル変数が使えるから一時的にStringの処理をするにはうってつけ。
関数の中身はこんな感じ。TempStr というのがローカル変数。
Replace ノードは From に置換対象の文字列を入れて、To に 入れ替える文字列を入れて使う。Shift キーを押しながら、Enter キーを押すと、改行文字が入力できるので、From に改行文字を、 To に <> を入れて試してみる。
無事置換できた。
次は、行数に制限をかける方法を考えたい。
それっぽいノードや設定項目はなさそう。
とりあえずWidget Reflector で見てみると、
Desired Size が入力した文字によって変動しているのが判った。
内容が変更されると呼ばれるイベントでGet Desired Size ノードで調べて表示させてみる。
1行・・・52.590
2行・・・89.181 (+36.591)
3行・・・125.771 (+36.59)
4行・・・162.362 (+36.591)
5行・・・198.952 (+36.59)
6行・・・235.543 (+36.591)
いったん一つ前の内容をキャッシュしておいて、オーバーしたら、強制的にキャッシュで上書きするというのを試してみる。
どうやらDesired Size の値が遅れていることが判明。内容が変化したタイミングでは、Desired Size が更新されていない様子。Reflectorではきっちり取れてたっぽいのだけど。
ということで Force Layout Prepass を入れてみたらうまくいった。
改行文字を置換する関数を少し整えて、結果をReturnValueにして外に出すようにする。ついでに、キャッシュ用の変数も更新。関数名をFormatTextToStringに変更。
ちょっとした演出を作ってみます。
まずは、文字を削る関数を用意します。
ここでもEditableTextの内容を一旦ローカル変数に入れて、 Right Chop というノードで一文字削って また元に戻しています。
この関数を動かすために、カスタムイベントを新しく作ります。
結果を見て、引き続き実行するかしないかを分岐します。文字の数だけこのカスタムイベントが実行されます。
あとは、送るボタンを押した時に、このカスタムイベントを呼べばいいだけ。
Playしてみると。
送ってる感が出てる気がする。
ひとまずこんなとこかな。
おまけ
ビヘイビアの設定をちょっとだけ確認してみた。
■ Select All Text when Focused 初期値:false
これを有効にすると、フォーカスした際に、テキストが全選択された状態になる。
コピペさせたい時に使えそう。
■ Clear Text Selection on Focus Loss 初期値:true
選択中の文字列がハイライトされた状態を維持するかどうか。デフォルトだと、フォーカスが他に移った時点でハイライト(Selection)がクリアされる。
■ Revert Text on Escape 初期値:false
入力中のテキストを一つ前の状態に戻すかどうか。フォーカスアウトした際にテキストの内容が確定してどこかしらに保存されるようで、内容を書き換えて ESCキーを押すと、フォーカスアウトした時点に戻る。書きかけでも安心。
■ Clear Keyboard Focus on Commite 初期値:true
キーボードでのフォーカス遷移に関係してそうだけど、うまく検証できなかった
■ Allow Context Menu 初期値:true
いわゆる右クリックメニュー。有効にするかしないか。
そういえば、 Appearance の設定で気になるのがあって、まだ使い方が分かってないんだけど、あれは リッチテキスト用かな?また機会があれば触ってみたいと思います。
最後に
今回の文字入力用Widgetについてあまり記事が上がっていない件で、思いつく範囲で少し触れてみたいと思います。
使い方はそれほど難しいものでもない。
というのが、あえて記事に取り上げる意欲が湧かない要因だと思いますが、ゲームでユーザーのテキスト入力を扱うということは、とてもたくさんのリスクを作り手側が負うことになるので、慎重にならざるを得ないのが実情で、これによって扱わない開発者も多いのでは、というのも結構大きな要因かと思います。
まずこのリスクをいくつか挙げておきます。
アジア圏の文字種の多さ。これは多言語対応する場合はフォントの選択肢が現状ほぼありません。表示できない文字が出てくる。
フォントの契約によっては、ユーザーが自由に文字組を編集できるということがライセンス許諾外になることが多く、その場合追加のライセンス料が発生することがある。
差別や性的、侮辱的、ドラッグ絡みなどのNGワードの置き換え処理が大変。オンラインだと多言語での対応になるので。日々ネットスラングが生まれる現状でセンシティブな内容にどこまでシステムがフォローできるかは頭の痛い問題。置き換えをあきらめた場合は、ダイアログウィンドウで事前に告知したり、不快な文章を発信しているユーザーを報告したりキックできる仕組みが必須になります。
オンラインの場合、UGC(UserGeneratedContents)になりうるので、これもゲームの場合、CEROやESRBなど、レーティングが上がることになります。
ちょっと前に流行ったのは自由文ではなく、定型文の組みあわせによる文字列生成。
これはフォントの問題は解決できるし、事前に翻訳できるので多言語対応しやすい。
最近ではスタンプもよく見かけます。
ユーザーが文字入力できるというのはそれなりに覚悟がいるので、実装経験が少ないということだろうと、勝手に推測しています。
ではでは
今回はこの辺で
ステキな文字入力ライフを!