みつまめ杏仁

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

スピンボックスも作ってみた

 前の記事でインターフェイスを作りましたが、せっかくなのでスピンボックスも作ってみることにしました。

 スライダー同様、ゲームの設定画面とかでよく見かけます。スピンボックスはGUIの1スタイルでMicrosoft Developer Networkのドキュメントに簡単な説明があります → スピンボックス

 

 ▲と▼をクリックするたびに値が1段階変化します。数値を増減させるときに便利なのですが仕組みとしてテキストなどの文字列を入れて使われることもあります。数値のみの場合は明確に区別するために、 Numeric Stepper と呼ぶこともあるようです。

f:id:hiyokosabrey:20180225110925p:plain

 テキストの場合は、序列や段階を意図した言葉選びが重要です。したがって、このUIのメリットは  選択中の状態が分かりやすい ことと、その前後の内容が予測しやすい ことにあります。なので、増減と関連性のない、前後の脈絡のないテキストを選択肢にすると、複数ある選択肢の全体が把握しづらいため一通り送って確認しないと選べないので時間がかかります。

f:id:hiyokosabrey:20180225111352p:plain

ゲームクリア後や実績解除などで、後から選択肢を追加しても画面のレイアウトに影響がないので、開発者にとっては都合のいいUI だったりします。でも途中で内容が増えても気づかないので、Newマークを付けたり、お知らせ表示を入れたりと、開発側としては結局手間がかかることに・・・。用法容量を守り正しく活用したいものです。

 このへんがデメリットとなって使いづらさにつながるので、段階や序列のないテキストを選択肢にする場合はコンボボックスやリストボックスがオススメです。もちろん表示に余裕があれば全部並べて選択させるのがベストです。ゲームのUIってほとんどが選択肢と言っても過言じゃありません。なんらかの選択をユーザーにさせてユーザーの意思を尊重しているかのようにふるまいつつ、都合の悪い選択肢なんて最初から無かったことに・・・うわやめろなにを・・・ゴフゥ・・・中断

 

 

 

 げふげふ(さてさて)、

 今回はスライダーなどと操作方法を統一するために ↑ ↓ キーで項目を選んで、←→キーで内容の切り替えを行うようにします。レイアウトはスライダーと同じように並べるので↓のようになります。

f:id:hiyokosabrey:20180225104855p:plain

新しくWidgetを用意してもいいですが、スライダーWidgetと共通の部分が多いので複製します。

 

 

UMG

f:id:hiyokosabrey:20180225185602p:plain

不要なパーツを消してテキストブロックに置き換えます。

テキストブロックは is Variable にチェックを付けるのを忘れないように。

 

 

Widgetブループリント

必要な変数を用意します。複数の選択肢を扱うので、その個数を保持する integer型の変数と、受け取った選択肢用の Text型の配列変数です。

f:id:hiyokosabrey:20180225190516p:plain

値を受け取る初期設定用の関数 initValue は ↓ のように変更します。

f:id:hiyokosabrey:20180225205556p:plain

 

 値を見た目に反映する関数 updateValue の中身は受け取った配列の中身を取り出すだけなので、 ↓ のようになります。

f:id:hiyokosabrey:20180225204654p:plain

配列のエラーチェックをする場合は IS VALID INDEX ノードが便利です。

f:id:hiyokosabrey:20180225205155p:plain

 

値をアイテムの数だけでループするようにするので、rangeCheck マクロを編集します。

f:id:hiyokosabrey:20180225210010p:plain

 

これでコンパイルしてエラーが出なければ完成。

 

制御用Widget

キャンバスにできたスピンボックスのWidgetを追加します。

f:id:hiyokosabrey:20180225210528p:plain

初期化の関数に追加します。

f:id:hiyokosabrey:20180225210727p:plain

選択肢用のアイテムはここで、Make Arrayノードで渡します。

 

アクティブなWidgetを切り替える関数にも追加します。Switch on Int ノードをAdd pinして割り込ませます。

f:id:hiyokosabrey:20180225210917p:plain

 以上です。メンバーが一つ追加になったのにこの簡単さ。これもインターフェイスのおかげです。

さっそく再生してみましょう。

f:id:hiyokosabrey:20180225212445g:plain

 

これでどんなセッティング画面が来ても対応できそうな気がしてきました。

あと必要そうな処理としては、制御用Widgetに値を返すようにしたり、変更内容をリアルタイムに反映したいときのために イベントディスパッチャーを用意したりすれば完璧な予感。

なんかUIって、表のグラフィックデザインにスポットが当たりがちですが、実際はこういった地味な内部処理が頑張ってたりするんですよね~ とか呟いてみたり。

コンポーネントとして用意されているので、わざわざイチから起こさなくても・・・

という声が聞こえてきそうですが、やっぱり仕組みを知ることで、次の新しいUIが生まれてくる気がするんですよ。

 

今回インターフェイスは使い回したので説明はしてませんが、汎用的なやつが一つあれば結構いろんなUIパーツに適用できるのでおすすめです。

 

というわけで今回はこの辺で。ツッコミとかリクエストとかあればコメントください。

ではでは

ステキなBPインターフェイスライフを!

 

 

 

スライダーUIを並べて操作してみる 《おまけ》

キーリピート処理の説明用にスライダーUIを作ってみたら、なんとなくいい感じになってきたので調子に乗って”初期設定に戻す”機能を追加してブループリント インターフェイスまで扱うとこまできたのですが、もうひとつ「ミュート」の切り替えができるようにして完了にしようと思います。

f:id:hiyokosabrey:20180220225350p:plain

 

前回の記事はこちら

 キーリピート機能をつくってみる - みつまめ杏仁

スライダーUIを並べて操作してみる - みつまめ杏仁 

スライダーUIを並べて操作してみる 《続き》 - みつまめ杏仁

 

アイコン

まずはアイコンの仕込みから

 

テクスチャは簡単な2パターンを用意します。2つの画像に分けてもよかったりしますが、今回はマテリアルで切り替えます。

f:id:hiyokosabrey:20180220225549p:plainSize: 96x96

 

画像をテクスチャとしてインポートしたら、そこから Create Material します。

f:id:hiyokosabrey:20180220230054p:plain

UVスクロールさせるだけのシンプルなマテリアルです。

 

 

Interface

インターフェイスにあとひとつ関数名を追加します。

これは A とか 〇 とかの「決定」ボタンを押したときに呼び出すイベント用です。

f:id:hiyokosabrey:20180221223507p:plain

コンパイルして閉じます。

ここでちょっと注意が必要なのが、インターフェイスを編集して保存すると、そのインターフェイスをセットしているブループリント全てが、再コンパイル対象になってしまうということです。

f:id:hiyokosabrey:20180221224142p:plain

なので、UE4を閉じるときに、普段触っていなかったブループリントが「save する?」

って訊いてきて慌てることがあります。インターフェイスを触るときは、心の準備をしてから触るようにしましょう。エラーが出てるとマズいので、アスタリスクのついたアセットは、面倒ですが一度開いてコンパイルしてから閉じるようにします。

 今回のスライダーのWidgetはそのまま編集をします。

 

 

UMG

まずはスライダーWidgetのキャンバスにアイコンを配置します。

f:id:hiyokosabrey:20180220230852p:plain

Imageパーツを表示したいサイズに調整して、作っておいたマテリアルをセットしてやります。

 

Widgetブループリント

つぎにGraphの編集です。

まず、 ブーリアン型の変数 "isMute" を一つ追加します。

f:id:hiyokosabrey:20180221225227p:plain

 

この変数を使って見た目を切り替える関数 "switchMuteDisp" を用意します。

f:id:hiyokosabrey:20180221225613p:plain

今回 ミュートのON / OFF で変更するのは3パーツ。

f:id:hiyokosabrey:20180221230435p:plain

扱う値が違うので結果的に3種類のSelectノードを使うことになりました。

Selectノードは ブーリアン型の値、 True か False を判定して、あらかじめ決めておいた対応する値を取り出してくれる大変便利なノードです。Brunch ノードで組むこともできますが、白いラインが分岐することを考えると、Selectノードを使う方がスッキリして見えます。

つないでみて気づくのですが、テキストのカラーについては SlateColor と呼ばれる特殊なカラー値なので、LinearColor が使えません。さらに面倒なことにSelectノードの上下にならんだピンの扱いがなぜか逆という・・・

 

さてさて 見た目をチェンジする関数ができたところで、初期設定用の関数に追加した変数を加えます。

f:id:hiyokosabrey:20180221231416p:plain

新しく引数(Inputs)ピンも追加。最後に、できたての見た目チェンジ関数をつなぎます。

 

あとはボタンを押したときに呼ばれるイベント。

f:id:hiyokosabrey:20180221231754p:plain

ブーリアン型の変数 isMute の内容をスイッチングします。スイッチングしたあとは見た目に反映するためにつくった関数をつないでいます。

これでスライダー用のWidgetは完成です。

 

制御用Widget

 スライダーWidgetの 初期設定用の関数に引数を追加したので、ノードが変化しているのが確認できます。

f:id:hiyokosabrey:20180221232348p:plain

 

前回の記事でリセットするイベントを作りましたが、そこにノードを追加します。

f:id:hiyokosabrey:20180221232619p:plain

単純にIndexFocus の値が3 以外なら、onDecide のイベントを呼ぶようにします。

これで、できあがりです。

 

再生して確認してみましょう。

f:id:hiyokosabrey:20180221233313g:plain

うまくいきました。

ただこの操作はユーザーが気づきにくいので、画面の隅にでも操作ガイド的なナビ表示があった方いいでしょうね。マウスクリックを受け付けるなら、おそらく直接スピーカーのアイコンを触るので心配なさそうですが、そうなるとアイコンのデザインも、もう少しボタン感のある見た目にしないといけないですね。

 

ひとまずスライダーUIに関してはここまでにします。

ではでは

ステキなスライダーUIライフを!

スライダーUIを並べて操作してみる 《続き》

前回それなりにサウンド設定ができそうな雰囲気を醸し出すことができました。

そこにリセット用の項目を追加してみます。

f:id:hiyokosabrey:20180218225034p:plain

右端にもオマケでアイコンを追加しています。

 

 前回の記事はこちら。limesode.hatenablog.com

 ↑これをベースにいじっていきます。

 

Blueprint Interface

今回の肝は、タイプの異なるWidgetを並べて操作できるようにする部分になります。そのための準備として、インターフェイスを用意します。

f:id:hiyokosabrey:20180218225616p:plain

とりあえず ”IF_Settings” と名付けました。

f:id:hiyokosabrey:20180218225841p:plain

ダブルクリックして、エディタ右上にある追加ボタンで関数名を4つ追加します。

f:id:hiyokosabrey:20180218230025p:plain

コンパイルして保存したら閉じます。

 

Interfaceを登録

前回作ったスライダーパーツのWidgetを編集します。

エディタをGraphに切り替えて、Class Settings をクリック。

f:id:hiyokosabrey:20180218230621p:plain

Detailsタブに Interface と書かれた項目があるので、そこの Add ボタンを押して、

さきほど用意した IF_Settings をセットします。

f:id:hiyokosabrey:20180218231046p:plain

ここでコンパイルするとエラーがでます。

f:id:hiyokosabrey:20180219094404p:plain

f:id:hiyokosabrey:20180219094801p:plain

同じ名前のカスタムイベントがいるためです。

そこで急いでイベントを入れ替えていきます。

いつものグラフで右クリックしてノードが検索できるやつで探すと出てくるので、つないであったカスタムイベントと入れ替えます。ちょっと見つけにくいけど、Add Event の中にいます。

f:id:hiyokosabrey:20180219093529p:plain

Add Event カテゴリにいるのは、Interfaceで関数に戻り値(Return Value)を設定しなかったのが理由ですが、UE4は戻り値がない関数はイベントとして扱われることがあります。頭に"Event" と付けられていますが、本当の名前には付いていないので名前が被ることになりました。

f:id:hiyokosabrey:20180219095702p:plain

↑この4つのカスタムイベントをいったん削除して付け替えます。 ↓

f:id:hiyokosabrey:20180219095738p:plain

これでコンパイルしてもエラーは出なくなります。

ちなみに、Interfaceで定義した関数はグラフに置けるのは1つだけなので、2つ目を置こうとすると、すでに置いてある場所にフォーカスが移ります。こういった事故を防ぐ仕様がステキですUE4

スライダーの改造をひと段落つけて、新しいWidgetを用意します。

 

テキストだけのWidget

f:id:hiyokosabrey:20180219101029p:plain

キャンバスには、スライダーと同じ大きさとカラーのテキストブロックを一つレイアウトします。

f:id:hiyokosabrey:20180219101225p:plain

is Variable にチェックを付けておきます。

フォーカス切り替えのアニメーションを付けておきます。

f:id:hiyokosabrey:20180219101920g:plain

グラフでスライダー同様にイベントノードにつなぐのですが、先に Interfaceを登録します。

f:id:hiyokosabrey:20180218231046p:plain

コンパイルしてエラーが出ないのを確認したら、Animationの再生ノードをつなぎます。

f:id:hiyokosabrey:20180219102325p:plain

このテキストだけのWidgetも汎用性を高くしたいので、初期設定用の関数を用意しておきます。

f:id:hiyokosabrey:20180219102554p:plain

テキストを受け取って差し替えるだけのシンプルな関数です。

これで完成です。

つぎは前回作った制御用のWidgetを編集します。

 

 

制御用Widget

 作ったテキストだけのWidgetをキャンバスに並べます。

f:id:hiyokosabrey:20180219112246p:plain

 

初期化している関数 を編集します。

まず配列変数周りをなくして、f:id:hiyokosabrey:20180219113427j:plain

テキストだけのWidgetに、ラベルのテキストを渡します。

f:id:hiyokosabrey:20180219112628p:plain

この関数の外にスライダーWidgetの数を保持する変数が待機しているので、そこに今回の項目数を戻り値として渡します。

 配列変数で処理するのをやめたのは、選択項目に別のWidgetが追加になって配列変数が使えなくなったためです。配列変数の型を wd_slider型 から UserWidget型 に変更しても、その配列変数には 1種類の同型のUserWidgetしか格納できませんでした。

2022/7/16 追記>>

あらんさん にご指摘(ブログ下部コメント参照)いただいて調べてみました。

記事を読み返してみて、前回の操作ですでに並べたWd_Slider型のオブジェクトから Promote to variable(変数に昇格) してる箇所があります。それを配列にしてるのでUserWidgetというカテゴリではありますが、VariableTypeが Wd_Slider型の配列になっているので、当然別のWd_TextOnly型が挿さらないわけです。当時の自分にツッコミに行きたい。

というわけで、Promote to variable せずに 新しくVariableTypeが UserWidget型の配列変数をつくるとエラーが出ずに、スルっと追加できます。

← ver4.27のキャプチャ

あらんさん ありがとうございます!

記事を書き直そうかと思ったのですが、差し替える必要のある画像が結構あって、しかも手元にあるUE4系は 4.27しかない状況。4.27でもプロジェクトファイルを開くことができました(この記事執筆時は4.18使用)が、アニメーションの挙動がおかしいのと、微妙にエディタのUIが変更になっているので、改めて UE5 で書き直そうと決意しました。便利なイベントも増えてますし。少し先になりますがなるべく近いうちにUE5版を公開します。

この記事を見つけて試していただいているのに失敗につき合っていただくのは心苦しいのですが、修正範囲が大きいのものあり、動かないわけではないのでこの記事はこのままにします。

<<

f:id:hiyokosabrey:20180219114844p:plain

そこで、前回は wd_slider型のWidgetしかなかったので、配列変数に格納できてそこから数を調べていましたが、今回は直接数を数えて返しています。

f:id:hiyokosabrey:20180219115439p:plain

ちょっと改造の手間を省こうとしてマジックナンバーで対応していますが、

プログラマに渋い顔をされてしまう場合は、親になっているキャンバスやその他パネルで余計なパーツが入らないようにしておけば、下のようなやり方もオススメです。

f:id:hiyokosabrey:20180219115856p:plain

 

ゲームに限らないと思いますが、UIを作るうえでのあるあるのひとつに、あとから項目の数が変更になるのが挙げられるとおもいます。すべてのノードをいつまでも把握しておくのは難しいので、あらかじめヒューマンエラーを起こさないように自動化しておくのはできるだけやっておいた方がいいです。

 

つぎに新しく、UserWidget型の変数をひとつ追加します。

f:id:hiyokosabrey:20180219121226p:plain

前回作った関数 "changeActiveWidget" を大改造します。

f:id:hiyokosabrey:20180219121620j:plain

↑これを ↓このようにします。

f:id:hiyokosabrey:20180219122241p:plain

ActiveWidgetという器(UserWidget型の変数)に、適宜該当するWidgetを入れていきます。

右端のフォーカスするイベントの呼び出しは、Interfaceで定義した関数にしないといけないので、検索して 作ったInterfaceカテゴリ探します。

f:id:hiyokosabrey:20180219122231p:plain

 

これが Interface の持つステキな仕様です。

なかなかピンとこないかもしれないですが、ActiveWidget に何か適当なWidgetが入っている状態だと仮定して、呼び出すための関数ノードをつなぐことができるのです。

普段、関数を呼び出す際には、呼びたい関数を持っていないとつなぐことができませんが、Interface 経由だと、関数を持っていなくても問題にならないのです。

 

あとは、wd_Slider型の ActiveSlider変数でつないでいるところを、入れ替えていきます。

f:id:hiyokosabrey:20180219173330p:plain

f:id:hiyokosabrey:20180219173408p:plain

後はここ↓

f:id:hiyokosabrey:20180219173605p:plain

 入れ替えてコンパイルに問題なければ完成です。

お役御免になった変数を削除します。

f:id:hiyokosabrey:20180219174506p:plain

確認してみましょう。

f:id:hiyokosabrey:20180219175121g:plain

フォーカスがいい感じに切り替われば成功です。

 

仕上げに、リセットのイベントを追加します。

f:id:hiyokosabrey:20180219180239p:plain

フォーカス用Index番号が 3 の時だけ受け付けるようにします。ちょっと雑ですが初期設定の関数をそのままつないで初期化として使ってます。

 

完全に初期化するか、変更前に戻すかは、決めておく必要がありますが、丁寧に作るならリセット用の関数を用意しておくと後から融通を効かせられます。スライダーWidgetの方にも指定した値に変更する関数があった方が便利です。

 

Index番号で判定する方法については、あとから項目の数が変わったり順番が入れ替わると、きちんと値を変えてやる必要があります。対策の方法はいくつかあると思いますが、今回はこれで。

 

レベルブループリント

リセットのイベントを呼ぶために、Inputノードを追加します。

f:id:hiyokosabrey:20180219181601p:plain

 

動かしてみます。

f:id:hiyokosabrey:20180219182011g:plain

いい感じになってきました。

 

いろんな種類のWidgetパーツを、InterfaceUserWidget型の変数 を使って、汎用的に処理する方法を紹介してみました。

 

長くなったので今回はこの辺にします。

ではでは ステキな設定初期化ライフを!

 

 

スライダーUIを並べて操作してみる

 

f:id:hiyokosabrey:20180210235447p:plain

前回 キーリピート処理を試すために作ったUIパーツのスライダーを並べてそれっぽく操作できるようにしてみようというのが今回の記事です。

 

limesode.hatenablog.com

 

まずは スライダーのWIdgetにパーツとアニメーションを追加します。追加するパーツは3つ。

項目名を示す「ラベル」と、「左右のキーを入力してね」と「いまこの項目を触ってるよ」という意味を持たせる一組の  <  > になります。

f:id:hiyokosabrey:20180211000308p:plain

f:id:hiyokosabrey:20180211000331p:plain

ラベル用のテキストブロックは、ブループリントから内容を書き換えるので、

Is Variableにチェックを付けます。あとの< > はブループリントからは直接触らないので画像等にしてもOK。

アニメーションは全部で4種類。

  • FOCUS ・・・ フォーカスされたとき
  • UNFOCUS ・・・ フォーカスが外れたとき
  • RIGHT ・・・ →右キー を入力したとき
  • LEFT ・・・ ←左キー を入力したとき

f:id:hiyokosabrey:20180211084832g:plain

左右のキーを入力したときのアニメーションは、ツマミが動くので、必ず必要ということはないですが、キー入力に対するリアクションは、分かりやすいほどユーザーフレンドリーなUIとなります。また、ユーザーは操作しながら無意識的にこのリアクションを記憶して学習していくので、「< > を見かけたら 左右入力できそう」というUI操作に対する期待感を育てることができます。これもひとつのUXです。デザインの段階で記号化のルール作りはとても重要になります。ここ試験に出ますよ(何のだ?)

 

Widgetブループリント

初期状態の値をもらう関数にラベルをセットする処理を追加します。

f:id:hiyokosabrey:20180211090404p:plain

Text型の引数(Inputsピン)を追加します。最後にスライダーの値更新用の関数をつないでいます。これは前回作ったやつで、処理の順番でおこる不具合対策でここに移動してきました。

というわけで、EventGraphが変わります。

前回↓のようになってたのを・・・

f:id:hiyokosabrey:20180211091022j:plain

こうします。

f:id:hiyokosabrey:20180211091436p:plain

ついでに左右キーを押したときに呼ばれるイベントなので、アニメーションの再生もここで行います。

 

続いてフォーカスとアンフォーカスのアニメーションを再生するイベントを新たに用意します。ただアニメーションをイベントとして再生するだけなのでカスタムイベントにつなぎます。

f:id:hiyokosabrey:20180211094506p:plain

f:id:hiyokosabrey:20180211094555p:plain

この2つの違いは、アニメーションの作り方によるものです。

UMGのアニメーションは、基本的にタイムラインの最後まできちんと再生しようとします。しかも同じ要素でのアニメーションがバッティングするとあとから再生したもので上書きされます。最終的に尺の長いアニメーションが勝ちます。

今回の4つのアニメーションでは、左右キー入力したときの LEFT と RIGHTのみ 0.2秒の尺で作りました。フォーカス切り替えでは、0.0 にキーを打っただけです。4つともカラーアニメーションです。

なので、左右キーを押して ”LEFT” か ”RIGHT” のアニメーションしている途中(0.2秒までの間)でフォーカスを切り替えると、一瞬だけ ”UNFOCUS” が再生されて残りの ”LEFT” か ”RIGHT” のアニメーションが再生されます。結果、”UNFOCUS” のアニメーションは敗北することになります。今のところ解決策は2つが考えられます。

  • 動いているであろうアニメーションを止める
  • アニメーションの要素がバッティングしないようにする

この挙動はバグにも思えますが、利用できる場面があるので、修正されないことを願っています。しくみが分かればきちんと対策できるので。(修正されるといろいろ面倒な処理が必要になる・・・)

このあたりの仕様を踏まえて、必要に応じてStopAnimationノードをつなぎます。

これでスライダーは完成です。次に並べて制御するためのWidgetアセットを新しく用意します。

 

 

 制御用Widget

f:id:hiyokosabrey:20180211105950p:plain

スライダーが一番マッチするサウンド設定の想定です。

キャンバスにバージョンアップしたスライダーWidgetを並べます。

f:id:hiyokosabrey:20180211104936p:plain

 

ブループリント

まずは準備する関数 "initSlders" を用意します。

f:id:hiyokosabrey:20180211105812p:plain

この関数を、EventPreConstructionにつないでコンパイルしてみると、

f:id:hiyokosabrey:20180211110258p:plain

キャンバスの内容が書き換わるのが確認できます。

 

引き続き関数を編集します。変数を2つ用意したいのでスライダーのWidgetノードから、Promote to Variable を2回します。

f:id:hiyokosabrey:20180211111307p:plain

作ったらすぐに消します。Variables のリストには残るので、リネームして一つは配列化します。

f:id:hiyokosabrey:20180211111803p:plain

この配列の方を関数に Set で戻します。そこに Make Array ノードをつないで、キャンバスに並べたWidgetを登録します。

f:id:hiyokosabrey:20180211112047p:plain

 仕上げに int型の戻り値(Outputsピン)に配列の登録した数をつないでこの関数は完成。

 

f:id:hiyokosabrey:20180211112722p:plain

 

 次に、int型の変数を2つ用意します。

配列に登録した3つのスライダーWidgetは、0~2の番号を使って扱うためと、スライダーの個数を保持しておくための変数です。

f:id:hiyokosabrey:20180211134356p:plain

先の関数からの戻り値を受け取るような形でつなぎます。

f:id:hiyokosabrey:20180211144806p:plain

 この続きには操作するスライダーWidgetを切り替える関数 "changeActiveWidget" を作って、

f:id:hiyokosabrey:20180211145409p:plain

くっつけます。

f:id:hiyokosabrey:20180211145615p:plain

 

あとは、前回のキーリピート処理を、レベルブループリントから持ってきます。

変数は移植できないので、ちょっと面倒ですが再び同じようにfloat型、TimerHandle型、Boolean型の変数をそれぞれ1つずつ用意しつつ、

f:id:hiyokosabrey:20180211150917p:plain

カスタムイベント "onKeyPress" を置いてつないでいきます。

f:id:hiyokosabrey:20180211151224p:plain

TimerHandle型は、Set Timer by Event のReturn Valueピンから Promote to Variable してもOK。

DoOnceノードのResetピンにつないでいた部分は、新たにカスタムイベントをつなぎます。

f:id:hiyokosabrey:20180211152032p:plain

 

前回のキー入力部分は、

f:id:hiyokosabrey:20180211152644j:plain

コンパクトにします。

f:id:hiyokosabrey:20180211152747p:plain

そして最後に、上下に並んだスライダーのフォーカスを順次切り替えるイベントを用意します。

f:id:hiyokosabrey:20180211153437p:plain

 上の2つのイベントは、←→左右キー用と、↑↓上下キー用になります。

それぞれの処理は上下と、左右で内容がほぼ同じなので、trueかfalseか、プラスかマイナスか、で分岐するようにして使い回せる形にしました。

このWidgetは一応完成です。

 

 

レベルブループリント

前回のレベルを改造するのであれば、Add to ViewportしていたWidgetが変わるので、前回 Promote to Variable していた変数が使い物にならなくなります。

f:id:hiyokosabrey:20180211154413p:plain

Input ノードで、↑ ↓ ← → のキー入力を検出して、それぞれのイベントを呼び出します。

f:id:hiyokosabrey:20180211154902p:plain

これで完成です。

再生して確認してみます。

f:id:hiyokosabrey:20180211155814g:plain

うまくいきました。

 

今回はこの辺で。それなりの操作ができるとこまでは来たかなと思います。

次回もうちょっとだけ手を入れます。

 

ではでは ステキなスライダーUIライフを!

 

キーリピート機能をつくってみる

 パソコンでキータイプしていると気づくこともあると思いますが、カーソルキーとか同じキーを押しっぱなしにしたときに、1文字目と2文字目の間にちょっとだけ間があります。タン、タタタタタタ・・・・・・ 初めてパソコンを触ったとき、当時子供だった私はモヤモヤした経験を覚えています。UIを意識するようになってようやく腑に落ちたものです。

f:id:hiyokosabrey:20180204094046g:plain

 ゲームでも、音量調節のスライダーなんかにごく当たりまえに実装されているので、身近なやつを見れば・・・って、最近はタッチデバイスなのであまり身近じゃないですね。でもまだPS4とかゲームパッドで遊ぶUIはあるので、今回ブループリントで作ってみました。

 

UMG

キーリピートの挙動を見るために、シンプルなボリュームスライダーを用意します。

キャンバスに3つのパーツを配置します。

f:id:hiyokosabrey:20180203125755p:plain

f:id:hiyokosabrey:20180203125802p:plain

右端がMAX想定なので、ツマミは左端に置きます。

 

ブループリントから触るので3つとも Is Variable にチェックを付けます。

f:id:hiyokosabrey:20180203130034p:plain

触るといっても、Image_Baseだけはサイズを取得するだけです。

 

 

Widgetブループリント

変数を2つ用意します。

f:id:hiyokosabrey:20180203130610p:plain

 

まずは、スライダーの移動範囲を調べて変数に入れます。

f:id:hiyokosabrey:20180203131734p:plain

すでにキャンバスに置いているパーツの長さを調べるので、Event Pre Constructで問題ないです。Get Size で調べたImage_Base(ラインみたいなやつ)の長さを変数 MaxPos に Set します。これは、後からデザインやレイアウトが変更になっても、キャンバスを調整するだけ(Blueprintは無傷)で済むのでオススメです。ひと手間かかってますが、こういったつくりは後で地味に効いてきます。

 

次に関数を2つ用意します。

まず一つ目は指定した値の場所にツマミを移動して、値を表示する関数になります。

f:id:hiyokosabrey:20180203132503p:plain

便利な Lerpノードを使います。AとB2つの値を線形補間したうえで、Alphaの値に応じた途中の値を返してくれるノードで、MaxPos を Bのピンにつなぐことで、どんな長さのスライダーでも対応できるようになります。MaxPosはパーツの長さを調べてるのでデザインが変更されても、勝手にこのBの値が変わっていることになります。

 

 もう一つは初期状態の値をもらう関数です。

f:id:hiyokosabrey:20180203193152p:plain

 ゲームでは設定した値をセーブしているのが普通なので、まずは初期値を受け取れるように関数で用意します。この int型の Value という変数は、念のため Private にチェックをつけておくと安全です。

 

関数が用意できたら、今度はマクロを用意します。

受け取った値を Valueに加算して、0~100の範囲を越えないようにします。

f:id:hiyokosabrey:20180203193901p:plain

越えたらループするようにしています。

減っていくとき、3 → 2 → 1 → 0 → 100・・・

増えていくとき、98 → 99 → 100 → 0・・・

このWidget以外から呼ばれる処理ではないので関数ではなくマクロにしました。

 

仕上げにイベントを用意していきます。

カスタムイベントを2つ置いてマクロと関数をつなぎます。

f:id:hiyokosabrey:20180203194836p:plain

引き算のノードを使わなくても、マイナスの値は、足し算することで引き算と同じ結果になります。汎用性の高いマクロや関数を作るときに応用できます。

 

Widgetは完成です。

次は動かすためにレベルブループリントを編集します。

 

 

レベル

おなじみのやりかたでWidgetをViewportに追加すると、表示された瞬間EventConstructが動くので、先に初期値を渡しておきます。

f:id:hiyokosabrey:20180203195847p:plain

試しに再生してみると、

f:id:hiyokosabrey:20180203200040p:plain

ちゃんと反映されていればOK。

 

 

では、キーリピートの処理を作ります。

必要な変数を用意します。

f:id:hiyokosabrey:20180203225924p:plain

まず、float型の変数は、キーリピートの間隔を保持します。スライダーが増えるか減るかをフラグで管理するためにBoolean型の変数を。一番上の RepeatTimerHNDLという名前の変数はTimerHandle型の変数で、Set TImer Event ノードの戻り値 Return Value ピンからPromote to Variable すると簡単に作れます。

 

カスタムイベントを用意して、下のようにつなぎます。

f:id:hiyokosabrey:20180203230620p:plain

右端のノードは、Widgetに作っておいた関数を呼び出しています。増えても減っても基本的なキーリピート処理は変わらないので、極力使い回せるものは使い回すようにしています。

続きはこのようになっています。

f:id:hiyokosabrey:20180203231752p:plain

 DoOnce ノードでキー入力初っ端だけ通すようにして、2回目以降のリピート間隔を float型の変数にセットしています。

 

まだ続きます。DoOnceはリセットすることができるので、キー入力をやめたらリセットするようにつなぎます。下図はカーソルキーの左右を押した場合です。

f:id:hiyokosabrey:20180203232534p:plain

 

これで完成です。

さっそく 動かしてみましょう。

f:id:hiyokosabrey:20180204084434g:plain

キー入力を開始して、ひと呼吸置く感じで動きます。

 

基本的な仕組みはイベントの自己呼び出し。 f:id:hiyokosabrey:20180204085027p:plain

あとは、DoOnceノードで、RepeatInterval 変数の値を 0.75 から 0.025 に変えます。

これでキー入力後の《 間 》が作れたことになります。この値は 1.0  が1秒です 。フレームレートによりますが 60fpsだと、0.0167 くらいで 1フレーム分です。これを変えてみるといい感じに調整できますよ。

 

この《 間 》が無いとどうなるか、0.75 をセットしてるとこを 0.025 にしてみると答えが分かります。

 

次回は、せっかく作ったスライダーをいくつか並べて操作できるようにしてみようと思います。

 

ではでは

ステキな キーリピート ライフを!

 

UIデザインというお仕事

 気がつけば2018年の12分の1が終わってちょっと焦り気味な今日この頃。残り少ない平成時代を精一杯堪能したいと思うんだけど、何をすればいいか思いつきません。あ、平成30年発行の硬貨を記念に取っておくとか?

それはさておき

2018年最初の記事は雑記で始めてみたいと思います。画像もなんもなく文字だらけなうえにテクニックとか有益な情報はないので悪しからず・・・

とりあえず、UIデザインというお仕事について、私なりに感じている事を書いていきます。

 

自分のこと

 私がゲームのUIを作る仕事に携わってかれこれ四半世紀。UIという言葉がまだなかったころから活動しております。最初は専門職という認識は誰にもどこにもなく、新人や比較的手の空いたメンバーが担当するのがUIだった時代です。

 最近では専門職としてブイブイ言わしてもらえ・・・てはいないですが、一応専門職ということになっております。今までにいろんなUIを作ってきました。「作った」と言っても一人で作れるわけはなく、私はデザイナーなので実際はプログラマとの共同作業です。

 アーケードゲームの開発から始まって家庭用のコンソール機用ゲームと多くのタイトル開発に携わっていろんなことを学びました。 今やアーケードゲームはほとんどが音ゲーや特殊な大型筐体ばかりになってしまいましたが、このアーケードゲームにおいてのUI開発はとても多くの示唆を与えてくれました。その知見が今でも十分に役に立っています。このへんのノウハウはうまくまとめられる自信がないので、今後それとなくこのブログに散りばめていこうと思っています。

 

ゲームのUI

 先に書いた通り私はゲームのUIを専門にしています。ゲームのUIというのはゲームという面白い遊びに、ユーザーを引き込んでコミュニケーションするためのもので、ユーザーとゲームシステムがコミュニケーションするためのインターフェイスのことを指します。「インターフェイス」という語についてはニュアンスをうまく伝えるのが難しい語なのですが、平易な書き方で「やり取りのための仕組み」くらいがゲームUIとしては一番近いニュアンスかなと思っています。そのUIを考えるうえで外せないのが「時間」の扱いです。

 

プレイ時間とUI

 ゲームにはユーザーがプレイし始めてから終了するまで、という「時間」があります。この「時間」はとても重要で大変貴重なものです。もちろんUIにとっても。

 昔のアーケードゲームでは単位時間あたりのインカム数(コイン投入数)がビジネスとして重視されました。プレイ開始から、いかに短時間でプレイヤーを満足させるか、またゲームオーバーになっても、コンティニューしたくなるようにするのが課題でした。そのためにはキャラクター選択や必殺技のデモ演出というようなゲームプレイ(体験)とはあまり関係ない画面はインカム数にダメージを与えるとして、徹底的に切り詰められました。とにかくパッと見て必要な情報をシンプルに分かりやすく配置し、短時間に何をどう操作して決定させるかが、UIに課せられた任務でした。100円で満足するには、UIではなくキャラを操作したいですよね。

 残念ながら、どれだけ素晴らしいデザインのキャラセレ画面やアイテム管理画面なんかを操作しても、それを思い出として語るプレイヤーはなかなかいませんし、ゲームをプレイしたという思い出にUIは含まれないものと了解するしかありません。空気のような存在感のUIでユーザーがゲームプレイに満足できれば、そのゲームのUIは素晴らしいUIである。と本気で思っています。

 

 ちなみに「GAME OVER」という表示は、アーケードゲームならではの演出です。その理由のひとつとして、ゲームセンターという公共の場所にカギがあります。乱暴な書き方をすると、ゲームオーバーの文字が表示されたら、さっさと席を譲れ。ということです。あなたの100円でのプレイ可能時間はここで終わったんだよ、と。

 

 アーケードゲームに限らず、このプレイ時間(アプリの場合は操作時間)というものは、プレイする側にとっても、プレイさせる側にとっても大変重要な要素であることは疑いようもなくて、ここの意識が弱いと、必要な操作なのに分かりにくくてモタついたり、目的の状態まで進めていくのに演出の尺が長いといった、テンポの悪いUIを許してしまう恐れがでてきます。

 

 そういえば、UIを操作している時間が貴重であることに気づいた偉い人が、何年か前にUX(ユーザー体験)という言葉をハイライトして、ユーザーがよりシンプルでスマートに目的を達成するためのUI設計を考えましょう。と提案したことで「UI/UX」というパワーワードが俄かに脚光を浴びることになったのだと思っているのですが、ゲームセンターでの教訓を学術論文にでもして発表できていれば、ゲームの世界からUI/UXが語られていたかもしれません。

 

 さてさて、この「時間」というものを意識してUIを考えると、ただグラフィックがそこにあるだけではUIは成り立たないことに気づきます。グラフィックの存在と合わせてそのグラフィックの行方をどうするか、がUIを作るうえで重要になってきます。

 

振る舞いと意味

 ゲームシステムからUIを通じて選べと言われてユーザーは選んで決定し、ゲームシステムはその情報を元に次のフローに進みます。このゲームシステムとユーザーとの対話がスムーズに進むようにするのがUIの役割です。ユーザーに不安を抱かせないように気つけながらいろんな手段でユーザーに選択を促しその結果をフィードバックします。この時の対話で何らかの動き=「振る舞い」を使うとやり取りの効率がぐっと上がります。

 分かりやすいのは決定した項目をハイライトしたり、カーソルを点滅させたりとかです。UIとして画面に表示されるグラフィックはすべて、必ず何らかの振る舞いを持ちます。「意味を持つ」と言い変えてもいいでしょう。パッと出て、パッと消えるのも振る舞いです。ユーザーの目に触れてから消えるまでの存在すべてが振る舞いであり、ユーザーへ伝わる「意味」となるのです。

 動きには当然時間が伴います。時間といっても非常に短い時間ですが、この小さな動き=振る舞い(新しい言葉でマイクロインタラクションとでも言えばいいのでしょうか)がつながってゲーム全体のテンポに影響を与えます。

 

UIデザインというお仕事

 だいぶ回りくどい感じになってしまいましたが、ようやく表題に近づいてきました。

 UIはメタで抽象的な世界から答えを見つけてきて表現することが多いので、「正解」というものはいくらでもあります。その中から「Bestな正解」を、ワインのソムリエのごとく見つけてきて開発チームに提案します。それが受け入れられれば幸せですが、受け入れられなければまた数多の正解の中から次の正解を探す旅に出ることになります。ステージに草を生やしたい。と言われれば草に見えるものを置けばいいのですが、UIにはゲームシステムからの情報伝達という大事な役割があるので、そういった具象的なものをそのまま置くだけでは意味が伝わりにくいです。ユーザーにUIとしての存在意義を疑われたらおしまいです。齟齬やミスリードの無いように慎重に設計する必要があります。どういったタイミングで、どういった情報をユーザーに伝えて判断を促すのがいいのか、どういった振る舞いをすればゲームシステムとの対話になるのか、どこまでが装飾であるのか、こういった部分をひたすら考えます。

 振る舞いも含めてデザインするためには、何らかの動き(アニメーションなど)を検証するところから始めて、Bestな正解を探します。光り方や点滅速度、移動の方向や速度など、ある程度はAfterEffectやDCCツール、Flashなどタイムラインの仕組みを持つツールがあれば検証できますが、それはあくまでもシミュレーションにすぎません。やはり対話には「コール&レスポンス」の心地よさが大事です。そこを実際に動かすことができるのは、ソフトウェアとしてのプログラムということになります。UIパーツの振る舞いを考え、それをプログラマに伝えて組み込んでもらいます。

 動いたら触ってみて感触を確かめます。手触りの悪いところは調整しつつ、仕様要件を満たしているか、また将来追加される仕様に対応できるかなども確認します。こうして限りなく製品に近いモックアッププログラマと一緒に作って、レビューしてもらうのが理想形です。

 プログラマに面倒な動きをお願いしたり、レビューの意見に振り回されたりしつつも二人三脚で作っていくのがUIデザインというお仕事の基本になります。このあたりの具体的なテクニックやノウハウなども、機会があれば書いてみようと思います。

 

 アンリアルエンジン推し

 UIをデザインしてモックアップを作る環境が理想なのですが、そこにはプログラマが必要です。絵にかいた餅を食べられるようにしてくれるのがプログラムなのです。デザインして食べてみておいしいかどうか。これを繰り返してUIデザイナーという職能(=スキル)を高めることができると思います。一人でUI開発するのは難しいです。振る舞いをどうにかしてくれるプログラマの存在が欠かせないからです。

 ただ画面のイメージ画を作って見せても、いまいち具体的な操作感や振る舞いをイメージしてもらうのは、頭の中でシミュレーションできる人じゃないと難しいです。

 

 なんとかして動くものを見てもらいたい、そのための研究としてOff-JTしたいなーと思っていたところにアンリアルエンジンと出会って、すっかり魅了されてしまいました。シンプルで柔軟なアセット管理ができるのも分かりやすいし、作ってみたいUIをすぐに試せる気軽なUMGとWidgetブループリントはもう手離せません。プログラマにお願いする前にプロトタイプを作って検証できるのがとにかく素晴らしいのです。アイディアを形にしやすいのがアンリアルエンジンのビジュアルスクリプトだと思っています。ブループリントの魅力は触ってみて実際に作ってみないと実感しにくいので、言葉で説明するのはやめておきます。UI的にどんなことができるかは、当ブログの記事で確認してもらえればと思います。まだまだこれからも頑張って紹介していくので、興味が沸いてきたならぜひ触ってみてください。

 

  結構な文字量になってきたのでこの辺で筆を置こうと思います。(←古風な言い回しですがこの表現好きです)

 UIデザインって実はとても大変なんです。グラフィックデザインを学んだからといって一朝一夕でできるようなお仕事では決してないことを言いたいがために、時間と振る舞いに重点を置いてみたのですが、いかがだったでしょうか。長くこの仕事をしていると、一家言どころか、百家言くらい言いたいことがあったりするのですが、こちらもまた別の機会にします。感想やらツッコミなどありましたらコメントとかでよろしくお願いします。

 

ではでは みなさん

ステキなUIデザイナーライフを!

今年も当ブログをよろしくお願いいたします

 

 

よいお年を

ついに大晦日ですねぇ。このブログもペースは落ちてますが、なんとかマル2年続けることができました。これも多くの方に読んでいただいているのが、励みになっています。ありがたや、ありがたや。

2年も経つとエンジンのバージョンも随分アップデートされてきました。そろそろ過去記事をメンテした方がいいかもしれないなぁ と思うんだけど、それよりも新しい記事書かないと、という想いがその思いを押しやってしまって結局のところネタを思いつかなくて筆が重くなるという、よくわからない悪循環のような状態になっている今日この頃です。大晦日ということで、小ネタを載せて2017年を締めようかと思います。

 

マテリアルで絵を揺らします。パラメータで制御できるので、いい感じにアニメーションさせればそれなりに使い途はある・・・と思いたい。

f:id:hiyokosabrey:20171231203820p:plain

制御用パラメータは3つ。

  • Period ・・・波の数
  • Amplitude ・・・ 左右の振れ幅  0でまっすぐ
  • OffsetY ・・・ タテ方向のスクロール用

 

真ん中のシマシマのテクスチャは、こんなやつ。

f:id:hiyokosabrey:20171231204249p:plain

8 x 512px のグレイスケールテクスチャです。

あとは、タイムラインからパラメータ用の数値を取り出して、横流しをしてやればOK。

アニメーションを再生して、

f:id:hiyokosabrey:20171231205515p:plain

 

EventTick で取り出して横流しです。

f:id:hiyokosabrey:20171231205555p:plain

この例では、最初からある キャンバスパネル の Pivot は演出に使わないので、Pivot にアニメーションを仕込んでいます。Pivotは XとYの2つしか値を持っていないので、 OffsetYとして 0.5を掛け算して 再利用しています。

 

アニメーションを再生するカスタムイベントが呼ばれると動きます。

f:id:hiyokosabrey:20171231210729p:plain

f:id:hiyokosabrey:20171231210739p:plain

f:id:hiyokosabrey:20171231210747p:plain

うにょ~んと動くので、なかなか楽しいです。

動画でお見せできないのが残念ですが、興味を持っていただけたならぜひ試してみてください。

どこで使うんだろうというネタですが、これで今年は締めさせていただこうと思います。

 

ではでは

来年もよろしくお願いいたします。 

ステキなUE4ライフを!