みつまめ杏仁

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

斜めになったメニューボタンでもマウスに反応するか試してみた

COVID-19のなんやかんやで緊張しながら日々過ごしていたらもうお盆の時期。心なしかセミの声も元気がないような気もする。近所の田んぼでは稲の穂もチラホラ見えはじめたのでまた花粉に苦しむことになる予感・・・。

 

先日 Twitterで、シアーで傾けたUIでもマウスオーバーを検出できるのか気になって試してみた、というのを呟きました。

f:id:hiyokosabrey:20200812114405p:plain

動画はここ ↓ で確認できます

 

 

試してみようと思ったきっかけは、今までの体験が元になっています。

こういったナナメのデザインを作ると、

矩形の範囲で検出する方がコストが安く時間がかからないのに、なんてデザインしてくれるんだ、とプログラマに渋い顔をされたものです。

 

f:id:hiyokosabrey:20200815133948p:plain

 

インハウスツールの場合、ツール開発者がよほど暇にならない限りデザイナーの気まぐれに先回りするような人はいないのが普通だとは思います。

さてUE4はどうかな~? フォートナイトでも見たし、大丈夫だろうきっと。

というわけで試して出来上がったものを記事にします。

 

 

用意したWidgetは2つ。

 

まずは子のWidgetから用意します。

 UMGのキャンバスに 下敷きとなる Image を置いて、手前にアイコン用の Image とラベル用の TextBlock を置きます。

f:id:hiyokosabrey:20200812161457p:plain

 ヒエラルキーは以下の状態

f:id:hiyokosabrey:20200812161857p:plain

ここで下敷きの Image に Shear(シアー)を適用します。

f:id:hiyokosabrey:20200812162146p:plain

 

f:id:hiyokosabrey:20200812162259p:plain

 アイコンとテキストの位置がおかしくなるので整えます。

 

 

Hierarchyに最初から置かれている CanvasPanel を SizeBox でラッピングします。

CanvasPanel で右クリックして

Wrap With ... > SizeBox を選択

f:id:hiyokosabrey:20200812163045p:plain

この操作で、CanvasPanel 以下を SizeBox の子供にします。一つ上の階層を増やして包むイメージで「Wrap」。

f:id:hiyokosabrey:20200812163713p:plain

この階層構造をやめたいときは、SizeBox のところで右クリックして

Replace with ... > Replace With Child を選択 するとSizeBoxはいなくなります。

f:id:hiyokosabrey:20200812225255p:plain

最上階のものはドラッグしても入れ替えができないようです。

 

ここからSizeBoxの調整をします。

UMGの編集用エリア(ビジュアルデザイナ)右上にあるボタンを切り替えます。

f:id:hiyokosabrey:20200812230046p:plain

Desired か Desired on Screen にします。

これは描画の際のアスペクト比、解像度によるスケーリングをシミュレーションしたりしていろんなデバイスによる差を確認しながら、レイアウト作業をするためのスイッチです。

この 子Widgetの場合、並べるのは別の 親Widgetになるので、レイアウト作業としては画面に対してではなく、パーツ同士の位置関係まで、ということになります。

 

わかりやすくするために SizeBox(またはCanvasPanel) を選んだ状態で切り替えてみます。

f:id:hiyokosabrey:20200812231202p:plain

「Desired」 は 「望まれた」 という意味合いなので、ここでの使い方としては

  《 UE4は、SizeBoxの大きさとしては、このように認識しているよ 》

という程度の理解で大丈夫だと思います。

 

この子Widgetを 並べる際に、SizeBox の持つサイズが、 利用されることになります。

f:id:hiyokosabrey:20200812235526p:plain

( ↑ 表示イメージ )

今の状態のままだと、左右にスキマがないのでくっついてしまいます。

後からでも調整できるのですが、編集の流れで行ったり来たりすると説明がややこしくなりそうなので、スキマの空け方を書いてしまいます。

ひとまず左右に 5 ずつ 空けることにします。

 

まず、SizeBoxはポジション等のSlot(Canvas Panel Slot) を持たないので、位置を変えることはできません。なので、中のImageパーツたちを移動させます。今回右に(X方向) +5 移動させました。フォームの値に +5 を追記してEnterキーを押すと加算してくれるので便利。

f:id:hiyokosabrey:20200813001813g:plain

CanvasPanel の Render Transform で 移動させるとまとめて移動できて簡単ですが、移動アニメーションを考えるときにややこしくなるので、できるだけRender Transformは触らないでおきます。編集時のバウンディングボックス(点線の四角)が増えてしまうのも回避できます。後々の修正・調整のためにできる限りシンプルなデータ作成を心掛けたいものです。

f:id:hiyokosabrey:20200813001124g:plain

中のパーツが右に移動した分、SizeBoxの幅が少し大きくなりました。

左に余白ができたので、今度は右側にも余白を足します。

ここで、SizeBoxの出番です。

今のSizeBox の幅を調べて、そこに +5 すればOK。

さて困りました。どうやってSizeBoxの幅を調べたものか・・・

 

いくつか試した方法があるのでメモとして残しておきます。

 

調べ方その1 Get Desired Size ノードで調べる

一旦 画面に表示して get Desired Size ノードを使うと精確な値が取れます。

SizeBoxの is Variable にチェックをつけておいて、グラフに下のようなノードをつなぎます。

f:id:hiyokosabrey:20200813003553p:plain

Delayノードは確実に値を取るための保険です。値が取れればこのノードたちは不要になります。

 

適当に新規レベルを作成してViewportに描画。

f:id:hiyokosabrey:20200813004003p:plain

PrintString が教えてくれます。

f:id:hiyokosabrey:20200813004019p:plain

 

ブループリントに慣れててもちょっと面倒に感じます。

 

次はちょっと特殊な方法。

 

調べ方その2 マテリアルで調べる

先日ツイートしたネタを使います。

 

このマテリアルは プロジェクトに1個作っておいて損はないやつです。

f:id:hiyokosabrey:20200813005408p:plain

TexCoordノードのIndex を3 にして使います。

f:id:hiyokosabrey:20200813005610p:plain

このマテリアルを、CanvasPanelの下に チェック用の Image を一つ追加して適用します。確認出来たら捨てるか非表示にしておくといいです。

f:id:hiyokosabrey:20200813005935p:plain

表示順は一番手前。

アンカーをストレッチにしてスキマなく広げます。

f:id:hiyokosabrey:20200813010106p:plain

この Image パーツに マテリアルをセットすればOK。

テクスチャをセットするところにマテリアルをセットできます。

f:id:hiyokosabrey:20200813010231p:plain

ただ、この方法で気を付けないといけないのが、表示倍率。

f:id:hiyokosabrey:20200813010612p:plain

Zoom が 1:1 (等倍) = 本来のサイズ になります。

 

この方法のいいところは、 マテリアルをセットしたImageパーツを追加するだけで、すぐに確認できるというところです。ブループリントを編集しないのでコンパイルの必要がありません。

 

 

調べ方その3 地道に探る

SizeBox の持つパラメータに、Max Desired Width というのがあります。

f:id:hiyokosabrey:20200813011517p:plain

ここに値を入れていって、これ以上変化しなくなる最大値をみつけます

 

最初に100とか200っみたいな値を入れてから、マウスで増減させます。

キャンバスの緑のラインが動くので、それが止まるところが最大値になります。

f:id:hiyokosabrey:20200813012522g:plain

ラインの変化が止まったらマウスをすぐに止めます。ここからは手入力で少しずつ探っていきます。

・・・

208 を 207 に変更。 ラインは変化しない。

207 を 206 に変更。 ラインは変化しない。 

206 を 205 に変更。 ラインは変化しない。

205 を 204 に変更。 ラインが動いた。

ということは 204 を 205 に変更。 ラインが動いた。

さらに 205 を 206 に変更。 ラインは変化しない。

 

ということでめでたく 205 という値をゲット! なかなか気の長い話です。

 正直頭のいい方法かどうかは気になるところですが、余計なパーツを追加したりしないので、アセットはピュアな状態のままです。

 

 

 

とりあえずこの3つくらいしか思いつかなかったです。
他にいい方法があるかもなので、教えていただけると嬉しいです。

 

で、この 205 に +5 した値が最終の表示サイズになります。

これを SizeBox の 持つパラメータ Width Override にセットします。

f:id:hiyokosabrey:20200813013658p:plain

これで左右に余白が生まれました。

f:id:hiyokosabrey:20200813013936p:plain

( ↑ 表示イメージ )

 

キャンバスの役者が揃いました。

 

ここからはマウスカーソル(ポインタ)が乗った時の処理を作っていきます。

 

まず、どういった見せ方にするか考えます。

ふるまいとして求められるのは、

 

  • マウスカーソル(ポインタ)が乗っていることをわからせたい
  • 決定時の演出とは違う見せ方をしたい

 

このあたりで十分だと思います。

アクションゲームなどは、プレイ中にノンビリできないので、できるだけ目立つ動きで誤操作を起こさないように表示するのが大事です。またマウスカーソル(ポインタ)やゲームパッド操作の場合、選択肢をどれにしようかな~って迷うことができるのも特徴になるので、カーソル的なハイライト表現も重要な表示要素です。もちろん決定時の表現も大事なフィードバックなので、決定前と決定後のメリハリを考えることになります。

 

いろんな作り方があるのですが、今回はマテリアルを使ったカーソル表現をやってみます。

ヒントになったのはこちらのツイート。

 

枠線を点滅させることにします。

f:id:hiyokosabrey:20200813233218g:plain

( ↑ イメージ )

 

マテリアルはこんな感じ

f:id:hiyokosabrey:20200815123425p:plain


部分を拡大しつつ説明していきます

 

まずは枠線を描く部分。

f:id:hiyokosabrey:20200815123616p:plain



任意の太さの枠線を描画サイズから割り出しておいて、カスタムノードで判定し塗分けています。

 左端のTexCoordノード(Uを押しながらクリック)は Detailsタブから Indexの値を 3に変更します。

f:id:hiyokosabrey:20200814214847p:plain

次(右側)の TexCoordノードは デフォルトのまま使います。

 

赤いサムネイルのノードは カスタムノードです。

HLSLのシェーダーコードを自前で用意して使うことができます。

詳しくは公式ドキュメント↓

Custom 表現式 | Unreal Engine Documentation

と、こちら↓のエントリー

pafuhana1213.hatenablog.com

カスタムノードは Detailsタブをいじっていきます。

f:id:hiyokosabrey:20200814215821p:plain

入力を3つに増やします。

Code のところに シェーダーコードを書きます。今回書いたのはこれです。

 

float f = (float)(Value>=Min)? 0 : 1;
float s = (float)(Value<=Max)? 0 : 1;
return (f+s);

 

? は 文字化けではなく 三項演算子ってやつです。ExcelVBAだったら Then に相当します。 : コロンはさしずめ Else ってとこですね。

オレンジ色は 入力ピンの名前です。fとsはなんとなく First とSecond です(適当)

UE4のフォーム内で改行する場合は、 Shift + Enter です。

もっと良い書き方がありそう・・・どなたかご教示いただけると嬉しいです。

 

で次は点滅部分

f:id:hiyokosabrey:20200815123904p:plain

Time に 大きい数値を掛けると点滅が速くなって、0.5 など小さい値を掛けると ゆっくりになります。

ConstantBiasScaleノードは、一定の幅を持った値に対して、調整するときに便利なノードです。

 

計算結果から想像するにたぶん中身はこんな感じだと思います。

f:id:hiyokosabrey:20200814231040p:plain

 

サインカーブは基本的に -1.0 ~ +1.0 の幅で変移するので、これを 0 ~ 1.0 の範囲に収めておかないと、マイナスの値の時に見た目がおかしくなりなります。(正規化とかNormalize とか言います)

Detailsタブから値を調整できますが、取り出したままつなぐだけで OK。

 

最後は、枠線の合成と着色する部分。

f:id:hiyokosabrey:20200815124356p:plain

Addの後の Clampノードは 値が 1.0をオーバーしないようにしています。

f:id:hiyokosabrey:20200815135800p:plain

f:id:hiyokosabrey:20200815201706p:plain

 

真ん中あたりの BlinkFlag というScalarパラメータを掛け算しているのは、ブループリントから枠線の有り無しをコントロールするためです。

 

ピクセルのカラーやアルファを扱う場合、OnとOff をコントロールするのに簡単なのが ゼロとイチ(= 1.0)を掛けることです。

ゼロを掛けるとカラーの場合は黒く、アルファの場合は透明にできます。今回カラーを Lerp(Linear Interpolate)で着色するので、枠線の部分がゼロになることで、見た目に枠線は消失します。

一方 1.0 をかけると何も変化しないので、スイッチの代わりになるということです。

 

0~1.0 の範囲 の 値が流れてくるのを、 Lerpノードが着色します。

Alphaのピンから入ってきた値が、0なら A のカラー。 1.0 なら B のカラー。

0.5 なら AとB の中間のカラー。 という風に、AとB2つのカラーを線形補間した値が出力されます。今どきのお風呂場にはたいていついてると思いますがお湯と水をブレンドできる混合栓みたいなやつのイメージ。Photoshopだとグラデーションマップが近いです。

 

 

このマテリアルを、UMGのキャンバスに戻ってベースのImageパーツにセットします。

f:id:hiyokosabrey:20200815150828p:plain

 

 

この辺でWidgetブループリントを編集します。

 

まず、マテリアルにアクセスするために、ダイナミックインスタンスマテリアルを用意します。

f:id:hiyokosabrey:20200815153350p:plain

 ReturnValueピンから ドラッグして、 Promote to Variable(変数に昇格) を選択します。

f:id:hiyokosabrey:20200815154046g:plain

MID_Base命名

 

次に この ダイナミックインスタンスマテリアルを使ってマテリアルのパラメータをいじる関数を新しく用意します。

f:id:hiyokosabrey:20200815154325p:plain

Select系のノードは、 型ごとにいろいろ用意されています。マテリアルのパラーメータは Scalarを選んだので、Select Float ノードを選択。Bool値(TrueかFalseのどちらか)によって分岐するように値を出し分けることができます。

その結果を Set Scalar Parameter Value ノードに渡します。

 

Selectノードを使わない場合はこうなります↓

f:id:hiyokosabrey:20200815155027p:plain

個人的にSelectノード使う方が、調整と修正がラクに感じるのでよく利用します。

関数名は switchBorderBlink命名

 

次に、マウス用のイベントをオーバーライドします。

f:id:hiyokosabrey:20200815160933p:plain

使うのはこの2つ。

Enter はマウスカーソル(ポインタ)が乗った時、Leave は離れたときに呼び出されるイベントになります。

 

最初から用意(ビルトイン)されている便利なやつですが中身がないので、上書き(オーバーライド)して使います。

 

2通りの取り出し方があります。

 

ひとつめ

Functions の Override ボタン(隠されてる)から探す方法。

f:id:hiyokosabrey:20200815160501g:plain

f:id:hiyokosabrey:20200815160829p:plain

 

 

ふたつめ

右クリックして検索する方法。

f:id:hiyokosabrey:20200815160702p:plain

Add Event > Mouse の中に カテゴライズされています。

 

 

取り出せたら、用意しておいた関数をつなぎます。

f:id:hiyokosabrey:20200815162120p:plain

片方のBool型のピン にチェックを付けたら準備完了です。

 

ひとまずテスト用のレベルで確認してみます。

適当に New Level を作って、Widgetを表示します。

f:id:hiyokosabrey:20200815180637p:plain

Create Widgetノードをとりだして、表示したいWidget をセット。ReturnValue ピンを Add to Viewport ノードにつなげば表示されます。

マウスカーソルを常時表示したいので、まず Player Controller ノード を取り出します。そこから Set Show Mouse Cursor を取り出して、True にします。

コンパイルして問題なければ再生してみます。

 

f:id:hiyokosabrey:20200815200712g:plain

 

表示されている位置が左上になってしまいますが、ちゃんと動作しているのがわかります。

素晴らしいですね。どういう作りになってるのかわかりませんが、それなりに頑張ってくれていると思います。

 

せっかくなので、セレクトメニューとして見せられるとこまでを書いていこうと思うのですが、結構長くなったので今回はこの辺までにします。

 

ではでは

ステキなマウスオーバーライフを!