みつまめ杏仁

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

体力ゲージ的なあれこれについていろいろ

アドベントカレンダー9日目の記事になります。

 このブログでは普段アンリアルエンジンでのUI制作に関することを書いています。

 今回は久しぶりにエンジンは起動しないで、ゲームUIのネタとして、体力ゲージ的なやつを取り上げてみようと思います。

  体力ゲージ的 という曖昧な言い回しになるのは、役割が同じでも見た目が違うものも含めてしまえという、ちょっと欲張ったネタにしようと思い立ったからです。 

 

ちょっとだけ昔の話を

 さてさて、ビデオゲームが世に生まれてから、何かしらのゲージを見ないゲームはないんじゃないかと思えるくらい、当たり前のようにみかけます。ゲージの無いゲームを探す方が難しい気がします。いろんなゲージがある中でも、体力ゲージとかHPゲージ、LIFEゲージといった、ゲームの世界に生きているキャラクターが行動不能になるまでを視覚化した表現は、ゲージがなくなること=ゲームオーバーやミッション失敗などプレイのやり直しを求められることが多く、ゲージが残りわずかになると、プレイ継続不可能な状況が迫っていることを否応なしにプレイヤーに突きつけてきて、緊張感を煽る存在でもあります。黎明期のアーケードゲームは1発でも当たったら死ぬという厳しさで、その無慈悲さを緩和するために残機が増えたり、シールドゲージがついたりしました。

 パーティや部隊を組んでプレイするゲームは、ゲージが無くなっても他のメンバーが生きていたら続行不能にはならなかったりしますが、どちらにせよゲームのメカニズムにおいてかなり重要な要素として位置付けられていることは間違いないと思います。

 一方で、プレイヤーが倒さなければいけない敵などに紐づく体力ゲージは、効率のいいプレイを探るモチベーションにもなったりします。

 

 ただ長さが変わるだけの表示に、一喜一憂し、乾坤一擲の勝負に出、背水の陣で冷や汗を流し、盛者必衰を悟る体力ゲージ。 

 古今東西いろんなゲームでゲージに翻弄されるささやかな人間ドラマが繰り広げられている事実を知り、またそれを意図的にデザインしてほくそ笑むことができれば、生粋のUIデザイナーだと胸を張っていいんじゃないかと思います。

 

 ゲームのUIデザインは ゲージに始まりゲージに終わる

 

 

 この業界に長く身を置く者として、作ってきたゲージ類は数え切れません。今までたくさんゲージを作ってきた経験則を元に、ゲージをデザインする際に考慮すべきポイントを書き連ねてみました。ゲージのカタチを考える時や、企画の仕様に抜けが無いかの確認等に役立てられればと思います。

 

 ちょっと小難しく親父語りを始めてしまいましたが、そろそろ今回のネタに移ろうと思います。ここからは画像が増えますよ。

 

 

そもそも体力ゲージ的なやつって?

 体力ゲージと言ってしまうと、棒グラフ状のゲージに限定されてしまう恐れがあるので、あえてぼかした言い回しにしてみました。ゲーム内のポジションが同じなら体力ゲージでいいじゃん、というのもちょっと乱暴な気がして、これ!、というコトバが見当たらないのですが、面倒くさいのでとりあえず「体力ゲージ」と呼ぶことにします。 

 体力ゲージの見た目はさまざまですが、大きく分類すると3タイプに分けられます。

f:id:hiyokosabrey:20191202200538p:plain

 

 中でも棒ゲージタイプは、カーブしていたり、太さが均等じゃなかったりと、いろんな派生形がありますが、基本的には長さが 可変か固定か の2系統に分かれます。

 

 

 3タイプそれぞれに向き不向きがあります。

 

棒ゲージタイプ

f:id:hiyokosabrey:20191206010744p:plain


 最大値に対して今どのくらい、というのがわかるというのが最大の長所。

 可変する量が無段階なので、アナログ的な補正や調整がしやすい。

 情報量に対し表現に必要な面積が少なく済み、長さを表現できるなら、シンプルな形状から複雑なものまでデザインの幅は広くとても汎用的で扱いやすい。

 長さが固定の場合は成長して最大値が増えても比率でしか表現できないので、成長している実感は得られにくい。また、最大値がゲージのドット数より多い時、残りわずかな状態で、見た目に消えてしまうことがあるので注意が必要。

 

 

数値タイプ

f:id:hiyokosabrey:20191206010801p:plain

 ズバリの数値が表れているので、具体的な値での増減が分かりやすいのが長所。

その反面、直截すぎて桁数の表現が心理的にどう作用するかが個人差があって読みにくいのが難点。

 ウソが付けないぶん後から補正やら調整が入れにくい。

 最大値を常に表示するために、分数表記が採用されることも多い。

 文字による表現は、脳内で変換が必要になるので、棒ゲージに比べて直観的ではないため、リアルタイム系のアクションゲームでは避けた方が無難。

 また、シリーズを重ねるタイトルの場合、仕様の拡張によるインフレの懸念もある。

  

 

アイコンゲージタイプ

f:id:hiyokosabrey:20191206010822p:plain

 

 定量的に増減できるので、ダメージ量や回復量について説明するのが簡単なのが長所。

アイコンのデザインによるところが大きく、あまり大きくできないため、細やかな増減を表現するには不向き。数が増えてくると見た目にうるさくなりがち。

 体力が減った場合、仕様的に最大個数を保持するかしないかで遊び方が変わる。保持する場合は、増えること=成長 で安心感が得られ、保持しない場合は、増えること=保険、という扱いになり、補充できないと辛くなる。

 増減の方向として、リニアに変化するタイプや角度で変化するタイプなどがある。

 

 

 

 もちろん不得意な部分を補う形で3タイプを混ぜて使う、いわゆる合わせ技という表現もあります。

 

f:id:hiyokosabrey:20191208150003p:plain

 ゲージで全体の割合を、数字で今の値を示したり、ゲージのカタチが分かりにくい代わりに数字で割合を示したりというのがあります。

 

 

 デザインする

 体力ゲージをデザインに落とし込むときにあれこれ考えながらベストなカタチを模索するのですが、ゲームのメカニクスを顧みず、見た目だけでデザインすると、後で不幸に見舞われるかもしれません。遊ばせ方を事前にすり合わせておくとそれほど迷わずに済みますし、ゲージの比重が明確だとレイアウトを考えるとき少しはラクになれます。

 まずはいろんな仕様に対するゲージのふるまいや普段私自身が気を付けていることを挙げていくので、参考になれば。

 

 

成長要素があり、最大量が増える?

f:id:hiyokosabrey:20191204014312g:plain

 

 

  f:id:hiyokosabrey:20191205223658p:plain 長くする。可変できる仕組みが必要

  

  f:id:hiyokosabrey:20191205223723p:plain 桁を増やす

 

  f:id:hiyokosabrey:20191205223741p:plain アイコンの数を増やす

 

 成長要素等で伸びていくタイプ。レイアウトに余裕があればそれほど問題はない。

 ただ、余白の取り方で最大の長さを予測される恐れがあるので、ゲーム進行を気取られないようにしたいなら伸びしろの扱いに注意。

 

 

 

表示領域に制限があるけど増やしたい

f:id:hiyokosabrey:20191204235222g:plain

 

 

  f:id:hiyokosabrey:20191205223658p:plain 色で本数を表現する。細かく増やせないし気づきにくい

          カラフルになりがち。後述のバフ表現等と衝突する可能性

 

  f:id:hiyokosabrey:20191205223723p:plain 数字を小さくしたり、少し重ねたりすれば可能だけど

 

  f:id:hiyokosabrey:20191205223741p:plain サイズ違いのアイコンを用意して x5とか x10 を表現

 

 かなり無理してる感がある。どうしても後から仕様を付け足した感が出るので、成長するかどうかの『握り』=仕様確認は大事。「なんとかならない?」と言われた場合は、合わせ技である程度解決できる。レイアウトを確定させるためにも早い段階で詰めておきたい。

 

 

 

割合が分かればいい

f:id:hiyokosabrey:20191205001519p:plain

 

 

  f:id:hiyokosabrey:20191205223658p:plain 問題ない。リング(環状)にするのもいい

 

  f:id:hiyokosabrey:20191205223723p:plain パーセント表記にする

 

  f:id:hiyokosabrey:20191205223741p:plain 向いていない

 

 最大値が大きくなると変化量が小さくなるので、ゼロ付近が視認しづらくなる。ゼロになってもゲームオーバーにならないゲームでは有効。

 表示面積が小さくできるので助かる。

 

 

 

満タン時の違いは必要?

f:id:hiyokosabrey:20191205225925g:plain

 

 

  f:id:hiyokosabrey:20191205223658p:plain 色相を大きく変えなければ色替えがベスト

 

  f:id:hiyokosabrey:20191205223723p:plain 難しい

 

  f:id:hiyokosabrey:20191205223741p:plain 効果が薄い

 

 格闘ゲームのパーフェクト K.O. や、満タン時のみダメージ軽減など、発動に特別な条件がある場合は、違いをある程度分かりやすくする必要がある。

 

 

 

どんな減らせ方がいい?

f:id:hiyokosabrey:20191207094305g:plain

 

 

  f:id:hiyokosabrey:20191205223658p:plain できるだけアニメーションさせたい

 

  f:id:hiyokosabrey:20191205223723p:plain カウントダウン。アニメーションの時間に注意

 

  f:id:hiyokosabrey:20191205223741p:plain できるだけアニメーションさせたい

 

 一気に減らしてしまうと、変化に気づきにくいので、できるだけアニメーションさせたい。その方がヤバイ感というか緊張感が伝わりやすい。ダメージのタイプによっ演出をいれると豪華になるうえに、原因が分かって納得度を上げることができる。たとえば物理ダメージの場合は振動させたり、魔法ダメージだとそれぞれのエレメントに合わせたエフェクトを乗せてみるとか。

 

 

 

減った量を判らせたい?

f:id:hiyokosabrey:20191207113129g:plain


 

 

  f:id:hiyokosabrey:20191205223658p:plain ゲージを2重にしてアニメーションさせる

 

  f:id:hiyokosabrey:20191205223723p:plain カウントダウンとマイナス分の表示を追加する

 

  f:id:hiyokosabrey:20191205223741p:plain アイコンのカタチに依存するので難しい

 

 

 ダメージを受けた瞬間にダメージ量を判りたいというのが、普通になってきているので、実装不可避な印象もあるけど、棒ゲージ以外は得意じゃないので、ゲージの近くか別の場所で減量分を表示するのがベター。

 重要なのは、ダメージを受けた時、ユーザーはどこを見ているか? という点に気を付けることです。そのあと、ゲージに視線誘導させるか否かで、演出やアニメーションのタイミングを調整します。

 

 

 

状態異常などバフ表示が必要?

f:id:hiyokosabrey:20191206000951p:plain

f:id:hiyokosabrey:20191205235216g:plain

 

 

  f:id:hiyokosabrey:20191205223658p:plain ゲージの色を大きく変えたり、エフェクトを乗せたり

 

  f:id:hiyokosabrey:20191205223723p:plain 色替え程度しかできない

 

  f:id:hiyokosabrey:20191205223741p:plain アイコンの色を変える。可能であればカタチも変える

 

 まずは、わかりやすく色を変えてみる。棒ゲージはそこそこ面積があるので、UVスクロール系のアニメーションをオーバレイしたりすると効果大。ステータスを表すアイコンを添えるなど、ゲージの表現以外の要素も含めたり、他の条件と被らないかなど、情報設計のコストは割と高め。

 色替えでわかりにくければ、動きを付けると目立たせることができる。

 

 

 

残り少なくなったらどうなる?

f:id:hiyokosabrey:20191207000700g:plain

f:id:hiyokosabrey:20191207002914g:plain

 

 

  f:id:hiyokosabrey:20191205223658p:plain ゲージ本体または、ゲージの背景を点滅して強調

 

  f:id:hiyokosabrey:20191205223723p:plain 数字を色替え点滅 

 

  f:id:hiyokosabrey:20191205223741p:plain アイコンの色または、枠を点滅して強調

 

  面積が小さくなった状態で点滅させても目立たない場合は、枠や下敷きを点滅させる。点滅のカラーはゲージカラーとも相談して慎重に選ぶ。赤色の点滅が一番心理的に響くけど、プロジェクトのカラースキームによっては、赤を使いたくないデザインもある。

 あとちょっとという状態が重要なのに見づらいという場合は、ゲージのサイズを見直そう。

 ピンチ表現を作り始めてようやくいろんな制約や問題に気付くこともある。点滅させたくない場合もあったりするので、どうすればピンチを伝えられるかのアイデアは日ごろから色んなゲームをプレイしてストックしておきたい。

 

 

 

回復表現はある?

f:id:hiyokosabrey:20191207171608g:plain

f:id:hiyokosabrey:20191207180508g:plain


 

  f:id:hiyokosabrey:20191205223658p:plain アニメーションさせる

 

  f:id:hiyokosabrey:20191205223723p:plain カウントアップさせる 

 

  f:id:hiyokosabrey:20191205223741p:plain アニメーションさせる

 

 回復は嬉しいもの。多少動きがあると嬉しさもアップ。動きを考えるときに、嬉しい表現と残念な表現についても分かりやすく区別できるよう意識して設計する。例えば赤点滅=ネガティブ表現、白点滅=ポジティブ表現 というのを決め、それを徹底するようにするだけで、パラメータ―やステータス表現でもプレイヤーの学習コストを下げる効果が期待できる。

 

 

 

 だいたいこんなところでしょうか。

 体力ゲージは、とにかく減らされることがプレイヤーにとっては最大の不利益となります。 したがって減ることの理由を、納得いくように表現して差し上げるのが、UIデザイナーの使命でもあります。情報量を増やし過ぎると初見で拒否られるし、少なすぎると謎感が上がって不親切な印象を与えてしまいます。UIデザインって匙加減がとても難しいのです。

 

 

 気をつけたいカタチ

 体力ゲージは、ゼロになるとペナルティがある以上、残りわずかであること、つまりピンチであることを表現することが重要になります。

  あとどのくらい? いまどれだけ減った? というのも大事なので、一方の太さが変わるようなゲージデザインは常時確認したくなる体力ゲージでは避けるべきです。

 f:id:hiyokosabrey:20191207103857p:plain

 物理的にピクセルが少なくなるので残り僅かであることを視認しづらい。

 反面これをチャージ系のゲージだとして考えると、ある程度溜まりきるまでは意識しない状態にできるし、溜まりきった時に最大面積になってようやく満タンになったよ!スキル使っていいよ!というアナウンスができます。

f:id:hiyokosabrey:20191207104918p:plain

 ONとOFFがハッキリしていて、あとちょっとで溜まりそう、という体力ゲージとはまったく逆の期待感を煽ることができるカタチです。まさにチャージ系のゲージに向いているといえます。

 

 とここまで書いておきながら、体力がゼロになっておしまい、とは逆の、ダメージが最大になったら戦闘不能、という詭弁のような発想で、このデザインでも緊張感を煽ることは可能です。SUN値やピヨリなどのスタン系ゲージでも使うことができます。

f:id:hiyokosabrey:20191208015228g:plain


 ザックリとですが言ってしまうと、大事な局面で必要な情報がしっかり視認できればなんでもアリです。まぁゲームUI全般に言えることでもありますが。

 

 全体のカタチとゲージの増減のカタチが合ってないのも時々見かけます。

f:id:hiyokosabrey:20191208142932g:plain



 

 

 棒ゲージの作り方いろいろ

 カタチがシンプルで構成するパーツも少なくて済みます。いろいろアレンジもしやすく、周囲をデコレーションで差別化できれば大体完成できるということで、扱いやすいのも魅力。

 用意するパーツは最低限2つあれば十分。ゲージ本体と、最大量が分かるための下敷きか枠。デザインは皆さんの素晴らしいセンスを発揮してもらえればバッチリなので、ここではゲージの増減についてちょっと技術的なところを書いていきます。

 

 ゲージの増減方法は大きく分けて3つほどに分けられます。

 

 簡単でコスパがいいのがスケーリングする方法。

 ゲージにグラデーションや模様などを入れていると、拡縮した際残念な見た目になるのでシンプルなベタ色か、スケールに影響されない方向にグラデーションを入れるのが無難。

f:id:hiyokosabrey:20191207120758g:plain

 

 と、思わせておいて、実は発想を変えると可能です。つまり見た目の下敷きを上から被せる方法。下図は重なりを斜めから見たイメージ。

f:id:hiyokosabrey:20191207120416g:plain

 

 

 2つめはステンシル等のアルファマスクや、シェーダーを使って画面の表示ピクセルを削っていく方法。これだと、下敷きを上に載せるというトンチを使わずに、ゲージにグラデーションや模様をいれることができます。

 

f:id:hiyokosabrey:20191206230534g:plain

f:id:hiyokosabrey:20191206230242g:plain

 シェーダーを使いこなせると面白い表現が可能なので積極的に使っていきたいですね。

 

 

 3つめはUVスクロール。これはゲージの先端部分がカーブしていたり、斜めになっている場合に効果的。テクスチャのオフセットを動かして増減させます。3Dメッシュを使えば、複雑な形状でも、ゲージにすることができます。下の例はちょっと贅沢に下敷きを別に用意している場合。

 

f:id:hiyokosabrey:20191207132805p:plain

 

 f:id:hiyokosabrey:20191206232400g:plain

 

 アルファチャンネルを使わないで作ることも可能で、デザインにもよるけど、下敷きが不要でパーツが減る分処理が少なくできる。無双系やタワーディフェンス系のゲームで大量にゲージを出す場合は特に効果的。

 ただの長方形だと安っぽいので、斜めにしたりすることが多い体力ゲージ。頂点を変形して斜めを作ると、UI描画の際アンチエイリアスが掛からないことが多いので、ピクセルの荒れを回避するなら頂点は長方形のままでUVスクロールがオススメ。

f:id:hiyokosabrey:20191206233535p:plain

 UVスクロールで動かす場合、テクスチャにゲージの長さの2倍の領域を確保しないといけないけど、テクスチャの設定をWrap(繰り返し)ではなくClamp(繰り返さない)設定にすると小さいテクスチャでもいい感じにできる。

 

 

 おまけとしてシェーダーを使ったテクニックをひとつご紹介。

f:id:hiyokosabrey:20191207135255g:plain

 アルファチャンネルのUVを動かさずに、RGBチャンネルのUVのみをスクロールさせます。絵に合わせてスクロール量を調整すれば完成。表示パーツはたった1枚で済むのでエコです。テクスチャのやりくりができれば、きっとプログラマにも喜ばれるはず。

 

 

かつては・・・

 大きなスプライト描画やポリゴンが登場するまでは、今のような自由なサイズのキャラを表示する仕組みがなかったり、解像度が低く拡縮でキレイに表示できなかったので、8x8とか 16x16とかのチップを並べてゲージを作っていました。ゲージは1ドットずつ変化します。これをプログラムで書き換えていくのです。

f:id:hiyokosabrey:20191206235118p:plain

 格闘ゲームなんかは1ピクセルがきちんと表示されていないとだめなので、丁寧に細かくパターンを用意していました。

 

 

開発にまつわる体力ゲージ

 ゲームグラフィックの表現力が上がってより繊細になるにつれてゲージのようなゲームゲームした表現を忌み嫌う向きもそれなりに増えてきているように感じますが、いつ死ぬか分からない緊張感はストレスにもなりえます。そのストレスから解放してくれるのがゲージだったりするわけで、このあたりのジレンマを解決しておかないと開発終盤にニッチモサッチモという事態に至ることがよくあります。

 「没入感」というキーワードを盾にしてUI要らないとか言い出すプロジェクトは、モーション(キャラクターアニメーション)やVFXの作成コストに跳ね返ることを考慮していないことが多く、最終的に作成コストが比較的安いと思われているUIに「やっぱり・・・」という事態に。

 没入感と一緒に語られるダイエジェティックなUI表現も、アートディレクターがUIデザイナーを兼任しているプロジェクトでもない限り、UIデザイン側から発信するのはとても難しいと思います。

 

 UIデザイナーがプロジェクトにアサインされる前にすでに検証やプロトタイプが進行していることはフツーです。プログラマがちょいちょいと作った簡素なゲージが表示されていることが多くて、開発中は見やすいし分かりやすいと当たり前のように鎮座しているのですが、どこかのタイミングで製品クオリティのデザインにしないといけません。ですが、シンプルイズベスト! 下手にデザインしたものに差し替えようものなら、分かりにくいとクレームが来ることもたまにありますし、ずーとそれでデバッグしてきたから生半可なデザイン差し替えでは納得されないし、じゃぁ、と手を入れなさすぎるとデザインをサボってるかのような目で見られたりします。

「これ、そのうちデザイン変わるんだよね?」

「え!? えぇ、も、もちろんです」(遠い目)

 

 マジで棒ゲージ最強です。これ以上分かりやすい見た目はないです。実際に作るのにほぼコストはかかりません。誰でも簡単に思いつくしものすごく手間がかかっていない仕事として見られています。仕様を色んな角度から眺めて検討し、あーでもないこーでもないと悩みぬいた挙句の棒ゲージ。他の可能性やロジックをシェイプアップして作った究極の一本!それでも、

「これって、仮キャラ?」

「え!? まぁ、今デザイン考えてるとこなんすよね~」(さらに遠い目)

 

 やっぱり体力とかライフといえばハートだよね。形も可愛いし。ハートを並べるのは分かりやすくてワールドで受けが良さそう!

「なんか、ゼルダっぽすぎない?」

「!・・・そうっすね。」(心も遠くへ)

 

 数字は桁が増えた時のインパクトは捨て難いよね。ちょっと生生しいけど、オシャレなフォントを使えば・・・ほら、いいカンジ!

「今回の仕様だと3桁ぐらいまでしか使わないんだけど」

「じゃぁ、後ろにゼロ3つくらい付けましょうよ」(汗)

「いや さすがにバレるだろ」

「・・・」(温泉行きたいな)

 

 

 

最後に

 一度でもゲージ類を作ったことがあれば、ゲージのパッと見簡単そうに見えて実はとても奥が深くて、いろいろ考えさせられるということを体験されていると思います。

 

  ゲージを笑う者はゲージに泣く

 

 いつか言ってやりたいのですが、ゲージを作るのはUIデザイナーの仕事。十分思い知ってるからこその任せてもらってるんだと思いますが、ゲージを笑うやつは、たぶんゲージを作ることはないんだろうなと。残念ながら。シンプル過ぎてアレンジしにくいのをなかなか判ってもらえないですが、頑張って新たな地平を見つけたいと日々チャレンジし続けたいと思います。

 

  ゲージに泣く者は、さらにゲージに泣く

 

 全然救われない格言でシめようと思いますが、いいゲージができた暁には、アドカレに記事を書こうと思います。ゲージについて語り合ったりお題を解決するゲージデザインOnlyのイベントとかあっても面白そうな気がします。ちなみにすごいのができれば発明になるので、特許を申請できますよ。

 え?この記事のゲージがダサい?

 もちろん仮ですよ。サンプルですよ。お試しですよ。本気出してないだけ。あはは・・・・は。

 

ではでは

ハッピークリスマス!

ステキなゲージライフを!

 

 

※今回の記事に載せているアニメーションGIFは、すべてPhotoshopのフレームアニメーションで作成しました

 

 

 

 

 

 

 

 

 

 

キャラ絵のテクスチャを作って表示する《おまけ編》

 前回前々回とキャラ絵をUMGでゲームっぽく作って表示する記事を書きましたが、いいねをたくさんもらえてありがたい限りです。さらにスポットライトに取り上げてもらえて

 びっくりするやら気恥ずかしいやらで、とりあえず浮かれたい気持ちをなんとか堪えつつおまけ編を書きていきます。

 

  できたのはこんなやつ。

f:id:hiyokosabrey:20191124151549j:plain

 

 さてさて、4人パーティのRPGなので、キャラの表示が4人分。それぞれを状況に応じてコントロールする必要があります。各キャラのパーツをクリックするとリアクションするとこまで、とりあえずグラフに並べて確認できました。

f:id:hiyokosabrey:20191124151049g:plain

 まだ、クリックしたら暗くなるだけです。

 

 そのノードがこんな状態。

f:id:hiyokosabrey:20191124142521p:plain

 せっかく配列にキャラ表示Widgetを4つ分入れているのに、なんだかコピペっぽい感じだし、無駄に同じ処理のイベントが並んでいます。

 

これをスッキリさせようというのが今回のネタとなります。

 

 

 中身が同じ4つのWidgetがキャンバスに並んでいて、それぞれは個別に動作はするけど、中身は同じなのでまったく同じ振る舞いをします。

f:id:hiyokosabrey:20191130205609p:plain

 これを並べて管理しているレイアウト用のWidget(以降、親、または親Widgetとします)は、それら各キャラ用Widget(以降、子、または子Widgetと書きます)を個別に扱う必要があります。

 親はいつでも好きなタイミングで子へアクセスできますが、子はバインドされていないと親への連絡はできません。

 

 親から子へバインドすると、専用のカスタムイベントを介して通知を受け取ることになります。

f:id:hiyokosabrey:20191201103305p:plain

  必然的に、子の数だけバインドノードと通知を受け取るためのカスタムイベントが必要になります。今回のパターンでは、個別の対応をする以外は全く同じ処理を必要とするのと、カスタムイベントの名前を変えないといけないので、↓ このようなセットが4つ並びます。

f:id:hiyokosabrey:20191130232255p:plain

↑左端のキャラ用  ↓右端のキャラ用

f:id:hiyokosabrey:20191130232318p:plain

 

 また、ゲーム画面の中でも、メニューボタンや機能ボタンのいくつもあるような画面では、ボタンごとに処理の方法や遷移方法が変わるために、こういったバインドとカスタムイベントの組み合わせが大量に並ぶことになります。

 

 これをスッキリさせていきますが、まずはこれから。

 

新しいグラフに引っ越す

 単純でシンプルで簡単な方法。

 エディタに最初から用意されているEventGraph 以外にもグラフを追加できます。

 Excelのシートみたいな感覚で使えます。増やすのはMy BlueprintタブのGraphsのところにある+ボタン。

f:id:hiyokosabrey:20191126225326p:plain

これでまっさらな新天地のグラフが使えるようになります。

 

コピペで引っ越しできますが、実行ピン同士をつなぐことができなくなるのでカスタムイベントを使って中継します。

f:id:hiyokosabrey:20191130210048p:plain

Sequenceノード以降を新しいグラフにペーストして、カスタムイベントを一つ追加。

f:id:hiyokosabrey:20191126232525p:plain

元のEventGraphに戻って、このカスタムイベントを呼び出すだけ。

f:id:hiyokosabrey:20191130210404p:plain

 

 セットアップや初期化のような、最初に一度だけ動かして、あとはそのままといったものを置くのに向いています

 ちょっと見えないところにやってしまうだけなので、カスタムイベントが追加になりますが、ノードの量は変わりません。あまり解決にはなってないですが、メンテナンスをする際に効率が上がります。

 

 

 ちなみに今回の例では4キャラ分のセットアップをSequenceノードを使って並べています。線を分断してコンパクトにできます。処理自体は順番に行われるので途切れません。

 

f:id:hiyokosabrey:20191201104124p:plain


 Sequenceノードはある程度のまとまりで管理できて見やすくなるし、途中や最後に追加しやすいのも便利な点です。

 

 これで再生して普通に動けば、1スッキリ成功です。

 

受け取り用のカスタムイベントを共用にする

 今回の例ではバインドから受け取った後の処理は同じでも、呼び出す関数がイベントディスパッチャーから通知のあった子が対象になるので、特定の子に対して関数を呼び出す必要があります。

 そこで有効なのが、子であるWidget自身が『誰』であるかを一緒に通知する方法です。そうすると、受け取ったカスタムイベントは、『誰』が特定できているので、同じ処理を『誰』ごとに行うことが簡単にできます。せっかく配列にして管理しているんですもの。Index番号を利用しない手はないですね。

 

 さて、同じWidgetなのに、個別の ID を持たせるにはどうすればいいか。それは、Expose on Spawn を使います。

 さっそく子Widgetを編集していきます。新しくInteger型の変数をひとつ追加します。

f:id:hiyokosabrey:20191130235213p:plain

Expose on Spawn と Instance Editable にチェックをつけて有効にしておきます。

 

 これで、このWidgetが、インスタンスとして生成されるとき(Create Widget ノードとか、どこかのキャンバスにUserCreated として配置されたときとか)に値を受け取って生まれることができます。

 この変数をイベントディスパッチャー経由で渡せるようにします。

イベントディスパッチャーを選択して、Detailタブから、Inputs を追加します。

f:id:hiyokosabrey:20191201003951p:plain

 Type を Integer型にします。

f:id:hiyokosabrey:20191201004018p:plain

 

 いったんコンパイルします。

 

 グラフに置いてあるノードにエラーが出ることがありますが、慌てずにエラーが表示されているノードの上で右クリックして Refresh Nodes を選ぶと消えます。

f:id:hiyokosabrey:20191201011328p:plain

入力ピンが増えるので、ここに先ほど追加した 変数をつなぎます。

f:id:hiyokosabrey:20191201011344p:plain

 これで子Widgetの編集完了。

 

 

 イベントディスパッチャーからの通知に変化があったので、親Widgetの方にもエラーが出ます。が、まずは、新しくカスタムイベントを新しく用意します。このイベントはどちらのグラフに置いても大丈夫。

 Integer型のピンを追加して、子Widgetを配列から取り出すようにします。

f:id:hiyokosabrey:20191201012149p:plain

 新しい受け取り準備ができました。これが共用のイベントになります。

 

 ここで、バインドノードとつながっているカスタムイベントを一掃してキレイにします。

f:id:hiyokosabrey:20191201012717p:plain

 新しくバインドノードとつなぐのは Create Event ノードです。

f:id:hiyokosabrey:20191201013103p:plain

 ↓こんなノードが取り出せます。

f:id:hiyokosabrey:20191201013132p:plain

 SelectFunction... と書かれたプルダウンから、さきほど用意した新しいカスタムイベントを探します。

f:id:hiyokosabrey:20191201013416p:plain

 このプルダウンに出てくる関数またはイベントは、パラメータのタイプと数が完全に一致してるものだけです。なので事前に作っておく必要があったのです。試しにピンの数を変えてみるとどうなるか。

 カスタムイベントの方にピンを追加してみます。

f:id:hiyokosabrey:20191201014014p:plain

 CreateEventノードはどうなっているか・・・

f:id:hiyokosabrey:20191201014104p:plain

 特に大きな変化はない様子だけど、コンパイルしてみると・・・

f:id:hiyokosabrey:20191201014136p:plain

 やっぱり。

 

 ということで、イベントディスパッチャーのパラメータ―と受け取り側のカスタムイベントのパラメータ―を一致させることが重要です。

 個人的に Create Event という名前と役割がいまいちしっくりこない感じがしているのですが、このノードで、離れたところにいるカスタムイベントをつなぐような使い方ができます。コンパイルした時点で、バインドノードに何もつながっていない場合エラーが出るので、このCreateEvent をつないで鎮めます。

f:id:hiyokosabrey:20191201014858p:plain

 

f:id:hiyokosabrey:20191201015059p:plain

 これで、受け取りのイベントは共用になったので、メンテナンスや改造が1か所で済むようになりました。

 

 エラーが無くなったところで、子に『誰』であるか特定するための固有番号を与えます。UMGのキャンバスを開いて並んでいる子を一つ選択します。

f:id:hiyokosabrey:20191201104730p:plain

 Detailsタブに新しく Default という項目が増えています。

f:id:hiyokosabrey:20191201104943p:plain

 ここに、Expose on Spawn を有効にした変数の名前が出てきているので、値をセットします。これで初期値として値を持たせておくことができます。

 

この状態で再生して変わりがなければ2スッキリ成功です。

 

バインドをシンプルにする

 まだスッキリさせる方法が残っています。

 バインドノードのTargetピンには配列をつなぐことも可能です。

 ↓このようになります。

f:id:hiyokosabrey:20191201020026p:plain

 これと、Sequence ノードに5番目のピンを追加してつなぎます。

f:id:hiyokosabrey:20191201020431p:plain

もともとあった個別のバインドノードたちはいらなくなります。

f:id:hiyokosabrey:20191201020626p:plain

 

 再生してみて何も変わらなければ、3スッキリ成功です。

 

 

 かなりスッキリしたと思います。あともう一つスッキリさせる方法があるのですが、今回はこの辺にしておきます。デフォルトの Event Graph 以外のグラフを使う方法と、Sequenceノード、CreateEventを使った方法が紹介できたので満足です。

 今回は同じ構造の子Widgetが対象なので、ForLoopを使うことも可能でしたが、まずはUIを作るうえで汎用的に役立てられるかなと思って記事にしました。

 

ではでは

ステキなUI開発ライフを!

 

 

 

 

 

おまけ

 TwitterにGIF画像を上げていた、タイマーイベントを使ったゲージ回復を一部ご紹介します。EventTickは使っていません。

 実際にゲームのプロジェクトで利用する場合はフラグの制御やらで回復中のクリックを抑制したり、キャラごとに回復速度の補正が入ったり、といろんな都合があると思います。下の例は子Widget内で処理しています。あくまでも一例ということで。

 

f:id:hiyokosabrey:20191201110637p:plain

  満タンじゃなければ、一定時間後に自身のカスタムイベントを呼び出しています。

 右端の青い変数は、Set Timer by Event ノードがReturn Value として出力するタイマーハンドラーを保持しておくもので、満タンになった時に、このタイマーハンドラーに対して停止を指示します。するとタイマーはストップするので、結果このイベントは呼ばれなくなります。上図の流れだけだと、止めなくてもSetTimerByEventが処理されないので大丈夫なはずですが保険的に入れています。入れておくとゲームの都合で止めなきゃいけない時などにこのハンドラーにPause(一時停止)の指示を出すこともできます。必殺技発動なんかのデモ再生中は止めたりしますよね。デモ技発動中にも回復していたら、デモの尺によって有利不利が出てきてしまいます。

 あとは、見た目を切り替える関数を適宜呼び出したり、回復中であるかどうかのフラグをセットしたり解除したりといった処理を追加すればいい感じになるはず。

f:id:hiyokosabrey:20191201113333g:plain

 キャラをクリックした時と満タン時にエフェクトとか出したいなーとか、妄想しています。

 以上 おまけのおまけでした。

 

 

 

 

 

 

キャラ絵のテクスチャを作って表示する《後編》

 

 前回の記事でRPGバトル画面風のUIを作る想定で、キャラのバストアップをテクスチャにしてUMGに仕込むところまでを書きました。その続きを作っていきます。

 材料はあらかた用意できたので、あとは実用に耐えるように関数をいくつか準備します。

 

 まずはゲームの企画仕様を元に、UIで必要な処理を考えてみます。

 

1.パーティ編成がプレイヤーによって自由にできる想定

  → キャラの絵を変えられる

 

2.職業がいくつか設定されている

  → 職業の名称を変えられる

 

3.棒状のゲージで体力量を表し、必要なタイミングで更新される

  → ゲージの長さを変えられる。ゲージは比率で増減。

 

4.棒状のゲージでスキルポイントの所持量を表し、適時値が更新される

  → ゲージの長さを変えられる。ゲージは比率で増減。

 

5.四角のスキルゲージによって、使用できるまでの時間を表す

  → 任意の量で見た目が増減する

 

6.プレイヤーが編集可能なスキルのアイコンを表示

  → アイコンの種類を変えられる

 

7.キャラを使用できるまでの間のタイマーを表示

  → 任意の値で数字が増減する

 

8.キャラの絵を待機中の見た目に変更する

 

9.キャラの絵を満タン時の見た目に変更する

 

10.スキルアイコンとゲージをチャージ中の見た目に変更する

 

11.スキルアイコンとゲージを満タン時の見た目に変更する

 

 

ひとまず思いつくのはこのあたり。

まずはシンプルなタスクをこなす役割としてごく単純な機能のみを備えた関数を用意する。

 

 

今回の作りでは、いろいろとマテリアルでいじれるようにしているので、まず最初に準備として マテリアル インスタンス ダイナミック を作ってそれを変数化する。

だらっと数珠つなぎになるので、マクロにまとめてしまって、Event  Construct につないでおく。

f:id:hiyokosabrey:20191123194943p:plain

中身は、マテリアルを適用したImageパーツをVariables からGetノードで取り出して、Get Dynamic Material ノードをつないだら、ReturnValue ピンより Promote to Variable を選択すると、変数が作られる。

f:id:hiyokosabrey:20191122230120p:plain

それをひたすら並べていく。

f:id:hiyokosabrey:20191122230524p:plain

順番は適当でOK。

マテリアルはダイナミック化しないといじれないのと、マテリアルを触るたびに Get Dynamic Material するのは効率が悪そうなので、先に変数化しておく。

マテリアルの準備ができたところで、関数作成開始。

 

 

1. キャラの絵を変える setCharFaceTexture

f:id:hiyokosabrey:20191122224442p:plain

先に用意しておいたキャラ顔用のマテリアルインスタンスダイナミックより Set Texture Parameter Value ノードをつないで、Parameter Name を セットしたら、Value ピンを関数の Inputsピン につなぐ。

f:id:hiyokosabrey:20191122233531g:plain

 

 

2. 職業の名称を変える setJobName

f:id:hiyokosabrey:20191123083628p:plain

 もらったテキストをそのままセットするだけ。

 

 

3. 体力ゲージの長さを変える setGaugeHP

f:id:hiyokosabrey:20191123084046p:plain

そのままマテリアルに渡すだけだけど、ゲーム内で扱う体力の値は、受け渡しの扱い方を事前に決めておかないといけない。今回は比率でいいとのことなので 0.0 ~ 1.0 で扱う想定。で直接 0.0~1.0の範囲で値を受け取る仕様。

 

キャラクターのパラメータが成長するタイプの場合、体力の値は整数で、例えば 800 とか 15000 みたいな値で管理する方が都合がいいので、どこかのタイミングで変換してやる必要がある。

この関数内で変換する場合はこんな感じになる。

f:id:hiyokosabrey:20191123091126p:plain

その時点での最大値も一緒に受け取ることになる。

それと大事なのが、整数同士の割り算は結果が整数になるので、1.0までの結果はすべて 0 になってしまう。先にInt型からFloat型に変換してから割り算しないといけない。

 

UIのゲージ系表示で、長さが固定の場合、この計算は割と汎用的に行うので、マクロライブラリに追加してしまった方がいいかもしれない。マクロライブラリの管理担当と相談してOKが出たので追加してみる。

f:id:hiyokosabrey:20191123094831p:plain

 getRatio と命名

このマクロを先の体力ゲージに組み込む。

f:id:hiyokosabrey:20191123095014p:plain

完成。

 

4. スキルポイントゲージの長さを変える setGaugeSP

f:id:hiyokosabrey:20191123095230p:plain

体力ゲージと同じマクロでつなぐ。

完成。

 

 

5. スキルゲージ使用チャージの状況を変える setSkillChargeGauge

f:id:hiyokosabrey:20191123103354p:plain

これだけだとゲージの増減しかできない。

今回のデザインではゲージの見た目に溜まり具合が分かりにくいので、一緒にテキストも表示するようにしている。

f:id:hiyokosabrey:20191123104637p:plain

0.0~1.0の値に100を掛け算するとパーセンテージになるのでそれを FormatTextノードで「%」記号を付けて書きだす。

f:id:hiyokosabrey:20191123105030p:plain

FormatTextノードは結構柔軟に数値を取り込んでくれる(テキストに変換してくれる)ので便利だけど、桁のコントロールが必要な場合は、間にキャストノードを挟むといい。ToTextノードを展開すると以下のような設定ができるようになる。

f:id:hiyokosabrey:20191123120353p:plain

f:id:hiyokosabrey:20191123121131p:plain

チャージ量を表すとき、四捨五入や切り上げをしてしまうと、99.9% なのに 100%と表示されてクレームの元になるので、不都合な期待感を持たれないように切り捨てる。

上の画像のように設定すると、 1~100の整数しか表示されなくなる。

 

完成。

 

 

 

6. スキルのアイコンを指定したものに変える setSkillIconTexture

f:id:hiyokosabrey:20191123125004p:plain

スキルのID番号を Float に変換してパラメータとして渡すだけ。

完成。

 

 

7. キャラを使用できるまでのタイマーを指定した数字に変える setWaitTimerCount

f:id:hiyokosabrey:20191123134208p:plain

ひとまず直接パーセンテージの値で受け取ることにする。

小数点以下の桁数はデザインの都合で1桁に決めたので、この関数内で整える。

 

見た目にこだわって、小数部分の文字サイズを変えてしまったので、操作対象のTextBlockが2つに分かれることになった。

f:id:hiyokosabrey:20191123134410p:plain

整数部分は、Floorで小数部を切り捨てるだけ。

小数部は、まずFrac で整数部を捨てた後、欲しい桁数×10倍 して小数点を移動し、Floorで小数部分を切り捨てれば完了。

limesode.hatenablog.com

 

 

8.キャラの絵を待機中の見た目に変更する setCharWait

f:id:hiyokosabrey:20191124145844p:plain
 

 

9.キャラの絵を満タン時の見た目に変更する setCharReady

f:id:hiyokosabrey:20191124145823p:plain

 

 

10.スキルアイコンとゲージをチャージ中の見た目に変更する setSkillUseWait

f:id:hiyokosabrey:20191124141120p:plain

 

 

11.スキルアイコンとゲージを満タン時の見た目に変更する setSkillUseReady

f:id:hiyokosabrey:20191124141145p:plain


 

 

レイアウトしてみる

これで一通りの表示をコンとロールできるようになったので、画面に仮組みしてみる。

新しくレイアウト用のWidgetを作って、キャンバスにUser Createからドラッグ&ドロップして並べる。

f:id:hiyokosabrey:20191123192805p:plain

ドラッグするときのコツは、直接キャンバスにドロップすること。この時に中身のサイズを計算しくれる。

ヒエラルキーパネルにドロップすると、Widgetのサイズ情報がリセットされる。

f:id:hiyokosabrey:20191123194129p:plain

Size To Content にチェックを付ければいいだけの話なのだけれど・・・

f:id:hiyokosabrey:20191123195434p:plain

特に問題ではないんだけど手数が減るに越したことはない。

 

並べるときに、Anchors の設定を Bottom Centerにする。

f:id:hiyokosabrey:20191123195931p:plain

あわせて、並べたWidgetAligment X を 0.5 にすると中央揃えのパーツはものすごく並べやすくなる。理由は、画面中央のX座標が0になるから。

右側か左側を並べたら、片方は符号を逆にするだけでシンメトリーになる。

f:id:hiyokosabrey:20191123211044p:plain

 

  キャンバスに並べ終えたら、左から0~3の順でリネームしておく。

f:id:hiyokosabrey:20191123211435p:plain

 

グラフモードに切り替えたら変数を新しく追加する。→①

f:id:hiyokosabrey:20191123211907p:plain

Variable Type に先ほどキャンバスに並べたWidgetと同じ名前があるので、それを選択。→②

配列型に切り替え。→③

適当にリネーム。 →④

 

この配列にキャンバスに配置したWidgetをセットして利用する。

f:id:hiyokosabrey:20191123213318p:plain

こうしておくことで、ForLoopなどでまとめて処理できるようになるし、各Widgetを番号(0~3の整数)で扱うことができるので、なにかと便利になる。

 

再生するために、テスト用にレベルブループリントからViewportに書き出してみる。

CreateWidgetノードに、レイアウト用Widgetをセット。

f:id:hiyokosabrey:20191123212840p:plain

 

f:id:hiyokosabrey:20191123213035j:plain

キャンバスに並べたまんま。

関数のテストを兼ねていろいろ値を入れてみよう。

 

 

関数の動作テスト

レベルブループリントは閉じて、レイアウト用のWidgetを開く。

まずテスト用の関数を用意。関数のグラフを開いたら、

Inputsにキャンバスに配置したWidgetを探して追加。

f:id:hiyokosabrey:20191123214111p:plain

入力ピンからドラッグして call で検索すると頑張って準備した関数名がずらっと出てくる。(※エディタの言語をEnglishにしている場合)

f:id:hiyokosabrey:20191123214410p:plain

関数をいくつか呼び出して、ランダムな値でも入れてみることにする。

f:id:hiyokosabrey:20191123215210p:plain

とりあえずゲージ3種とタイマー。関数名は適当に TestParam と命名

 

EventGraphに戻ってつなぐ。

f:id:hiyokosabrey:20191123215356p:plain

これで再生してみる。

f:id:hiyokosabrey:20191123215541j:plain

配列の0番を関数に渡したので、左端のものだけ変化した。

あとは名前やらテクスチャやらも試してみよう。

さっきのテスト用の関数の中に追加。

f:id:hiyokosabrey:20191123215936p:plain

再生。

f:id:hiyokosabrey:20191123220049j:plain

うまくいってる様子。

 

この状態でも機能的にはかなり実装できた。

あと残っているのは重要な「イベント」。

 

 

イベント

イベントの発生には「条件」が付くのが基本だ。「条件」は銃で言うところの「トリガー」に相当するもので、条件が満たされた瞬間にトリガーが引かれ弾が飛び出す。飛び出した弾が「活動中のイベント」になって、次に何かに当たると当たったものによってリアクションの特定のイベントが発生する。そうやってイベントがドミノ倒しのように次のイベントを起こしていく。それが「イベントドリブン」イベント駆動ってやつだ。常に全体を見張っていて起こった事象を解析し、次の行動を確定する、というのに比べると、状況に応じて必要なイベントのみが処理されていくので無駄が少ない。

 

まずそのトリガーとなる要素を仕込む。

キャラ用のWidgetを開いて、キャンバスに Buttonパーツを重ねる。

f:id:hiyokosabrey:20191124002730p:plain

名前に Action と SkillUse を付けた。

個人的に推奨しているのが、名前の先頭にUMGのパーツ名をそのまま残しておくというルール。そうすることで同じ名称が使えるし、そのままグループだということも分かりやすくできる。

f:id:hiyokosabrey:20191124002546p:plain

見た目だけを非表示にしたいので、カラーを透明にする。

f:id:hiyokosabrey:20191124002853p:plain

f:id:hiyokosabrey:20191124002936p:plain

見た目より少し大きくカバーするようにしている。

ButtonパーツのDetailsタブにある緑色のイベント追加ボタンから On Pressed のイベントを追加する。触ってみて操作事故が起こるようだったら On Click に変えるといい。

f:id:hiyokosabrey:20191124003324p:plain

On Pressed は触れた瞬間にトリガーされて、On Clicked は一度触れ後、離れた瞬間にトリガーされる。押してから間違いに気づいた時、キャンセルできるかできないかの違い。

+ボタンをクリックすると、エディタは EventGraph に飛ばされて、一度イベントノードを作ると、次回から  +  が  View  に変わっている。このあたりからもエディタの優しさというか丁寧な作りを感じる。

 

f:id:hiyokosabrey:20191124125437p:plain

それぞれのボタンをクリックした時のイベント処理用のノードが置かれるので、ここにイベントディスパッチャー(イベントを起こすもの)をつなげる。

 

イベントディスパッチャーは、MyBlueprintタブの下の方で追加できる。

f:id:hiyokosabrey:20191124125809p:plain

これを先のイベントに Call で取り出してつなぐ。

f:id:hiyokosabrey:20191124131816p:plain

封筒のアイコンがつく。

f:id:hiyokosabrey:20191124131829p:plain2つのイベントにつないだら

 

f:id:hiyokosabrey:20191124132048p:plain

クリックされたら、この赤いイベントが呼び出され、さらにイベントディスパッチャーが通知イベントを起こします。この時点で通知先はまだ未定。

これでコンパイルして保存したらひとまず完成。

 

レイアウトWidgetの方に移って、イベントディスパッチャーを受け取る用意をする。

f:id:hiyokosabrey:20191124132739p:plain

テストで、クリック受け取ったら Print String ノードで画面に表示するようにしてみた。

この バインドノードが対象Widgetにある イベントディスパッチャーと紐づけ(Bind)てくれる。

バインドすることで、イベントディスパッチャーと通知先がつながるので、別々のWidgetでも通信ができるようになる。親のWidgetから子のWidgetへのアクセスはできるけど、子から親へのアクセスはできない。それを特別なルートでつなぐことができるのがこのイベントディスパッチャーとバインドと使った仕組み。

 

これで再生してみる。

f:id:hiyokosabrey:20191124134507j:plain

ちゃんと反応してくれている。

これで受け取った時に待機中の見た目にすればさらに雰囲気が出そう。

まず満タン状態にするために、テスト用関数の最後に、満タン状態にする関数をつなぐ。

f:id:hiyokosabrey:20191124135200p:plain

これで再生した時に、待機中のタイマーが消えていれば成功。

キャラのWidgetの方で初期状態を非表示にしておけばいいのだけど、関数のテストも兼ねてつないでみた。

 

クリックした時に待機状態にしたいので、Print String を入れ替える。

f:id:hiyokosabrey:20191124135551p:plain

これで再生してみると。

f:id:hiyokosabrey:20191124135909g:plain

スキルも同じようにしてみる。

f:id:hiyokosabrey:20191124141417g:plain

 

他のキャラにもバインドすれば同じように動作する。

普通につなぐとちょっと冗長(だら~っとすること)になってしまう。

f:id:hiyokosabrey:20191124142521p:plain

一応これで動くけどスマートじゃない感じ。

 

とりあえずテスト用関数を少しいじって、キャラ顔のテクスチャとスキルID、職業名を外から渡せるようにしてみる。

f:id:hiyokosabrey:20191124150605p:plain

 

これでテクスチャとかを個別にセットして再生してみる。

f:id:hiyokosabrey:20191124151049g:plain


一応完成。

f:id:hiyokosabrey:20191124151549j:plain

 

この辺からは、キャラクターのステータスを管理する仕組みと連動するので、ひとまずUIとして仕込めるのはこの辺までにしようと思います。バインドのところがスマートじゃないので、次回《番外編》を書こうと思います。

 

f:id:hiyokosabrey:20191124152117j:plain

ではでは

ステキなキャラ顔表示ライフを!

 

 













 

キャラ絵のテクスチャを作って表示する《前編》

 いつのまにか日も短くなって朝夕の冷え込みが堪える季節になってきました。今回はゲームUI制作のネタでもと思いたったので、キャラクターの絵をちょっと特殊な切り方で表示する方法を、架空のゲーム画面を制作する想定で書いていこうと思います。HUDなどのキャラ絵のバストアップをデザインのモックからテクスチャにしてUMGに持っていくあたりまで。

 

キャラの切り出し方について

  テクスチャは四角です。四角く切って貼るのはデジタルではあたりまえです。ゆがむことはありません。そこに技術的な何かを感じられないとダメだ、という強迫観念のようなものと日々UIデザイナーたちは悪戦苦闘しているのではないでしょうか?

 

 例えば、会話シーンなどでキャラの顔がフキダシと一緒に出る場合で、四角く切ってみたらこんな感じ。

f:id:hiyokosabrey:20191120220846j:plain

 確かに安定感はあります。翼や謎のブースターがついていても、問答無用でトリミングできるし、どんな絵でも確実に固定サイズできっちり揃えられる上に、アルファチャンネルを作りやすいです。

 

 一方、スマブラのバトル画面で表示されている顔アイコンとか、ちょっと前に出たファイヤーエムブレムの風花雪月の会話ウィンドウでも見かけるやつがこんな感じ。

f:id:hiyokosabrey:20191120220753j:plain

 やっぱりきっちりと下敷きやフレームのカタチでカットされるより、少しはみ出した方が下敷きとの距離が出て立体的になるし、シルエットに変化が出るので、アホ毛やケモミミや角などでキャラ付けしてる場合は特に効果的です。さらに見た目に安っぽくならないのがいいのです。ただ作るのが若干手間だったりします。

 

 前置きが長くなりましたが、このはみ出しタイプをPhotoshopで準備して、UE4のマテリアルで合成して、UMGでちゃっちゃと表示します。ただテクスチャ作って出してハイ終わり、だと面白くないので、架空のゲーム画面を作る体で進めます。 

  なので、先のチャットUI~の文体と被らないようにしてみたので、少々な感じの文体ですがそこは大目に見ていただけると幸いです。

 

RPGのバトル画面UIを作ることになった

お題として

  • プラットフォームは未定だけど操作はマウスクリック
  • 戦闘に出せるパーティーキャラは同時に4体まで
  • 横画面で1920x1080

ひとまずキャラ絵の表示だけなら、これだけの情報でも作れなくはないけど、もう少し詳細がほしい。プランナーと話をする。

一緒に表示したいパラメータ

  • キャラ名または職業名
  • HPゲージ ※比率が分かる程度でよい
  • SPゲージ(スキルポイント) ※比率が分かる程度でよい
  • スキルアイコン ※チャージタイプ

で、この仕様をあれこれ考えてできたのがこんなレイアウト。

f:id:hiyokosabrey:20191117122440p:plain

クリックできるものはナナメの◆。ただの情報は水平の ■ でルール化する。

背景の◆は半透明で、パーツをグルーピングするための要素で最終的には柄を入れたい。

これを、サイズ感を調整しながら4体分並べる。

f:id:hiyokosabrey:20191117123031p:plain

ターン制という指示はないけど、チャージタイプの要素があるということはアクティブになったら即アクションというリアルタイム系のプレイスタイルだと思われるので、そのへんの仕様を考慮して3つの状態(初期・途中・最大)をデザインに織り込む。

f:id:hiyokosabrey:20191117124223p:plain


アクティブの状態と非アクティブの状態が確実にわかるようにメリハリをつける。

今回、初期状態と最大の状態は同じ見た目ということにする。

チャージ中は、カウントアップの数字を出して待たせる。

この段階ではダメージを受けた時、スキル発動時なのどインタラクションは重要ではないので、いったんこれでプレゼンする。

 

レイアウトはOK出たので次は素材作成

無事プレゼンして了解が得られたところで、キャラ絵についてスケジュールを相談。

イラストレーターに発注するということで、作画のための仕様書を用意して渡すことにする。

いったん今回のレイアウトで決めたルールでキャラ絵のテクスチャを想定してみる。

サイズは256x256で行けそう。

そのテクスチャを基準にしてまとめると

f:id:hiyokosabrey:20191117133044p:plain

ライトグリーンの領域を除いた部分が有効範囲。

それと併せて、

  • バストアップ(全身は不要)
  • カット領域は表示する際にカットする
  • はみだし領域やカット領域については全キャラ共通
  • ゲーム内でしか使わないので解像度はほどほどで

などの条件に加えて、レイアウト参考用のテンプレを作り依頼。

 

f:id:hiyokosabrey:20191117135553p:plain

 

絵が上がってくるあいだに、アセットの準備を進める。

 

アセットをどう分けるかを考える。

キャラはフルカラーで透過が必要なので、256x256のRGBA各8bit 32bppのテクスチャ1枚。

背景の◆はどうするか?全キャラ共通でさらにマスクと共用できそうなので1枚。

 

f:id:hiyokosabrey:20191117142952p:plain

 

計2枚あればなんとかなりそう。

◆のグラデもマテリアルで色を付けるくらいのシンプルさの方がキャラを引き立てられる。文様をいれてもいい。

 

キャラ絵はトリミングだけで済むので後で作るとして、キャラをカットするためのマスク用テクスチャから用意する。

 

下図のような3種類のマスクテクスチャを用意するんだけど、

 

f:id:hiyokosabrey:20191117154851p:plain

 

これをR(#FF0000)G(#00FF00)B(#0000FF)の輝度としてレイヤー化。覆い焼きのリニアで加算することで1枚のテクスチャにできる。真っ黒(輝度が0)の部分は完全に透明になる。

 

f:id:hiyokosabrey:20191117155107p:plain

 

色味が重要ではないので、それほど神経質になる必要はないけど、グラデーション機能を利用する場合は、滑らかさのパラメータ―を0%にしないと、リニアにならないので注意。

 

f:id:hiyokosabrey:20191117180033p:plain


これをTGA形式 で書きだしたらUE4にインポートする。

 

圧縮するとエッジがガタガタになるので、Compression Settings は User Interface を選択。

f:id:hiyokosabrey:20191117180915p:plain

  •  sRGBのチェックを外してリニアカラーにする
  • タイリングは不要なので、Clamp を選択
  • MipMapは必要無い

この設定でアセットにする。

 

マスク用のテクスチャができたところで、キャラ表示用のマテリアルを作成する。

ここは企画資料にあったラフ絵(PNGで抜いてあったから便利)で代用する。

サイズを調整して配置を決めたら、アルファチャンネルを作る。

f:id:hiyokosabrey:20191117200738p:plain

まず抜きになっているレイヤーのサムネイルの上で、Ctrlキーを押しながらクリックすると、選択範囲が作られる。

f:id:hiyokosabrey:20191117201316p:plain

f:id:hiyokosabrey:20191117201740g:plain

そして、チャンネルパレットから 選択範囲の保存ボタンをクリックして作成完了。

f:id:hiyokosabrey:20191117202416p:plain

f:id:hiyokosabrey:20191117202845p:plain

本番のイラストがくれば差し替えるので、作業用Photoshopデータとして PSDで保存しておいてから、32bppの Targa(拡張子TGA)形式で書きだす。

f:id:hiyokosabrey:20191117202623p:plain

 

UE4にインポートする。

マスク用のとほぼ同じ設定だけど、カラーはリニアではなくsRGBを有効にする。

f:id:hiyokosabrey:20191117203408p:plain

 

テクスチャの準備ができたので、マテリアルを新しく作成。

f:id:hiyokosabrey:20191117204707p:plain

 

UMGで使うので、MaterialDomainを User Interface に変更して、ついでに半透明も使えるようにする。

f:id:hiyokosabrey:20191117203823p:plain

 

グラフにテクスチャのアセットアイコンをドラッグ&ドロップ。

f:id:hiyokosabrey:20191117204720p:plain

先にTextureSampleノードを置く場合、テクスチャのSamplerTypeが合わないとエラーがでるので、手間を減らす意味でこのドラッグ&ドロップが便利。あとはいい感じに加算と掛け算を駆使。

f:id:hiyokosabrey:20191117205452p:plain

キャラ絵は最終的にパラメータノードに差し替えるけど、今はこれで確認してみる。

 

UserWidgetを新しく用意。

f:id:hiyokosabrey:20191117210603p:plain

 

UMGのキャンバスに256x256の Imageを配置する。

f:id:hiyokosabrey:20191117210202p:plain

画像をセットする Brush のところにコンテンツブラウザからマテリアルアセットをドラッグ&ドロップする。

f:id:hiyokosabrey:20191117210338p:plain

f:id:hiyokosabrey:20191117210426p:plain

汎用性を持たせたいので、キャラをブループリントから差し替えられるようにする。

マテリアルを再び編集。

マテリアルパラメータ用のノードとして TextureSampleParameter2D を取り出す。

f:id:hiyokosabrey:20191117211234p:plain

f:id:hiyokosabrey:20191117211333p:plain

これと差し替える。

f:id:hiyokosabrey:20191117211754p:plain

パラメータのノードには気持ち悪いサンプルのテクスチャがセットされているので、これをサイズの小さなブランクテクスチャを別途作って差し替えておきたい。

ブランク素材は差し替え前提の汎用アセットで、メモリにロードされた際に不要な参照を持たないようにするのが主な目的。プロジェクトで共通のアセットとして管理するのがベター。

ParameterDefaults というタブが用意されているので、そこからセットできる。

f:id:hiyokosabrey:20191117212536p:plain

今回は 8x8の白色で、アルファチャンネルが0のテクスチャ。

これでマテリアルの編集は完了。

 

Widgetに戻ってブループリントを編集する。

マテリアルがうまくできているか確認するために、Event Pre Construct からパラメータにアクセスしてみる。

f:id:hiyokosabrey:20191117213643p:plain

パラメータ名を None から 書き換えて、Value のところにキャラ絵のテクスチャアセットをセットしてコンパイルすると、キャンバスの状態が変わっているはず。試しに別のキャラ絵をインポートしてやってみる。

f:id:hiyokosabrey:20191117214717p:plain

おなじ要領でサイズを調整してテクスチャにする。

f:id:hiyokosabrey:20191117215541p:plain

大丈夫そうなので、マスクテクスチャを利用してドロップシャドウ的な下敷きもマテリアルで用意する。

f:id:hiyokosabrey:20191117220508p:plain

このマテリアルも、キャラ絵と同じく Image の Brush にセットして使う。

f:id:hiyokosabrey:20191117220659p:plain

あとはテキストやら、ゲージやらを置いていく。

 

レイアウトができた。

f:id:hiyokosabrey:20191121203922p:plain

いまだに(4.23でも)UE4のフォントに付けられるアウトラインは、書体と表示するサイズによって欠けが発生する。フォントを手作りして頑張って回避してみるか、諦めてテクスチャにした方がいいと思う。けどテクスチャで数字のカウントを表示するのはひと手間かかるので何とかしてほしい。

 

ヒエラルキーはこんな感じ。

f:id:hiyokosabrey:20191123133054p:plain


 

HPとSPゲージはマテリアルで色替えできるようにしておく。

f:id:hiyokosabrey:20191121205224p:plain

今のところ、同じ仕様でただの色違いなので、マテリアルインスタンスでカラーバリエーションを作るようにする。
まずは、基本となる親マテリアル。

f:id:hiyokosabrey:20191121205703p:plain

ゲージを増減させるための閾値を Scalar Parameter にして、ゲージ本体のカラーを Vector Parameter で受け取るようにする。

 

これができたら、コンテンツブラウザからこの親マテリアルうえで右クリックすると、マテリアルインスタンスが作成できる。

f:id:hiyokosabrey:20191121205801p:plain

 

できたマテリアルインスタンスを開いてパラメータの部分を設定する。

f:id:hiyokosabrey:20191121210345p:plain

このマテリアルインスタンスをUMGのImageパーツにあるセットするとゲージのできあがり。

親マテリアルとマテリアルインスタンスはサムネイルだけでは判別できないので、アセットアイコンの下線のカラーで見分けるようにする。

f:id:hiyokosabrey:20191121211115p:plain

 

スキルアイコンは、少々特殊なゲージにしたかったのでマテリアルを専用で作る。

これは特にバリエーションがなさそうなので、マテリアルインスタンスは作らない。

f:id:hiyokosabrey:20191121211813p:plain

BoxMask-2D というノードが用意されていたのでこれを使うことにする。

f:id:hiyokosabrey:20191121212017p:plain

入力ピンのところに(V2)とあるので、Float型の値を2つまとめた Vector2 をつなぐ仕様になってるみたいだけど、Scalar でもエラーは出ない。

チャージ中と満タンの時でカラーを変えたいので、カラーを Vector Parameterにしておく。

 

あとは色味付けで、フォント用のグラデーションをマテリアルで用意する。これも汎用で使えそうなので、親を作って、マテリアルインスタンスでバリエーションを増やす。

f:id:hiyokosabrey:20191121214713p:plain

このマテリアルを、TextBlockのフォントマテリアルのところにセットすると、文字にグラデーションがつく。

f:id:hiyokosabrey:20191121215443p:plain

 

最後にスキルアイコン用マテリアル。

f:id:hiyokosabrey:20191121220305p:plain

テクスチャは1024x1024の解像度に 80x80 のアイコンが 144種。職業によって覚えるスキルの数が変動するらしいけど、とりあえず、1職業につき最大10スキル。13職業ある想定。

ゲーム中にスキルの変更を手軽にできるようなことを聞いたので、常駐させる必要がある。サイズが大きくなるが、グレースケールテクスチャ(8bit)にするとテクスチャメモリが節約できる。

f:id:hiyokosabrey:20191121225943p:plain

 アイコン1個のサイズが 80px なので、 1024で割ると  0.078125

f:id:hiyokosabrey:20191121230717p:plain

このマテリアルに スキルのID番号をパラメータとして渡してやる と、そのID番号のアイコンがトリミングされる仕組み。

 

表示するときにいくらか角度をつけて傾けているけど、斜めのままだとテクスチャ容量がもったいないので、Photoshopのスマートオブジェクトを使えば無理なく無駄なく詰めることができる。

f:id:hiyokosabrey:20191121232157p:plain

表示範囲を示す赤いレイヤーを置いてその中にアイコンを置く。この状態で見た目を調整。テクスチャに持っていくとき、斜め角度を打ち消すように並べる。

f:id:hiyokosabrey:20191121232541p:plain

スマートオブジェクトが中に持つピクセルは傾いたり回転していないので、編集する際はそのまま表示される見た目のままで作業できる。変形していても非破壊なのが素晴らしい。

 

だいたいのパーツは準備ができた。

あとは関数を用意して、キャラやスキルアイコンの差し替え、ゲージの更新などができるようになればかなり実用的になると思う。

 

というわけで、今回はこの辺でいったん終わりにします。続きは次回。

 

ではでは

ステキなキャラ顔表示ライフを!

 

 

 

 

 


 おまけ

 このネタを書こうと思い立った理由の一つに、キャラ絵をいかにキレイに表示するかというテクニックを解説している記事を見たことが無いから(個人調べ)というのがあります。

 おそらくゲーム開発の現場で一子相伝暗殺拳として子々孫々へと伝えられているのだろうと想像しています。実際そういったゲームUIの作り方なる秘伝のタレみたいなものがあればいいのですが、私自身ハードウェアの進化に付いていくために常に手探りしながら試行錯誤の毎日です。

 で、今回の手法は、圧縮テクスチャを使わずに済むなら気にしなくてもいいのですが、圧縮される場合に特に効果的です。さらに、スマブラのように、状況によって背景のカラーを変えることができます。勢力違い、プレイヤー番号違いなど、同じキャラ絵でもバリエーションを安く量産できます。

 また、圧縮するしないに関わらず、効果があるのが、シェーダーでまとめて重ねてしまうので、ピクセルシェーダー的には計算量が減るわけではないですが、表示パーツが少なくなるので、その分の処理を省くことができます。組み合わせるパーツのサイズやパラメータの扱い方によって変わるので確実に軽くなるとは言えないですが、試してみる価値は十分にあります。

 

 

チャットUIを作る

 12

 予定通りディレクターへのお披露目の時間になり、それほど広くないスペースに召集された4人が密集する。一気に湿度と気温が上がるような気がする。

 あらかじめ動作確認程度にはセッティングを済ませておいた。いつもの軽い挨拶を交わした後ディレクターに直接触ってもらう。

 「チャット画面でコメントを入力するときに、左右のどちら側に飛ばすか、という仕様に対して、2タイプのUIを考えました」

 「まずは中央で、コメントを書き終えてからどちらにするか選ぶタイプ」

 

f:id:hiyokosabrey:20191024224843j:plain

 

 適当に軽い感じのコメントを打ち込みながらうなずく。

 「ふんふん、なるほどね。いいね。はいはい」

 少し改まった感じの空気で始まったので、ややディレクターの反応も固い気がする。予定が空いていたとはいえわざわざ集まるからには、ここに集まったメンバー全員何かしら判断や決定、相談などの重要な案件であることを意識しているだろうけど、もう少し雑談から始めても良かったかもしれない。

 「もう一つは、あらかじめ入力する場所が左右に分かれてるタイプ」

 ちょっと失礼しますと言って、マウス操作でブランチノードの値を切り替えてタイプを変更して見せる。

 「え? 今ので、切り替わるの?」

 「はい」

 入力用のWidgetを2つレイアウトしておいて、表示のON/OFFを切り替えているだけだが、今の驚き様を見る限り、警戒しておくに越したことはない。

 

f:id:hiyokosabrey:20191024224904j:plain

 

  こちらのタイプも適当に文字を打ち込んで試しながら。「うんうん、なるほど、なるほど」という反応。テンションはそれほど違いが無い印象。

 

 「どうです?」

 と谷山田が訊く。

 「うん、悪くないね。企画書見てからあっという間にここまで作れるのすごいね」

 「ええ、まぁ、そこがアンリアルエンジンの素晴らしいところです」

 ここぞとばかりに、UE4推しの返答をする。

 「う~ん、選ぶとなると難しいな。どっちも機能的には出来てるし、使い勝手も悪くない。UI的にはどっちがいいと思う?」

 できれば選んでほしくない決定に向かう気配を感じていたので、ここで水を向けられるのはチャンスかもしれない。

 「そうですね、ポイントはコメントする前に、どちらにメッセージを飛ばすかが決っているかどうかだと思います。さきに決めて書き始める場合、左右に分かれたタイプの方が分かりやすいですし、とりあえずコメントを打ってから、どっちにするか決める場合は中央の方が向いてそうです。コメントを入力中に会話の流れが変わることを考えると、中央の方が状況に対して融通が利く感じでしょうか」

 まずはそれぞれのタイプが持つ特徴について、あまり重みを付けずに説明したつもりだが、ここで息を整える。少しは議論の焦点を絞れたのではないだろうか。

 そこへ「ですよね」と谷山田が続ける。

 「僕も仕様書書いた後にちょこっと考えてまして、どちらに書くかという選択って、気持ち的にいつなのかという」

 うんうん。

 「やっぱ書く前かなぁと思うんスけどね」

 「説明されんと戸惑うやろな」

 「うん、左右に分けてコメントするという仕様自体が普通じゃなくて、既存のチャットUIからしたら、なまじ踏襲しているスタイルだから初見で仕様の意図を理解できる人はいないと思うんだよ。この辺どう説明するかも考えないとな・・・」

 時間をかけて一般化したUIに新な仕様を入れることは難しい。一度慣れたものを壊すのは勇気がいる。また授業料を払わされるのを嫌ってクレームをつけられる可能性が高い。そもそも右側に自分のフキダシがくるのが定番みたいになってるから、左右のフキダシが1P2Pサイドの陣営ぽくなってるのをどう説明する・・・?ここでふとイメージが閃いた。

 ちょっと失礼、といいながら起動しっぱなしのPhotoshopのウィンドウをアクティブにする。以前に作ったデザインラフのドキュメントを開く。

 「ここは1P2Pのカラーを反映したらいけるかもしれません」

  左右のフキダシと入力用ボタンに色を付けてゆく。

 「カラーは後で調整しますんで、とりあえず・・・」

 途中で意図を察した谷山田が声を上げた。

 「なるほど!これなら分かりやすくなりますね」

 

f:id:hiyokosabrey:20191022001544j:plain

 

 「自分のフキダシも統一したカラーになりますが、自分のアイコンを見失うことはないはずなので、それほど心配ないかと」

 「確かに、確かに!」

 ディレクターの顔色が明るくなって、心なしか声のトーンも上がっている。

 

 「中央入力のタイプだとこんな感じでしょうか?」

 レイヤーの表示を切り替えてグラデーションをオーバーレイする。

 

f:id:hiyokosabrey:20191022002535j:plain

 

 「自分のコメントだけ文字の色が違うのいいかも!」

 「こうして見てると、左右の陣営がハッキリしてきて、仕様が分かりやすくなった分、中央入力タイプだっけ?この真ん中のタイプは迷ってる感があるね」

 「やっぱり、書き始める前にどちらの陣営に書くか決ってると思うんスよ」

 「だな。じゃぁこっちの左右分かれた方で行こう!」

 よし、なんとか決まった。両方実装してオプションで切り替えようとか言い出したらどうしようか不安だったけど、無事着地できて一安心だ。両方とも実装というのは、改造やら調整等メンテナンスのコストが地味に重たくなるので避けたかった。

 両サイドで入力する方が、画面は少しうるさくはなるけど結果的に分かりやすくできそうだ。中央入力のタイプはまとまっているけど、操作後の結果が想像しにくいので、操作事故の可能性も想定しないといけない。

 「ちょっとだけ改造してみますね」といってエディタを開く。

 操作しながら気になった質問を投げる。

 「ちなみに、どちらの陣営でもないコメントを送りたいときどうします?」

 考えてもらってる間にフキダシWidgetの左右反転表示するためのマクロを編集する。

 

f:id:hiyokosabrey:20191024225434p:plain

 

 ちょっとの間、ノードの編集を眺めていたディレクターが口を開く。

 「まぁ部屋を作って集まるメンバーといえば、どちらかの知り合いだろうし、まったく知らないプレイヤーを観戦することはほぼないと思うから、おのずと知人の方に付くと思う。それとなく応援って感じで。むしろ左右に分かれることで、プレイに関係ないコメントが自然と控えられたりしない?まぁ使い始めたら、意外と気にしないかもしれないし、また違った方向に向かうこともあるかもな。」

 「確かに」と谷山田が受ける。

 UIは考えつく限りの使い方を想定して設計するけど、やっぱり想定外の使われ方をされることがあるのが面白いし、怖いところでもある。

 左右に分けた入力仕様と表示についてそんなに気にしなくてもいいかもと、今更ディレクター自身が言うと身も蓋も無い気がするけど、今は試せる表現を少しでも探っておこう。

 

 次にテキスト入力Widget を開いて、キャンバス上でカラーを付ける。

 

f:id:hiyokosabrey:20191024231228p:plain

 

 「まぁ、いっかいこれでやってみて、キビシイ意見が出るようだったら、サイアク片側だけにしよう。QAメンバーに伺ってからだと遅いかな?大丈夫?」

 ディレクターはそう言いながら、チラっとリードプロフラマの南河原さんに視線を送る。

 「QAのキックオフは来月やったと思うけど、そのころはまだネットワークの準備できてないんで、今日みたいにエディタで触らせるくらいやけど。いい?」

 「あ、意見聞くだけなんで僕の方から話ししておきます。」

 この辺をさくっと融通できるディレクターのポジションは強い。

 ロケテストやユーザーテスト、フォーカステストなどのリリース前に行うテストで問題点をあぶり出せるのは理想だけど、テスターの確保やNDA(秘密保持契約)等の手続き関連、場所のセッティング、いろいろやることが多く人員に余裕のないプロダクションだと、中の人は面倒がって積極的にやろうとはしない。うちも会社規模はそんなに大きくはない。5年ほど前にQA部門が新設されたけど、常駐のスタッフは2名しかいなくて、外のQA専門会社との窓口役が基本業務だと聞いている。すでにいくつかのプロジェクトを扱っているので、それなりの経験則があると期待されているのだろう。また、QAから上がってくるレポートはすべてディレクターがチェックして、仕様とするか修正するかのジャッジを行う。そこでのやり取りで得た信頼もあるだろう。そんなQAメンバーにも意見がもらえる流れになったので、ここはありがたく受けよう。

 コンパイルが成功したので再生する。

 

  「できました。」

 「どれどれ、おー」

 さっそく入力して確かめる。

 

f:id:hiyokosabrey:20191024233931j:plain

 

 「色は後で調整します」

 「そうっスね、このカラーだとジェンダーを意識しちゃいそうです」

 「いやぁ面白いねUE4

 ここで別のアイデアを思い付いた。「ちょっとすみません」といいながら再びPhotoshopをアクティブにする。

 「フキダシの色はそのままでもいいかもしれません。」

 「エンジンの方ではこのグラデーションを置いてないのですが」

 といいながらレイヤーを操作する。

 

f:id:hiyokosabrey:20191026110242j:plain

 

 「なるほどね、フキダシの色は中立で、対立している感じはマイルドになった。」

 「ただ、背景の色味によっては効果的じゃない気もします」

 自分で思いついて試してみたものの、それほどキャッチーではなかった。

 ここで、もう一つ思いついていたのがあったので、この際試してみてもらおう。

 「あ、もうちょっと時間いいですか? いいアイデアを思いつきました」

 「いいよ」

 「適当に歓談でもしておいてください」

 何が始まるのかと、見守る空気になったけど、すぐに谷山田が話題を提供してくれたので、そこからしばし歓談タイムとなった。

 「え!マジで?!」「めっちゃ強いっス」「俺なんか」「40連したけど出なかった」「ちょ~」「!」「ほんまや!」「?」「だー」 ・・・

 

 「できました」

 

f:id:hiyokosabrey:20191026111750j:plain

 

 「おお!いいじゃんいいじゃん!普通のチャット感あるし、さりげなくボタンの色がフキダシにも入ってて、まったくの無関係じゃなくなってるのイイネ」

 「これっスよ!」

 谷山田の尻馬感のある言葉に苦笑いしつつも、手応えを感じられて気分が軽くなった。 

 「これで行きましょう」

 「南河原さん大丈夫っスか?」

 「ええんちゃう」

 「じゃぁこれで進めますね」

 「よろしくです」

 これでお披露目会はお開きとなった。

 

 結局、完全決定とはならず、少し先送りになった分は油断はできないけど、少なくともモックのせいでチャットそのものに難色が示されることはなかったし、採用さ入力方式のタイプがひとつに決まったので、この先の機能を作り込むのに迷いがなくなったのはありがたい。いくつか問題点を検討できたし、解決策も見えてきた。やっぱりモックを作ってすぐに触れるのはいいなぁ。と一人UE4のありがたみを噛みしめる。

 

 さっそくデザインの改修について、Widgetのパーツをどう変えようか考えながら、コーヒーでも買いに行こうと席を立った。あ、ついでにスタンプの進捗でも見に行ってみよう。

 

 

※この物語はフィクションです。実在の人物・団体などとはいっさい関係がありません。

チャットUIを作る

11

 再生してみると、テキストを何度か入力してうちにズレてくる。手で改行を入れる一つ前の入力タイプでは、自然とこの問題を回避できていたのだ。表示バグというやつだ。

 入力方法の違う2タイプを作ったことで、結果的にモックの段階で発見できたのは良かったが、さて、どうやって解決したものか・・・

 フキダシWidgetの高さを返すところで、UIDをセットしている部分をちょこっといじる。

f:id:hiyokosabrey:20191009214754p:plain

適当な文字列を入力して確認してみる。

f:id:hiyokosabrey:20191009214627j:plain

 

 どうやら自動改行が入った場合、何かしらの条件で正しく行数が計測されないということがわかった。間の半角スペースの数を変えたり、文字を変えたり、いろいろ試してみたけど、今一つこれだ、という法則のようなものにたどり着けない。入力のタイミング的なものかもしれないと思い、クリップボードに入れておいた文字列を適当なタイミングで出力してみると、これは必ず同じ値になる。ということはやはり、改行処理にかかる時間か何かが関係している気がする。Get Desired Size のタイミングなのだろうか。

 改行のルールも今一つピンとこない。適当な文字列を入力して試しているときに気づいたのがこれ。

f:id:hiyokosabrey:20191009222920j:plain

 2つ目の入力で aaa a aaa と入力すると短いセンテンスなのに改行された。真ん中の a が冠詞として判断されたのかと思い、アルファベットb~zに変えて入力してみたら、 i j l のアイ、ジェイ、エル については改行されなかった。文字の幅?と思ってたら、m と w でも改行されなかった。まだまだ自分の知識の及ばない世界のラテン文字文化圏のルールか何かがあるのかもしれない。こんな短いセンテンスでも改行されるうえに、サイズ取得がうまくいかないということで、より謎が深まってしまった。

 首をひねっていると、周りで人の動く気配がする。時計を見ると12時だった。もうこんな時間か。結構集中していたようで伸びをして固くなった背中と首をほぐす。よし気分転換しよう。

 

  外に出てみると、気持ちよく晴れていた。澄んだ空の色がさっきのモヤモヤした気分をいっとき忘れさせてくれたけど、すぐに思い出してしまったので、いつもの書店へは向かわず、コンビニに行くことにした。このまま歩いていると会社に戻るのを忘れてしまいそうで、できれば永遠に忘れてしまいたかったけど、早くこのモヤモヤを解消したいという気持ちが、わずかな責任感という協力を得て勝利を収めた。

 コンビニで適当な炭水化物と適当なお茶を入手。適当なルートを歩いていたらいつの間にか自分の椅子に腰掛けていた。対策を考えながらぼんやりと歩いていたので、まったく気分転換できていない気がする。適当に選んだおにぎりを食べながらノードをいじる。

 Auto Wrapping(自動改行)に頼る以上は、この処理が終ってから高さを調べればいいのだけど、それはタイミング的にいつなのか分からない。描画処理が少しでも進めば良さそうだけど、Tick使ってみるか。

 テキストを流し込んだ後の部分で接続を切る。

f:id:hiyokosabrey:20191010004306p:plain

 Force Layout Prepass ノードとTick をつなぐんだけど、1回でいいので、Do Once ノード入れておこう。

f:id:hiyokosabrey:20191010004617p:plain

 これで再生してみる。

f:id:hiyokosabrey:20191010004839j:plain

 ダメか。

 もう少し進めたらうまくいくのかな。ものは試しで、delayノードを入れてみよう。

まずは Duration を 0.0 で。f:id:hiyokosabrey:20191010005222p:plain

 これでどうだ?

f:id:hiyokosabrey:20191010005459j:plain

 お、端数が出てきた。なんか精度が上がった? でもうまくいってるっぽい。

 ちょっとスマートじゃないけどなんとかなったかな。

 

 最初から改行が入っていると問題はないので、このTick処理が負担になるようだったら、入力フォームをあらかじめ3行にしてしまうのが無難だろう。とにかくモックを仕上げて入力のタイプを決めないと。

 

 ちょうど誰かがセットした目覚ましのアラームが昼休みの終了を告げる。

 UIDの表示を戻して、谷山田を呼びに行く。ゲーム系情報サイトを眺めていた彼は声をかけるとすぐに来てくれた。入力タイプの違う2つを順番に再生して触らせる。

 「うん、うん、いいっスね。」

 「ほうほう」

 ひとしきり触ってから、

 「Dに見てもらいましょう。ちょこっと予定聞いてきますね。」

 と言って立ち上がると、鳥囃子ディレクターのところに向かった。そんなに広いフロアではないし、ほとんど人がうろうろしない職場なので、パーテーションより背が高く動く物体は目で追いやすい。

 どうやら席にいないようだ。そのまま彼は自分の席でPCに向かって操作を始めた。恐らく予定管理ソフトで予定を確認しているのだろう。しばらくして再びこちらに戻ってきた。

 「今ミーティングみたいッスね。予定入れておくんで、後でまたお願いします。」

 「分かった。」

 ということで一時解散することになり、谷山田は自席に戻って行った。コーヒーを飲みたくなったので、いつもの自動販売機コーナーへと向かう。エレベーターから降りると人影があった。

 「あぁ、なんとバッタリ!」

 目が会った。立木坂だった。

 「お、おう、おつかれ。」

 いきなりな言葉で咄嗟に切り返すことができず、動揺してしまった。疲れるから妙なタイミングでこちらの意表を突くのをやめていただきたい。

 「おつかれさまです~」

 手に何も持ってないところを見るとまだ買っていないようだ。すると、

 「あ、どうぞどうぞ。」

 少し下がって自販機の前を空けてくれた。ホットかアイスかも決めていなかったので、急に譲られても戸惑ってしまうじゃないか。

 「いやいやまだ決めてないし、お先にどうぞ。」

 と、こちらも譲り返して手振りで促すが、

 「そんなそんな、わたしもまだ決められないんで大丈夫です。」

 と、一度譲ったものは譲らないという固い意思を感じたので、さすがに折れることにする。

 「じゃお言葉に甘えて。」

 と言ったものの、さっきからのやり取りで、何が飲みたかったのかを一瞬忘れてしまい、いくつもある商品ボタンを一通り眺めてから、ようやくブラックしか飲まないのを思い出した。なんだか妙な汗が出てる気がしてアイスコーヒーのボタンを押す。

  カップが出てくるまで時間がかかるやつである。30という数字が点灯。なんとなく気詰まり感を覚えたので、気になってたことを訊いてみる。

 「そういえば、スタンプの話ってうちのプロデューサーから直々に?」

 「あ、そうです。ふらっとやってきて突然、絵描けるよね?って訊かれて、えぇ、まぁ・・・って答えたら、OKって一言を残して去って行ったんですよ。不吉な予感がするなぁって思ってたら、そのあとスグにチームメンバー全員ミーティングルームに召集されて、プロジェクト見直しの話が出て、あぁって。アイツそれを先に知ってて確認しに来たんですよ?間違ってないけどなんかみんなより先に動けるのってズルイ気がしません?」

 一気に吐き出すように返してきた。

 「アイツって・・・まぁ直前のミーティングで決定したその足で声をかけてきただけだろう?チームのディレクターもその場にいただろうし。」

 その手の話をプロデューサーが後から知ることはないだろう。なにしろプロデューサーが決めるんだから。とツッコミたくなったが、そこまで言うのはさすがにくどい気がしたので飲み込む。

 自販機のカウントが0になり、カップを匿っていた扉がぎこちなく開く。奥の紙コップをそっと取り出す。

 「まぁ、とりあえずイラストよろしく。」

 自販機の前を空けると、

 「了解です。頑張ります。」

 と明るい声で答えた。何気なく顔を見て、初めて見る笑顔に、思わずドキリとした。

 「で、決まった?」

 動揺を隠しながら、自販機を指さすと、

 「いえ、いいんです、本当はちょっとネタに詰まって、誰かと話したかっただけなんで。顔が見れてよかったです。戻りますか。」

 「え、あ、うん。」

 なぜか一緒に戻ることになり、エレベーターの矢印ボタンを押してカゴが降りてくるのを待つ。

 「わたしもアンリアルエンジン触ってみようと思うんですけど、いろいろ教えてもらってもいいですか?」

 「いいよ。」

 「家で勉強できたらいいなぁ。家のパソコンで動くかなぁ。」

 「いつ頃買ったか覚えてる?」

 買った年が分かれば、スペックについてはおおよその見当を付けることができる。

 「いつだったかなぁ?うーん。」

 そこで到着音が鳴ってタイムアップ。エレベータの扉が開くと、先に何人か乗っていて、見知った顔もあった。

 

 フロアに戻ると、予定管理ソフトから通知が来ていた。予約者が谷山田で、出席依頼に自分と、鳥囃子、南河原となっていた。場所は空欄になっている。ミーティングルームじゃないということは、おそらくチャット画面のお披露目会で間違いないだろう。集合時間まで、まだ時間があるな。だいぶ氷の溶けたコーヒーを飲む。ときどき唇に当たる氷の粒を感じながら、さっきの立木坂との会話を思い出す。いつものコーヒーと違う味がした。

 

 

 つづく

 

 

 

 

 

 

 

 

 

 

 

 

 

 

チャットUIを作る

10

 翌日は晴れて澄んだ青空が広がっていた。抱えた屈託が全て溶けてクリアになるようなそんな色。こんな青空を見ていると、どこか遠くに行きたくなる病が発症しそうになる。なんとかチャット画面のモックがそれとなく出来上がってきたのを思い出し、続きを作らないといけない使命感をサルベージしながら移動。ようやく会社にたどり着いた。

 今日は少し早く着いたのでいつもよりフロアは静かだった。PCを起動しUE4のエディタが開くまでの間に昨日帰ってから頭の中で考えていた構造を思い出す。

 テキスト入力を左右に分離させたバージョンを作るにあたって、用意するWidgetはいくつか考えてみた。

 最初、左右反転した見た目になるのであれば、フキダシWidgetのように、片側だけ作って2個置けば?というアイデアが浮かんだ。ただし片方でテキスト入力中、もう一方は触ることができない状態にする必要がある。2つが同時にアクティブになることはない。左右の入力Widget間で相互にやり取りするのはとても面倒だ。かといって親のレイアウトWidget経由も手間が増えるだけでまどろっこしい印象。

 

 ここは一つのWidgetにキャンバスパネルによって左右2つの入力部分を管理するのがよさそうだと思い至った。さらに親のレイアウトWidgetで受け取る際にバインドしている処理も変えなくても済む。

 

f:id:hiyokosabrey:20191002191728p:plain

 

 

 まずは今のテキスト入力Widgetを複製して、これを改造していこう。コンテンツブラウザからテキスト入力Widgetを複製する。

f:id:hiyokosabrey:20190929212152p:plain

f:id:hiyokosabrey:20190929212453p:plain

 とりあえず名前を wb_WriteBox にしておこう。

 使えそうなパーツは流用する。入力用の下敷きは、いまのフキダシと同じテクスチャにして調整。ボタンは一つだけでいいので片方を消す。テキスト入力は1行入力だからMultiLineじゃないやつに差し替える。

f:id:hiyokosabrey:20190929103234p:plain

 大きさはフキダシの許容文字数分横に伸ばしておいてっと・・・こんなもんかな。顔アイコンは適当なやつをセットしておこう。

 

 入力開始ボタンのテクスチャはこんな感じでいいかな。

f:id:hiyokosabrey:20190928230958p:plain

 

 しばらくパーツの調整に手間取ったが、なんとか整った。このテキスト入力で必要な「状態」は3つ。

 

● テキスト入力中

f:id:hiyokosabrey:20190928002555p:plain

 ユーザーがコメントを入力中はこの状態。

 

 

● 待機中  [DEFAULT] 

f:id:hiyokosabrey:20190928002621p:plain

 入力する前の状態。左右どちらも同じ。この状態のみ入力を開始するための +ボタンが表示される。

 

 

● 非アクティブ [N/A] 

f:id:hiyokosabrey:20190928002639p:plain

 反対側で入力を行っている間の状態。見た目に存在感を弱く。CanvasPanelの透明度を変えるとラクチン。

 

 

 ヒエラルキーはこんな状態。

f:id:hiyokosabrey:20190928003255p:plain

 反対側も同じ構成になるので、キャンバスパネルを一つ追加してある。

このCanvasPanel_Left をまるっと複製して 名前を CanvasPanel_Right に変更。

見た目にシンメトリーになるように調整。

f:id:hiyokosabrey:20190928003811p:plain

 ヒエラルキーはこうなった。

f:id:hiyokosabrey:20190928004527p:plain

 

 入力中、待機中、非アクティブと、見た目にこの3つの状態を遷移させるためにアニメーションを使って切り替える。いきなり切り替わると味気ないので、入力開始と終了時くらいは短いながらもアニメーションを作ってみよう。状態遷移のイメージはこんな感じになると思う。

 

f:id:hiyokosabrey:20190929004248p:plain 

 

 この遷移イメージを元に、左と右それぞれにアニメーションを用意する。待機と非アクティブについては、状態の記憶として利用するので、尺は不要。開始 0.0 の位置にバシっと確実に状態を切り替えるためのキーを打つだけでいい。

 

f:id:hiyokosabrey:20190929092607p:plain

 


 これをブループリントの方で組んでいこう。

 +ボタンは、前のWidgetから残したパーツで、Buttonコンポーネントだから OnClickイベントが有効だ。そこへアニメーションの再生ノードをつなぐ。まずは左側のクリック。

f:id:hiyokosabrey:20190929095541p:plain

 右側のクリックも同じ要領で、再生するアニメーションが変わる。

f:id:hiyokosabrey:20190929095654p:plain

 Widget の Buttonコンポーネントは、画面に表示されている間はクリックを受け付けてくれる。なので、クリックした直後にアニメーションで Visibility を Collapse にするキーを打っておくと必要なクリックを防ぐことができて安全。

 Play Animationノードは、再生のトリガーでしかないので、再生を開始したらすぐに次のノードに処理が流れてくれる。ただ基本的に終わりを待ってくれないので、終わってから何かする場合だけ、特別な処理が必要になる。

 

 次は投稿の処理だけど、送信ボタンが無いので何をトリガーにしようか。確かEnterキーを判定するイベントがあったはず。

f:id:hiyokosabrey:20190929113203p:plain

 

 あった。たぶんこれだ。

f:id:hiyokosabrey:20190929202851p:plain

  Commit Method は ENumのピンだから、判定用の Equalノードが使えそう。

f:id:hiyokosabrey:20190929203134p:plain

 あった!

 ということは、さっそくプルダウンを見てみると・・・

f:id:hiyokosabrey:20190929203325p:plain

 On Enter !

 これをブランチノードで判定すればよさそうな気がする。True の時に、用意してあった、イベントディスパッチャーにテキストのピンをつなげれば入力した内容が送信できるはず。

f:id:hiyokosabrey:20190929204312p:plain

 左側用だから、isLeft を True にしておく。

 右側のも同じように作ってから、テストしてみよう。

f:id:hiyokosabrey:20190929204326p:plain

 

 テストするためには、レイアウトWidgetに配置してやらなければいけない。

 と、その前にコイツをひとまずどかす。

f:id:hiyokosabrey:20190929204705p:plain

 Visibility を Collapse にしてしまえばいいのだけど、ここは Event Pre Construct を使おう。

f:id:hiyokosabrey:20190929204848p:plain

 これで、コンパイルした瞬間にキャンバスから消えてくれる。

f:id:hiyokosabrey:20190929205135p:plain

 ヒエラルキーの可視オプションを見えないようにすればいい。存在を戻すときはノードの接続を切るだけなので安全。

f:id:hiyokosabrey:20190929205629p:plain

 これで、改めて新しい入力Widget を配置できる。

f:id:hiyokosabrey:20190929205847p:plain

 

 テキストをバインドしてイベントディスパッチャーから受け取っているところがあったから、そこをつなぎ変えないと。

 今こうなってる。

f:id:hiyokosabrey:20190929213531p:plain


 wb_WriteBoxCenter のバインドを一時的に外して、今回追加した wb_WriteBox からバインドノードを取り出す。

f:id:hiyokosabrey:20190929213355p:plain

 最終的にどちらの入力方法で行くか決まれば、片方は消滅することになる。

 よし、再生してみるか。

f:id:hiyokosabrey:20190929224404g:plain

 よしよし、うまくいってる。

 

 でも、フキダシの開始位置が低くて重なってしまっているので、VerticalBox の位置を上にあげよう。

f:id:hiyokosabrey:20190929224747p:plain

 あとは、OnEnterで送信した後の処理だな。元の待機状態に戻さないと。レイアウトWidgetコンパイルして編集完了。続いてテキスト入力Widgetの方。

 

 この続きに、閉じて元に戻す処理をつないでいく。

f:id:hiyokosabrey:20190929231149p:plain

f:id:hiyokosabrey:20190929232341p:plain

 左と右、順番に気を付けながら元に戻すためのアニメーションを再生していく。

 

 イベント全体はこんな感じ。

f:id:hiyokosabrey:20190929232604p:plain

 

 再生してみる。 

f:id:hiyokosabrey:20190930230650g:plain

  特に問題はなさそうだ。あとは顔アイコンのセットと入力の途中キャンセル、改行ができたら見せられるかな。

 とりあえず顔アイコンを渡すところを作ってしまおう。テキスト入力Widgetに受け取り用の関数を用意すればいいだろう。 

f:id:hiyokosabrey:20190930233310p:plain

 今はこの関数をに対してレイアウトWidgetから顔アイコンのテクスチャ渡すようにすればいい。

f:id:hiyokosabrey:20190930233732p:plain

 このあたりは画面の初期化処理としてイベントにしておいた方がいいかもしれない。Event Constructで処理している場合、Widget がViewportに追加された段階でデータが渡されていないといけないからだ。データの受け渡しのタイミングはネットワークが絡むと思うようにはコントロールできないことが多い。後でプログラマと相談しよう。

 

 UI表示を作る場合、Viewport に追加された瞬間に動き出すやつと、そうでないやつがある。追加された瞬間動きだすのはワンショット的に役目を果たすと消滅するタイプ。主にエフェクトとか演出系の表示。そうでない方は、基本的に常駐することになる。もう少し細かく分けると、常時表示されていて更新されていくタイプと、適宜必要に応じて呼び出されて表示するタイプに分けられる。これらのタイプを用途に合わせて設計する。プログラマに相談する際にも、この辺の分類を念頭に入れて話すとスムーズに仕様を決めることができる。と考えているのだけれど、なかなかこういった話ができるUIデザイナーが周りにいないのはさみしい。

 さて、わかりやすくコメントをつけておいて、あとで改造しやすくしたところで、次はキャンセル処理か。

 入力用Widgetの外をクリックしたら入力をキャンセルして閉じるようにしてみよう。感触が良くなかったら、キャンセルボタンを置くことになるかな。

 

 テキスト入力エリア以外のクリック判定なので レイアウトWidgetに判定させるのがよさそう。

 OnMouseButtonDown 関数をオーバーライドしてと、

f:id:hiyokosabrey:20191001085914p:plain

f:id:hiyokosabrey:20191001085414p:plain

 ここに判定用のパーツを持ってくるわけだけど、Widgetのクリック検出は、Visible状態じゃないと判定してくれないので、キャンバスに何も置いていないとこの関数が呼び出されることはない。というわけで、キャンバスにテキスト入力Widgetを避けて、CanvasPanelを配置する。

f:id:hiyokosabrey:20191001091446p:plain

 このCanbasPanel の Visibility の設定をVisibleに 変更しておく。

f:id:hiyokosabrey:20191001091653p:plain

 名前を CanvasPanel_CancelArea としておこう。

 これをさっきオーバーライドした関数の中に置く。

f:id:hiyokosabrey:20191001092051p:plain

 レイアウトWidgetのキャンバスに置かれているすべてのパーツが検出対象で、何かあるところでマウスボタンを押すと、この関数が呼び出される。特定のパーツのみを対象にしたい。ここで判定してはじくために isHoverd ノードを使う。hover は WebページのデザインでCSSを触ったことがあるならピンとくるはず。このノードはカーソルが乗っているかどうかをブーリアンの値で返してくれるノード。テストするためにPrintStringをつないでみる。

f:id:hiyokosabrey:20191001093119p:plain

 コンパイルするとWarningが出た。

f:id:hiyokosabrey:20191001093414p:plain

 そうだ、ReturnValue が空いてるとダメなのだった。Handled ノードをつないで祈る。鎮まり給へ!

f:id:hiyokosabrey:20191001093603p:plain

 鎮まった。

 テストしてみよう。

f:id:hiyokosabrey:20191001151550p:plain

 よし、ちゃんと反応してる。

 

 今度は閉じるイベント作って、それを呼び出せばいい。テキスト入力Widgetを編集しよう。

 ただ呼び出すだけでいいのか?勝手にいろんなタイミングでクリックされるから、都合の悪い時がある。あと、入力中のやつはキャンセルするけど、非アクティブの方はすでに閉じてる。となると、フラグが要るな。ブーリアン型の変数を2つ用意しよう。

f:id:hiyokosabrey:20191001152559p:plain

 まずこの値をセットするはここだ。+ のButtonをクリック処理するところ。

f:id:hiyokosabrey:20191001153024p:plain

f:id:hiyokosabrey:20191001153041p:plain

 

 このフラグを元にカスタムイベントで閉じることが可能かチェックする。

f:id:hiyokosabrey:20191001153615p:plain

 このあとに閉じるアニメーションをつなぐんだけど、ちょうどいい場所があった。

f:id:hiyokosabrey:20191001153839p:plain

 すっぽり!

 送信した直後だから、ただ閉じる処理しかしていない。で、この処理の一番最後尾でフラグを元に戻せばいい。

f:id:hiyokosabrey:20191001154238p:plain

 これで、テキスト入力Widget側の準備はできた。レイアウトWidgetの OnMouseButtonDown 関数に戻る。テストでPrintStringをつないでいたところを差し替える。さっき準備したカスタムイベントを呼び出す。f:id:hiyokosabrey:20191001154455p:plain

 これでOK。

 テストしてみよう。

f:id:hiyokosabrey:20191001160146g:plain

 よし、これで良さそうだ。あとはフキダシの改行表示かな。うまくいくといいけど。

 

 

 しばらくキャンバスのパーツを触っていると、SizeBoxにそれっぽいパラメータを発見。あと、TextBlock の改行設定を入れておく必要があった。

 SizeBoxの子供の TextBlock_Body に改行無しのダミーテキストを入れて、Wrappingの設定を変更する。

f:id:hiyokosabrey:20191001181436p:plain

f:id:hiyokosabrey:20191001181642p:plain

f:id:hiyokosabrey:20191002174005p:plain


 で、SiizeBoxの方も設定を変える。

f:id:hiyokosabrey:20191001175946p:plain

 まず SizeBox の Size To Content を一時的に無効にする。

f:id:hiyokosabrey:20191001180237p:plain

  いい感じのサイズになるようにSizeXとYを調整。

 そして、Child Layout カテゴリの  Max Desired WidthMax Desired Height を有効にして Size X と Size Y を移植する。Size To Content のチェックを戻せばOK。

f:id:hiyokosabrey:20191001180755p:plain

 これで大丈夫。

 テストしてみよう。

f:id:hiyokosabrey:20191001200706g:plain

 よかった。なんとかできた。

 

 あとはプレゼンに向けてちょっと細工を。レイアウトWidgetの送信処理のところを、2種類のテキスト入力Widgetを両方有効にしておく。

f:id:hiyokosabrey:20191001222245p:plain

 Event Pre Construct のところで、切り替えられるようにする。

f:id:hiyokosabrey:20191001223509p:plain

 コンパイルが必要だけど、ブランチノードのCondition を切り替えてコンパイルするだけで切り替えができる。確認しておこう。

 

 ?!・・・あれ?なんかスクロールがおかしいな。

 

 

つづく