みつまめ杏仁

アンリアルエンジン(UE4)でGUIを作るためにゴニョゴニョしてます。UIデザイナーの皆様の助けになれば幸いです。

チャットUIを作る

5

 外に出たら雨だった。昼休み時間であちこちのオフィスからこぼれ出るように出てきたスーツ姿の人々で、通りは賑やかになる。歩行者用信号待ちでひしめく傘を避けつつ書店へと向かう。ネットで発売日を調べれば無駄足にならないことは分かっているけど、なんとなく発見があるような気がして定期的に通っている。

 店に着いて、最初にPC関連の棚へと向かう。相変わらずアンリアルエンジン関連の書籍は少ない。いつもと同じ顔ぶれだ。技術書などは勢力の移り変わりが激しいので見ていて飽きない。棚の許容量は変わらないので、新しいものが出てくると何かがひっそりと姿を消しているということになる。書棚のタイムラプス動画があったら面白そうだ。ひととおり巡って最後に文庫の棚へ向かう。途中の棚でも、何かステキなゲームのネタやらデザインのヒントになりそうなものに出会えれるかもしれないので、見るとはなしに見る。タイトルや帯の惹句なんかも目に飛び込んでくるものは、それなりに計算して作られているように思う。並べられ方や観察するだけでトレンドが見えてくるようで、本屋は面白い。今日はこれといって収穫はなかったが散歩としては十分満足できた。

 書店を出ると雨は相変わらず降っていたが出てきた時より空が明るい。帰るまでに止むかもしれない。

 フロアに戻ってくると、コンビニで買ってきたおにぎりを食べながらアンリアルエンジンを触る。時折カードゲームをプレイしているグループから叫び声が聞こえてくる。谷山田もそこに混じっているようだ。

 

 えっと、どこまで作ってたかな。フキダシのサイズを送り返してもらうところまでだったから、次はスクロールする部分だな。とりあえず動かすということはポジションを管理することになるから変数を用意する。座標を扱うので基本的にFloat型だ。

 アニメーションさせたいから、目的地と現在地用の2つ。

f:id:hiyokosabrey:20190604222131p:plain

 いつも気になるのが、この単語の間のスペース。実際の変数名には半角のスペースは入っていない。

f:id:hiyokosabrey:20190604222259p:plain

 小文字で始めても勝手に大文字始まりにしてくれさえもする。エンジニア文化に馴染みが無いメンバーでも読みやすく、扱いやすくなるようにという意図で設計されているのだろうか。逆にスペースが入ってない方が気持ち悪いという声が多いとか?それなりに手間を掛けてるはず。ぜひともアンリアルエンジンを作っているスタッフの話が聞けるなら聞いてみたい。公式のブログとかで連載してくれないかな。ぜひ日本語で。

 

 さっそくこの変数をつないでいく。まずは値を受け取って加算するところ。

f:id:hiyokosabrey:20190604224540p:plain

 Print String ノードを置いていたところにつなぐ。フキダシからこのイベント経由で値が送られてくるので、そのたびに目的地用の変数に加算していく。フキダシが追加されるたびに目的地は先へ先へと進んでいくことになる。

 一方、現在地用の変数はEvent Tick で頑張ることになる。

f:id:hiyokosabrey:20190604225259p:plain

  目的地から現在地を引いて、その距離の何%か を現在地に加算することで徐々に目的地に近づいていくやつ。近づくにつれて減速するような動きになる。ブログ『みつまめ杏仁』で何度か見たと思う。まずは 0.125 くらいでいってみよう。方向は下から上に移動させないといけない。0 から引き算するとマイナスの値にできるから、これを VerticalBox の Translation にセットしてやる。

 あとは、VerticalBox の位置を画面の下に配置しなおす。

 アンカーを変更して、

f:id:hiyokosabrey:20190604231155p:plain

 Position Y を 0 にすればいい。

f:id:hiyokosabrey:20190604230918p:plain

 

 よし、再生してみるか。あ、ついでにフキダシの文言も変えよう。

f:id:hiyokosabrey:20190604234802g:plain

 なんか徐々にズレてきてない?計算おかしかったかな?

f:id:hiyokosabrey:20190604235611p:plain

 あ、Set Padding というか Margin の分を足すの忘れてた。

f:id:hiyokosabrey:20190605000350p:plain

 これでどうかな?

f:id:hiyokosabrey:20190605004541g:plain

 なかなかいい感じ。

 

 ただこのままフキダシが足され続けると重くなりそうだから対策を考えないといけないな。今回巻き戻さなくていいので、スクロールアウトした部分は要らなくなる。ListView を使えばいいのか?確か詳しく書かれた記事があったはず。あったこれだ。

[UE4]UE4.20で追加されたListViewウィジェットについて|株式会社ヒストリア

アニメーションはうまくいくかな。さっそく試してみよう。

 

 ふむ。まずはListVew の 中身にあたるのが フキダシWidget だから、こっちに Interface を追加するのか。UserObjectListEntryを探して・・・、あった。

f:id:hiyokosabrey:20190609011023p:plain

 次は?

 

インターフェースのイベントであるEventOnListItemObjectSetを実装します。

 

 とあるな。こんな感じか?

f:id:hiyokosabrey:20190609223210p:plain

 ブログではEntryWidgetが使い回され、みたいなことが書いてあるけど、

 

このイベントは、ListViewのスクロールによって使い回されるウィジェットの内容を更新するときに呼び出されるイベントです。更新する項目の情報を保持するオブジェクトが引数として与えられるので、そのオブジェクトに基づいてウィジェットの内容を更新する処理を実装します。

 

 そうか。ListViewの仕組みが何となく見えてきた。たぶんこういうことだろう。

 見えている部分のEntryWidget はスクロールアウトして消えるのではなく、EntryWidgetそのものは必要最小限のぶんが画面に残り続けている。そして中身であるところの “内容” だけが、どこかにリスト的に格納されていて、適宜見えているWidgetを選んで再セットされる。このとき再セットの指示をもらって “内容” を受け取るのが Event On List Item Obeject Set ということだ。だからこのイベントが無いと中身が更新されない。

 となると、フキダシWidgetと中身は1対1でない可能性がでてくるな。 なんだか今回のチャットUIには向いていない気がしてきた。とりあえず動くところを見てからにしよう。

 よし、このイベントを外して実験だ。接続を切っておこう。

f:id:hiyokosabrey:20190609232047p:plain

 フキダシWidgetは一旦編集終了だな。

 

 次は表示側のレイアウトWidget。ListView を追加してと。とりあえず様子を見るために、キャンバスの真ん中に置いておく。

f:id:hiyokosabrey:20190609232633p:plain

 

 List Entries のとこに、リストの中に並べる Widget Class だから フキダシWidgetをセットする。あれ?プルダウンに何もないな。となるとアセットから矢印ボタンでセットできないか試してみよう。

f:id:hiyokosabrey:20190609201627p:plain

 ちょっと間があったけどできたっぽい。

f:id:hiyokosabrey:20190609221040p:plain

 へぇ、プレビューしてくれるのか。これで表示範囲の調整ができるな。なんという心遣い。でも今回サイズが可変だった。

 

 ここまでできたら、あとはVerticalBox との入れ替えだけだな。

 

f:id:hiyokosabrey:20190609221345p:plain

 ここは問題なさそう。

 

 次はリスト要素を追加する部分。VerticalBox に Add Child してるところだけど、どうやら Add Child ~ のノードが見当たらない。代わりに Add Item を使えばいいようだけど、やはり Return Value ピンが無いな。1対1じゃないのは確実だろう。Add Child~ という名前にしなかったのは差別化を図ったのだと思う。実際の意図はどうなんだろう。まあ、今回大して困るわけではないので、Set Padding を飛ばせばいい。ListViewの設定に Entry Spacing というのがあったし。

f:id:hiyokosabrey:20190609222026p:plain

 再生してみると。

f:id:hiyokosabrey:20190609233533j:plain

 なんと!Event Construct が動いてない? フキダシWidgetの方に Hello を追加してみよう。

f:id:hiyokosabrey:20190609234046p:plain

 

f:id:hiyokosabrey:20190609234236j:plain

 動いてる?これって、どういう・・・

 ブレイクポイント 貼って Step実行してみるか。

f:id:hiyokosabrey:20190610000813p:plain

 

 ・・・

 PrintStringノードの次でエンジンがクラッシュ。

 

 

 とりあえずイベント使ってみようか。

 

 再起動したら、BreakPoint が Disable 状態にされていた。

f:id:hiyokosabrey:20190609235548p:plain

 Removeしておこう。これは触れてはいけない闇に触れてしまったのか・・・

 

 Event Construct につないでいた内容を Event On List Item Object Set  に切り替えてみよう。

f:id:hiyokosabrey:20190610001237p:plain

 これでどうかな。

f:id:hiyokosabrey:20190610001418j:plain

 うまく表示された。中身の更新は確かに例のイベントがやってくれるみたいだけど、デリゲートが動いてない感じ。そうか、既に1対1じゃないからBindしても無駄ということか・・・。

 

 ListView もっと検証したい気もするけど、とりあえずこの辺にしておこう。まだまだ用意しないといけない機能もあるし。VerticalBox でプロトタイプを作りきって、処理が問題になるようだったら、改めてListViewで作るのを検討しよう。

 ふぅ、ちょっと休憩。

 

 いつの間にか昼休憩の時間はとっくに過ぎていた。コーヒーを買いに行こうと席を立つ。

 自動販売機の前でアイスかホットか悩んでいると、後ろから声を掛けられた。

「おつかれさまです」

 明るい声だ。

「おつかれさま」

 すかさず返す。別のプロジェクトでUIを担当している後輩の立木坂だ。

「今日はムンムンしますね」

 ムンムンって。

「相変わらず言葉のチョイスが不可解なやつだな」

 お金を入れてアイスコーヒーのボタンを探しながら言ってやる。

「雨降ってるからじゃない?」

「え、雨降ってるんですか?わたし傘持ってこなかった。やべぇ」

 ボタンを押すと氷が吐き出される音が聞こえてくる。

「そういえばアンリアルエンジン使ってるんでしたっけ?」

「うん」

「うちのプロジェクト、今どっち使うかで戦争が起こりそうなんです」

 戦争って、そんな大層な。

「どっちって?」

「ユニティかアンリアルか」

 アンリアルエンジンはハイエンドな印象からモバイル開発とは縁遠いと思われてるフシが強いが、ようやく選択肢として名が挙がるようになってきたのは嬉しい。とはいえプログラマが開発の主導権を握るプロジェクトでは、アンリアルは選ばれにくいというのも個人的な印象としてはある。プログラムを書けばいいのに何故ブループリントなんぞを触らにゃいかんのか?といった声も聞いたことがある。

 扉が開いて紙コップがライトアップされる。取り出して一口啜る。まだ氷と馴染んでなくてぬるい。

「確かモバイルタイトルだよね?」

 前にうっすら聞いていたのを思い出した。

「そうなんですよね~」

 コインを入れ お砂糖 ボタンを連打しながら彼女は答えた。そして、ホットのほうじ茶ラテのボタンを押すと、

「アンリアルに決まったら、イロイロ教えて下さいね。あんなことや、そんなことも」

 とにこやかな表情で言う。びくっと思わず体が硬くなった気がした。動揺を隠すように、

「別にいいけど、小野杉さんは?」

「あのお方はユニティ派ですね」

 出てきた紙コップをそっと取り出し、あちあちと言いながら答えた。

 

 狭い自動販売機コーナーでそんな会話を交わしていると、話し声が近づいてきたのでフロアに戻ることにする。共用スペースなので他のテナントの会社員たちも利用するのだ。立木坂と「じゃまた」と言って別れたあと、自席に向かう足が少し軽くなったような気がした。