久しぶりの更新です。大丈夫、まだ生きてます。
電光掲示板なんかでよくある、長さが足りなくてテキストが右から左へ流れるアレをUMGで作ってみました。ただ流れるだけではなく、端っこをフェードさせます。
最近見なくなりましたが、HTML界ではマーキーというタグがあったの思い出しました。
まずはマテリアルから。
ちょっと前にヒストリアさんのブログでも紹介されてました ScreenPosition というのを使います。
ScreenPositionには ”ViewportUV” と ”SceneTextureUV” の2つの設定値があります。
公式のドキュメントには大した情報が載ってなくて、Tipsテキスト見ながら試してみたのですが、表示に差がありませんでした。
画面いっぱいサイズのWidgetに、試しに下のようなマテリアルをセットしてみると、
U(水平)方向にグラデーションが現れます。
ちゃんと調べて検証しないといけないですが、今回はとりあえず ViewportUV にして使ってみることにします。
Final Color に 白(Constant 3 Vector)をつないでいるのは、後からWidgetで好きなカラーにセットするためです。
というわけで、さっそく今回のスクロールテキスト用のマテリアルを用意します。
キャンバスには、Horizontal Box をひとつ置きます。ブループリントから触るので isVariable にチェックを付けておきます。
青い帯は、テキストを読みやすくするための下敷きです。
配置したHorizontal Box を動かすので、あらかじめ準備として Vector2D 型の変数を2つ用意してHorizontal Boxの初期位置を入れておきます。1つはポジションをリセットするのに使います。
新幹線の車内ドアの上にある表示では、いくつかのニュースがつながって流れるので、同じように複数のテキストを一つにまとめる関数を用意しました。
Text型の配列で結合する方法を見つけられなかったので、いったんString型のローカル変数に格納します。 ForEachLoopでText配列から一つずつ取り出して、String型にキャスト(型変換)しながら格納していきます。
Text型の配列を全て、String型にして格納できたら、Join String Array ノードで一つにします。このとき間に挟み込む文字列(Separator)を指定できるので適当な記号やら文字を指定します。
関数内だけで完結して、関数の外に特に再利用されない変数はローカル変数にしておくといいです。ローカル変数にしておけば、この関数の処理が終わった後に内容が捨てられるので安全だからです。
今回テキストのみですが、将来的にテキスト以外のもの(アイコンとか)も一緒にスクロールさせることを考えて、テキストブロックは動的に作ります。「動的に」というのは「その都度、必要に応じて」みたいなニュアンスです。
ブループリントから動的にいろんなオブジェクトを生みだすために
Construct Object from Class というノードを使います。
取り出すと Construct NONE というノード名になるのでご注意を。
NONEの部分は Class によって自動的に変化します。下はTextBlockを設定した場合。
TextBlockをブループリントで動的に生成するので、普段UMGエディタで設定していたものもブループリントから設定することになります。
そこで、フォントの設定をする関数を用意します。関数の 引数(Inputs)にTextBlock を設定します。
テキストブロックに対しての設定を好きなだけ追加します。
ここでは「色」と「フォント情報」をセットしています。テキストブロックでは、フォントの情報としてマテリアルを渡すことができるので、Make SlateFontInfo ノードの Font Material のところに 用意しておいた Screen Position を仕込んだマテリアルをセットします。
次にテキストをセットする関数を用意します。
ちょっと長いので左右に分けました。
テキストを流し込んだら、キャンバスに置いておいたHorizontal Box の子供として追加します。
右端のBoolean型の変数は、スクロール開始のためのフラグです。この関数が呼ばれたらテキストがセットされて画面に表示されるので、このタイミングでフラグを True にしています。
だいたい必要な材料が揃ったので、スクロールする処理を作ります。
EventGraph の EventTick を使います
これも長いので3つのパートに分けました。
EventTickはViewportに置かれたタイミング(Add to Viewport)で動き始めてしまうので、まずはスクロールするかしないかの判定をします。
Float型の変数を用意して、Delta Time を自身に加算していきます。
これは テキストが画面に表れていきなりスクロールすると読めないので、2秒待つようにするための待ち時間をチェックする変数です。
フラグが True になって、さらに2秒経ったら、スクロールし始めます。
EventTick によって、毎フレームこのノードたちが処理されるので、座標を加算してはHorizontalBoxのポジションを更新していきます。
テキストの長さ(=HorizontalBoxのサイズ)分のスクロールが終わって画面から消えると、ポジションをリセットします。そのためにちょっと変な計算をしています。
例えば、テキストの長さが 1000 だった場合、ポジションが -1000 になると画面から消えることになります。ここで問題になるのが、ポジションはマイナスの値。サイズはプラスの値ということです。符号を変える方法はいくつかあります。マイナスの値は ABSノードや -1 を掛けたりしてマイナスを打ち消すことができます。そこでポジションと長さを比較して判定すればいいのです。でも今回は足し算で済ませています。毎フレーム高速に処理するためには省略できるものはしましょう。
文字のサイズ的にピッタリ 0 にはほぼならないので、0以下のマイナスの値なったらスクロール終了ということにしています。
スクロールが終了したら、変数の値とポジションをリセットして次に備えます。
これでWidgetは完成です。
実際に表示を確認してみます。
レベルブループリントに表示する仕組みを用意します。
いつもの Create Widget ノード から、Add to Viewport へのコンボ。
そこに、あとから中の関数を呼ぶためにReturn Value を変数化してものと、表示位置と表示サイズを設定するノードをつなげます。
仕上げに、テキストを渡す部分。
とりあえずスペースキーを押すと表示されるようにしました。
完成です。
GIFにしてみました。コマ数が少ないので滑らかじゃないけど。
これを商品レベルにするには、まだまだ必要な仕様やエラー対策なんかがありますが、ちょっとしたプロトタイプなら十分かなと思うのですがいかがでしょう。
ではでは
UI開発者の紳士淑女のみなさま
ステキなスクロールテキストライフを!
ついでに UIネタも募集中!