久しぶりの更新です。まだそんなに秋を堪能してないのに年賀状が売られていたり、おせちの予約受付とか始まってて、年末感が漂ってくるのが切ない・・・梨食べたくなってきた。
さてさて、ネタを探す旅に出てました~てへぺろ、とか言えたらまだ良かったんだけど、実際は毎日帰るのが遅く、仕事で睡魔と戦うのに精一杯で記事を書くエナジーが無かったのです~てへぺろ。
今回は、トグルスイッチを作ります。ゲームのオプション設定とかでよくあるUIで、ON か OFF かの2つの状態を切り替えるスイッチです。チェックボックスも同じ機能を持っているんだけど、アニメーションさせた方がリッチに見えるので、スライドするタイプをマテリアルで表現します。値の管理方法として他の設定項目のことも考えてストラクチャを使ったりしています。また、イベントディスパッチャーやバインドは使いません。最後の方にUI制作のワークフローについて少しだけ触れています。
クリックするたびに切り替わります。
UV移動なので、スライドをやめればいつでもチェックボックスに転用できます。
まずテクスチャを用意します。
Sizeは 256x64 で RGBはこんな感じ。
フリンジが気になる場合は黒い部分をなくしてカラーを入れます。
アルファチャンネルは片側だけを白くして抜きます。
これをマテリアルで制御します。
テクスチャの一部分を切り出すには、TexCoord ノードのタイリングを利用します。
縦方向(V)は全部使うので 1.0 のまま。横方向(U)は以下のように計算します。
切り出したい大きさを、テクスチャのサイズで割ると求められます。
もっと大きなテクスチャでも基本的に計算方法は同じ。
ただし下図のように左上に配置していない場合はオフセットの値が必要になります。
UV値は0~1の値なので、小数を扱うことになります。テクスチャサイズで割り算することになるので、配置場所や面積(パーツのサイズ)は極力偶数にすることをオススメします。奇数だと割り切れないため誤差が生まれやすく一部のピクセルがキレイに描画されなくなったりします。テクスチャ圧縮がかかる場合は、さらに4で割り切れる場所にパーツを配置することをオススメします。後からテクスチャの解像度を変える場合でも偶数にしておけば安心です。
UI表示はカメラに依存せず静止していることがほとんどなので、ピクセルが荒れていると目立ちます。面倒ですがこのあたりは丁寧に扱うに越したことはないです。
切り出すUVの範囲が分かったところで、今度はUVを動かす範囲を計算します。
96をテクスチャサイズで割った値が移動量です。テクスチャサイズが 256pxだと、0.375 になります。
この移動をUMGのアニメーションで動かします。
まずはTextBlockと共にキャンバスに配置します。
トグルスイッチのテクスチャは、画像ではなくマテリアルをセットします。
TextBlockには is Variable のチェックを付けておきます。
次にアニメーションを作成します。
Imageパーツの Pivot の値を 0.0 ~ 0.375 でキーを打ちます。
UMGはここまでです。
エディタウィンドウをGraphに移動してブループリントを編集していきます。
常時走る EventTick を使っていますが、トグルスイッチなどのUIパーツはセッティング画面で使うことが多いので、ポーズ中とかゲームプレイ以外だと、それほど問題にならない、はず。Pivotの値を取り出して、毎フレームマテリアルのパラメータに渡しています。
少しでも処理をシンプルにしたいので、コストの高いGet Dynamic MaterialノードはTickに入れないようにして、Event Construct で最初に変数化しておいたものを利用します。
次に、トグルスイッチを動かすイベント。
UIのインタラクションはユーザーの操作で割り込まれるのが常なので、割り込まれても問題なく動くようにします。 なるべくアニメーションの尺を短く作るのも大事。
最後に、初期値をもらって反映する関数。
トグルスイッチのWidgetは完成です。
次は管理とレイアウトの仕組みを作っていきます。
管理はプログラマでなくても扱える ストラクチャ (構造体)ってやつを使ってみます。
コンテンツブラウザから作成してエディットします。
New Variable ボタンをクリックして名前との型をセットします。
Saveして閉じます。
新しくレイアウト用のWidgetブループリントを用意します。
キャンバスに用意したトグルスイッチのWidgetを並べます。
Graph エディットに移行して、
変数を一つ用意します。
Variable Type を 先ほど作ったストラクチャ にします。
このストラクチャ型の変数をグラフに取り出したら、Breakノード経由で中の値にアクセスできます。今回はブーリアンばっかりですが、いろんな型をひとまとめに扱うこともできます。
値を取り出してキャンバスに並べたトグルスイッチのWidgetに渡します。
これで初期値とラベル名がセットされます。
ここで一旦画面で確認してみます。
このWidgetを保存してレベルブループリントから表示させます。
右の赤いブーリアン型のノードは、 エンジンに最初から用意されているパラメーター設定用のノードで、PlayerController から取り出します。
再生するとこんな感じ。項目の名前とストラクチャの初期値が反映できています。
仕上げにマウスイベント。
クリックしてスイッチを切り替える処理を用意します。
GraphエディタのFunctions 欄 にある Override ボタンを押して、
On Mouse Button Down というのを選択。
特殊な関数を編集できるようになります。
あらかじめ引数と戻り値のピンが設定されていて変更できない作りです。
Warningが出ているので、黙らせるには、Unhandledノードをつないで・・・
コンパイルすると鎮まります。
この関数は、自身のWidgetの上で、マウスのクリックが検出されたら呼び出されます。
白いラインの間にPrint Stringノードを入れて再生してみると分かります。高速でクリックしても反応しないのは、ダブルクリックと区別するためで、ダブルクリックはまた別の関数で処理することができます。
ここに、トグルスイッチのWidgetをクリックしているかどうかの判定と、スイッチの切り替えの処理を並べます。
今回スイッチが4つあって、全体はこんな感じです。
マウスクリック系のイベントはひとまとめにできます。
基本的に、クリックした瞬間、カーソルは誰の上に乗っていたのか?をチェック。
カーソルが乗っていたらどうするか? こうします。
ストラクチャ型の変数から取り出した値をひっくり返して入れ直します。
Not ノードを使うと、ブール値は反転します。
これで完成です。
今回はプログラマとデザイナのブループリント編集がなるべくぶつからないように安全に触れないかな、と考えながら試作しました。
基本的に
デザイナは見た目を担当。受け取った値を反映するとこまで。
プログラマは並んだ各パーツを管理してシステムの値と連携するところ。
今回のアセットをそれぞれで担当を分けるとしたらこんな感じです。
この辺を考慮してイベントディスパッチャーを使わない仕組みです。
UIパーツの方にマウスイベントの検出を入れると、イベントディスパッチャーを Call する処理と、管理側のWidgetBPからバインドが必要で、そこそこ連携しないといけないので、メンテナンスや調整のとき編集がバッティングしやすかったり、デザイナとプログラマとのコミュニケーションコストがそれなりにかかるのが予想されます。
あと、マウスイベント処理をある程度まとめてON・OFFしたり管理できないと、後から条件がややこしくなるので、それを避ける狙いもあります。
UIの見た目は自由にUIパーツの方で受け持って、設定画面を抜けるときに、UIパーツから最終の値を全て回収するという方法も考えられますが、UIパーツの状態を見てリアルタイムに反映させたい場合(サウンドのボリュームとか)は難しいし、値の回収を失敗する可能性を考えるとかえって手間がかかりそうです。
他にも効率的で安全な作り方がありそうですが、いったんこんな感じでいかがでしょうか?
トグルスイッチUIがたまたまいい感じになっただけだと思うので、もうしばらく検証していこうと思います。今までゲームパッドとかのコントローラ操作をメインに考えてきたので、マウスやタッチ操作にも馴染んでいかないとね。マルチプラットフォーム対応はUI泣かせですし。
ではでは
ステキなトグルスイッチライフを!