雑記::20201011
Twitterだと書ききれない、ブログだとボリュームが少なめの内容で書き散らしました。内容は適当なのはご容赦いただけますと幸いです。
ここ最近風邪っぽくて
いつもと同じものを食べてるのに、あれ?こんな味だったっけ? と、ちょっとした味覚の変化にびくびくしながら過ごしています。 新型コロナじゃない普通の風邪?従来の風邪?だったとしても、大変不安な気持ちになるので早く免疫をゲットしたいですね。昔だったら誰かの祟りとか怨霊の仕業で必死に鎮めようとしただろうな。陰陽師という職業が今も続いていたら、この疫病の原因はなんと答えるのだろうか。などと資料を読みながらいろいろ妄想しています。
最近『原神』始めました
おすすめポイントはどこかと聞かれれば、景色が綺麗なのとキャラがかわいいのと、あとはそれくらいだったりするのですが、ブログの記事を書かなきゃと思いつつも原神始めてしまうくらいのおすすめ感です。で、コントローラ操作で遊んでいるのですが、ボタン操作のナビ表示をしっかり見てから操作しないと意図しない結果になるので、かなり学習コストが高い印象です。慣れの問題でしょうけど 日系プレステ操作の 〇決定 ×キャンセル のボタン配置がそのまま XInput系ゲームパッドで、B決定 Aキャンセルなのが辛い。
ゲーム中のナビ表示に従ってきた経験で決定やキャンセルをボタンの場所だけでなく文脈(でいいのかな?)で体得しているのを改めて実感。
Nintendo Switch版 はまだリリースされてないけど XInputと逆なので、たぶん今の仕様でも問題なさそう。
先日公開されたPS5 の仕様も考えると、マルチプラットフォームや縦マルチ対応でこの辺りを開発される現場の方々の苦労を想像するといろいろこみ上がるものを感じます。
『原神』を遊んでいて気になるのは、UIの操作ナビ表示に頼りすぎててボタンの役割をうまく設計できていないところ。細かい話になるのでこの辺はまた別の機会に譲るとして、ゲームパッドにやさしい作りになってない印象を受けてしまうんだけど、今後どこかのタイミングで対応が入るのかな。
UMGでヒット&ブローを作ってみた
作ったといってもNintendo Switchのアソビ大全に収録されているゲームをUE4で再現しただけです。マスターマインドっていうかなり古いボードゲームがあったよな~となんとなく記憶の隅にあった程度で、いざ遊んでみるとこれが結構面白い。ルールはシンプル。隠された4つの色と順番を当てるだけ。CPUの思考とかもいらないし、ほぼほぼUIだけじゃん、て甘く考えてUMGで作ってみたわけです。できたのはこんなやつ。
あとでブログの記事か、UIプロトタイプの教材にできないかな~と考えながら作ったので、あえて仮素材感バリバリです。
Widgetの切り分けとカーソル制御、判定部分で悩みましたが、なんとか遊べるとこまでできました。記事読みたい人いるかな?
このゲームは基本的にゲームパッド操作に向いていません。それをちゃんと遊べるようにしているのがさすがNintendo。そこに感心したのが今回作ろう思ったきっかけでもあります。とはいえ問題点がまったくないわけではなく、Joy-Conで遊ぶ時アナログスティック操作で動かしたくないほうのカーソルを動かしてしまい慌ててしまうこと。
UIの仕様はだいたい以下のとおり
- 移動方向の違うカーソルが同時に2つ存在する
- 中央縦に並んだ4つの穴すべてに、画面下の横に並んだ6つの選択肢から選んでセットする
- 4つ埋まると判定可能になり、OKボタンが出現
- 判定結果をテキストと4本のピンで知らせる
- 左端から順番に判定していく
- タップ操作も対応(今回の検証では無視)
で、作っていて気づいたことがあって、
一つセットすると、カーソルが次の穴に移動するかしないか。というもの。
ただ遊ぶだけだったらきっと気づけなかったと思う。
本家では自動で次の穴にカーソルが移動します。
文字入力の際にキャレットが移動するのと同じ感覚。
ところが自分で一から作っているものですから、ちょっとずつ作りながらテストします。まずは自動でカーソルが移動しない状態ができたので、遊んでみたところものすごく遊びにくいことに気づいたのです。
おそらく原因は、カーソルが2つあって独立して操作しなければいけないところ。
気持ちは次の穴に向かっているのに、カーソルは動かない。次の色も選ばなくちゃいけない。これがそこそこ気ぜわしくなって、操作の順番がおかしくなることがしょっちゅう起こりました。バグ取りしつつ何度か試すけど頭が慣れてくれない。で、ようやくインクリメント(でよかったかな?)処理を入れたら、なんということでしょう!とりあえず4つすべて埋まるまでは色を選ぶことに集中できて快適になりました。
こういうのは、デザイナーがあれこれ考えるより先に、プログラマーが先に解決してくれてたりするので、今回気づけたのが妙に嬉しかった。
そういえばこれに近い感じのゲームがあったのを思い出しました。
ナンプレです。数独とも呼ばれますね。あれは9x9マスの移動に方向キーを全部使うので、このヒット&ブローのような操作で混乱することはない。ナンプレも結構好きで自作したこともあるのですが、よく見かけるのがミニポップアップ方式。ヒット&ブローだとこんなイメージかな。
これだと上下移動と左右移動が同時にはできなくなるので、操作が混乱することはなくせます。ではなぜこれを採用しなかったか?
おそらく、画面下に選択リストを配置しているせい。ただしこの存在価値は非常に高いと思われるので、費用対効果を考えて残したのだと思います。推測ですが、
- 画面がにぎやか(穴がいっぱいあるだけなので、このゲームの場合重要)
- 選択肢(=出題の範囲)の数がわかる
- アナログゲームの趣き=コンポーネント(駒とか)の手触りをイメージ
- 操作しているより考えている時間の方が長い
あたりかなと。特に2番目の選択肢の幅が明確なのは、思考するときに重要な要素。
これは試してないですが、実際に作りながらだと検証できますね。
このカーソル送り機能は実際に試してみて気づくことができました。この感覚を共有したいのですが、文字で伝えるのは難しいです。ということで、ゲームUIデザイナーの紳士淑女の皆さんにはぜひともアンリアルエンジンでプロトタイピングを嗜んでいただきたいなと強く心に思った次第です。
製品になったUIは、開発者の試行した結果が結晶化している状態なので、直接本人から聞き出さない限り、どうしてそうなったのか?という理由は推測するしかなく、真相は藪の中。
一から自分のUIを作れるようになるには、デザイン力と情報設計力は大事だけど、何より説得力がカギ。ロジックは問題ないようにしてやるからデザインは任せろ、と言えたらどんなにカッコいいか!とはいえ、仕様変更なんて世の常。諸行無常の響きがこだましています。どんなデコレーションにも対応できるロジックが自分の中にないと、デザイン変更に耐えるのはかなりしんどいものです。そこで体験として持っていると、UI提案する際に戦力アップ間違いなしです。
そこに遊び心が加わるともう一気にUIクオリティが上がります。
というわけでちょっと大げさに盛りましたが、実際作ってみたらこうだったという気づきは、できれば日常的に経験値上げしていきたいなと思う今日この頃。
オーバーロイドが楽しい
人に話すと大抵ラノベのオーバーロードと勘違いされるのですが、アナログのカードゲームです。
OVERЯOID と書いてオーバーロイドと読みます。2人で対戦します。13枚のカードしか使いません。カードの役割を覚えるまでは、リストを作ってみながらやるといいです。
ルールは超シンプル。一枚づつ出し合って数字の大きいほうが勝つというもの。そこにカードごとの効果が加わることで、より複雑な心理戦が繰り広げられます。自分の持っていないカードを相手が持っていることにはなるので、読みやすいと思いきやそう簡単には読めないのがこのオーバーロイドの真骨頂。6枚づつ配って1枚残った伏せ札。これがまた悩ましい。とにかく確実に勝てるカードが一枚もないのです。たった13枚しかないのにこの凝縮された感じ。勝敗がついた後の感想戦もまた大変楽しく盛り上がります。数字と役割とのバランスが絶妙で、シンプルなのに同じ展開にならない奥深さ。
とてもよくできているので、気になった方は是非一度遊んでみてほしいです。
オンラインでした手に入らないみたいなので、すぐに試せる簡単な方法をご紹介。
トランプの A~K までの13枚を用意。
あとは、公式サイト にルールも載っています。カード効果 も載っているので、これを見ながらでも十分プレイできます。
イラストもカードのデザインも素敵で、楽しみ方がわかれば本物のカードを手に入れて遊ぶと雰囲気が出て盛り上がること間違いなしです。
バリウムがおいしく感じた
火曜に年一回のドック検診受けてきまして、珍しく空腹だったせいか、バリウムを飲んだ時に、ああ、これが食べ物だったら・・・と悲しくなるくらい美味しくいただきました。空腹感すげぇ。バリウムはお腹に溜めておくと危険と脅されるので、出し切るためにゴハンに申し訳ないと思いつつドカ食いする。今年は検査会場の近場で使える食事券をくれた。有難かったけど、このお金はどこから出ているのだろう。
UMGの最適化の話
この記事を書いているときにTwitterに流れてきたこれ
Unreal Fest Online 2020における講演「Unreal モーション グラフィックス (UMG) UI デザイナーで AAA UI の最適化と作成」に日本語字幕を追加いたしました #UE4https://t.co/EE0el5UYyA
— アンリアルエンジン (@UnrealEngineJP) 2020年10月10日
興味深いことを紹介してくれていてうれしい。特にSMeshWidgetのパーティクルは面白いですね。Mapのアイコンのような大量の表示パーツがある場合などに効果的って話で、ぜひ将来のVerで使えるようにしてほしい。
負荷軽減のテクニックで、Tickをなるべく使わないとか、Visibility設定の Hiddenよりは Collapse の方がいいよ とかはそれなりによく聞くやつだけど、ツリーの平坦化、階層を深くしすぎないというのと、HitTest しなくていいものは 無効にするというのは、確かに!って思った。
Widgetの生成はコスト高いので、プールして管理する方法はそれなりに考えたりします。たとえばフェードインアウト用のWidgetや汎用のダイアログなんかは、常駐させる方が何かと都合がいいし。表示頻度やタイミングなんかを相談して決める感じ。
今回はこの辺で
みなさま お体大事になさってください
ではでは
セリフの表示の速さを調節するあれのあれ《後編》
秋草の花粉かホコリかわかりませんが鼻水とくしゃみが止まりません。せっせとティッシュの山を築き上げつつ、とりあえず座っている頭の高さを越えるまでに書き上げようと思います。
前回の続きです
テキストをタイプライターのように一文字ずつ表示するとこまで作りました。
セッティング用のUIを作っていきます。
調整に必要なパーツはなんでしょうか?
タイピング速度が設定できればいいのですがここで、どういった方法で調整したいのか考えます。
- 数値入力は面倒
- 直感的にやりたい
- 細かすぎるのはやだけど、微調整したいかも
とかなんとか検討の結果、スライダーが適任かと思われます。
ただしマウスの移動量は物理的なものなので、スライダーの変化の幅を大きくすると、微調整しにくくなります。下図はちょっと極端な例ですが同じ距離を移動したときの変化量を電卓で調べてフレームアニメーションにしたものです。
ちょっと極端な値も見てみたいかもしれない。
そこで補助機能としてスライダーの最大値を変更できるようにします。
ここは微調整しなくてもよさそうなのでスピンボックスで。
気になったらスライダーに替えればいい。
あとはテスト用に仕込んでおいた 待機時間を調整するやつ。
これもスピンボックスで。
というわけで、
さっそく新しくWidgetを作成。
このWidgetはあくまでも検証用です。好みの値を試しながら、議論して最終的な値を確定させるための役割なので、確定したら不要になります。
ですので肩の力を抜いて適当に置きたいものを置いてちゃちゃっと作ります。
キャンバスにパーツを並べていきます。
下のようなレイアウトにしてみました。
ヒエラルキーはこのようになりました。CanvasPanel以下の階層は平たんです。
左側の3つのTextBlockはラベルです。右側の4つはブループリントと連動します。
まず、一番右上の TextBlock は スライダーの値を流し込むので、Is Variable を有効にします。
適当に初期値を決めておきます。
なんとなく 0.1 くらいでいいかと。
次に スライダーの設定を確認。
Appearanceの項目を編集します。
Value は 初期状態の値としてなんとなく決めた 0.1 にしておきます。
Min Value(最小値)と Max Value(最大値)もなんとなく入れておきます。
最小値の 0.0167 は 60fps想定で、 1/60=0.01666666... から。
0 にすると停止します。
次に最大値のスピンボックス
こちらは Content の項目
これもなんとなくな値を入れておきます。
ただし Value(初期値) は スライダーの Max Value と同じにしておきます。
次は 待機時間のスピンボックス
これもなんとなくな値を入れておきます。
Value(初期値)は、テキスト表示用Widgetで変数の初期値として設定した値です。
UIパーツの設定ができたので、イベントを用意します。
On Value Change (*) という便利なイベントがあらかじめ用意されています。
まず スピンボックスを選択した状態で
Detailタブを下の方までスクロールさせます。
Events の項目にある On Value Changed の右の+ボタンをクリック。
すると 自動でグラフに切り替わります。
このイベントにつないでいく前に必要な変数を2つ用意します。
プロジェクト内に作ったWidgetは、変数の型として扱うことができます。
WB_TextType は前回の記事で作ったWidgetです。
この変数の Expose on Spawn を有効にします。
変数が揃ったので、イベントにつないでいきます。
右端は、WB_TextType型の変数を通して中に用意したカスタムイベントを呼び出して値を渡しています。
Float型の値をText型のピンにつなげようとすると、自動でキャスト(型変換)ノードを挟み込んでくれます。
このキャストノード、初期設定だと小数第3位で四捨五入するので、少し設定を変えます。▼をクリックしてたたまれたノードを展開します。
一番下の Maximum Fractional Digits を 4 に変更。
これで、0.0167 が 0.017 にならなくなります。
スピンボックスも キャンバス の方 でパーツを選択しておいてから、On Value Changed の横にある 緑色の+ボタンをクリックします。
グラフはこんな感じです。
最大値のスピンボックスは、値が変更されると即座にこのイベントが呼ばれ、その瞬間の値がピンから出てきます。それをすかさずスライダーの最大値にセットしています。
一方の待機時間のスピンボックスは、テキスト表示用Widgetに値を渡しています。
ちなみに緑の+ボタンは、イベントノードが作られると、View ボタンに変わります。
クリックするとそのノードの場所へジャンプします。
こういった細かい気遣いがUE4の好き。
これでこのWidgetは完成です。
このセッティング用Widgetを前回表示テスト用に作ったレベルブループリントに追加します。
WB_TextType型の変数の Expose on Spawn を有効にしているので、初期値として直前のCreate Widget ノードからの ReturnValue(戻り値)を渡すことができます。
これで、セッティングWidgetから テキスト表示Widgetのイベントを呼び出せるようになるので、パーツは全てそろったことにはなります。
ただマウスカーソルが常時表示でないし、テキストは一つだけど永遠に次のテキストに切り替わらないので、とりあえず待機時間の仕組みを確認するための処理を追加します。
テキスト表示が完了した際に通知を受け取る必要があります。そこで イベントディスパッチャー と バインド(紐づけ) しておきます。
つながっていた GoTypingノードと入れ替えます。
ここにカスタムイベントを追加
白いラインが途切れて GoTypingが呼ばれなくなったので、バインドノードの後ろに追加します。
カスタムイベントを呼び出すには、My Blueprint > Graphs > EventGraph の中にあるのをドラッグ&ドロップします。
続けて、マウスカーソルの常時表示のために、PlayerController が持っているフラグを有効にします。
GetPlayerController ノードを取り出してそこから、show で検索すると見つけやすいです。
これでようやくすべてが機能するはずです。
確認してみます。
スピードの値を小さくすると残像が長く伸びるのがわかります。
0.0167 の値で、 StandAloneで実行して動画キャプチャして数えてみると、おおむね 57文字表示されてました。小数点を扱うので丸め誤差もあるかもしれない。どちらにしろ速くしても目で追いかけるのは無理なので安定するほどほどのところで最小値を決めたい。この辺の値を探る目的で作ったので、作った意味はそれなりに得られたと思います。ちなみに 0.1 ではきっちり10文字表示されました。
また、テスト表示ということでほぼ負荷を気にしない作りなので、0.0167以下の値 0.001 (1ms?) だと更に高速に表示されます。
こんな感じのサンプルを作ることで、プロジェクトの偉い人と一緒に値を決めたり、感覚を共有できます。
今回のテキスト表示は、タイピング速度の検証を優先したので、テキスト表示自体はすごくシンプルで、任意改行や文字修飾に対応できていません。あとは欲しい機能に合わせてアレンジしたり工夫すると面白くなると思います。
おまけ
テキストをあと何種類か追加してローテーションさせてみます。
レベルブループリントにまず変数を3つ用意
バインドノードと イベント呼び出しノードの間でマクロと変数の初期化を行います
マクロの内容は以下
MakeArrayノードを使って好きなだけつなぎます。
つなぐのはこのノード。
順番入れ替えや差し替えがラクです。
ピンを増やすときは Add pin + をクリック。いらないピンは、そのピンの上で右クリックして Remove array element pin を選択すると削除できます。
お気に入りのキャラを表示して、いい感じのセリフをしゃべらせると楽しさがアップします!たぶん!
マクロができたら、仕上げにテキストの順番を進める部分を作ります。
マクロ内で、テキストの最大数を調べてあるので、その数で剰余を計算すると最大数までの範囲でループするような結果になります。剰余はほんとに便利なので当ブログでもよく登場します。ブループリントだと 「%」 マテリアルだと 「fmod」 です。
テキストを表示するたびにカウンタである TextIndex 変数を1加算しているので、次回表示する際は新しいテキストになっていて、最大数までカウントするとまたゼロに戻って・・・というのを繰り返します。ブランチノードで判定するよりシンプルです。
動画を撮ろうかと思ったのですが、ものすごく地味な動画になりそうなので、保留にします。そんなにややこしい構成ではないと思うので、興味が沸いた方はぜひ試してみてください。リクエストがあれば・・・考えます。
今回は以上です。
メッセージ表示のUIって、いざ作ってみるといろいろ工夫できて面白いですね。いい感じにできると、あとはシナリオとキャラがあればゲームができそうな気さえします。
なんとかティッシュに埋もれる前に書ききることができました。
鼻がひりひりします。
分かりにくいとことか、あれれ~おっかしいぞー?みたいなとこあれば容赦なくツッコミお願いします。
最後にちょっと前にCEDECの講演で話題になったキーワードについて、自分の思っていることをしゃべらせてみたのを貼っておきます。
ではでは
ステキな テキスト表示ライフを!
セリフの表示の速さを調節するあれのあれ《前編》
PS5の予約が始まりましたね。初代機が発売されて四半世紀が経過。あの四角形のポリゴンを傾けたときのテクスチャの歪みに抗おうとしてた頃が懐かしいです。
ふと当時が思い出されたので、Photoshopでドットを描いて再現してみました。
今はもう、少ないパレットカラーでレンガを作ることもないのですね。
さて、しみじみしたところで、今回は、ツール的なものをネタに書いていきます。
ジャンル的にADVでいいのかな。大昔のPCゲームでアドベンチャーゲームといえば、アドベンチャーな世界で、アドベンチャーなストーリー展開でまさにアドベンチャーしていたとかすかに記憶していますが、ヴィジュアルノベルとかノベルゲームの方がアドベンチャーゲームの系譜に近い気がする。実は世間ではすでにアドベンチャーゲームってなんだよ、意味わかんねーよw、と言われているような気がしないでもない。そんなADVゲーム的なゲームで必ずといっていいほど見かけるのが セリフ枠 とか メッセージ枠 とか呼ばれるやつ。「枠」は「ウィンドウ」に置き換えて メッセージウィンドウ という呼び方もします。メインのゲームがADVじゃなくても途中で挿入される会話劇なんかで今でもよく見かけます。
美しい立ち絵を眺めながら小芝居と会話劇を堪能するためのセリフ表示システムとしての役割で扱われている印象が強いゲームのテキスト表示とその周辺のUI。
位置固定式フキダシ表示システム とでも言いましょうか。Googleの画像検索でスクショを眺めてみると、タイトルを印象付けるユニークなデザインになっている様子がうかがえます。UIデザイナーとしては頑張り甲斐のあるところですね。
ゲームプレイ全体で目にする時間も長いので、デザインも凝ったものが多く、またいろんな機能がついていたりします。中でも当たり前にあるのが表示速度調整。コンフィグとかの設定をみると、ゆっくり~はやい などでだいたい3,4段階というところでしょうか。
調整機能を入れつつも、タイピング中にボタンを押すと最後まで一気に表示する仕組みも今では普通に実装されていて、さらに連打やうっかり押しちゃってさっきのセリフなんだっけ?となったときのために会話ログが閲覧できるようになっていたりと、UI的にもかなりの進化を遂げていると思います。
だいぶ前置きが長くなりましたが、今回作ったのはこの調整機能を実装するために段階を確定するための検証用の仕組みになります。ややこしい。
つまり開発用です。プロトタイプとしてサクッと作ってみました。
ではさっそくベースになるセリフ表示のパーツから
まずはタイプに使う文字。
キータイプっぽい見せ方をしたかったのでカーソルの残像が残るようなアニメーションから作っていきます。
新しいWidgetをアセットブラウザ上で作成。
ダブルクリックして開いたら、
キャンバスに 新しく CanvasPanel を置きます。
キャンバスの方にドロップしてもいいのですが、ポジションをリセットするのが面倒なので、ポジションを調整しない場合はヒエラルキーパネルの方にドロップすると楽ちんです。
置いたら、 Size To Content を有効にします。
続けて
CanvasPanelの中に TextBlock をひとつ追加して、
書体と文字のサイズを決めたら、これも Size To Content を有効にしておきます。
これで、文字の大きさによってキャンバスパネルが可変してくれるようになります。
さらに Is Variable も有効にします。
これを有効にすると、ブループリントから触れるようになります。
TextBlockの上に Image を被せます。
Imageのアンカーは右下のやつ。
Offsetはすべて ゼロ
これでCanvasPanelの大きさに追随してくれます。
結果的に 文字の大きさに合わせて Image のサイズも変化することになります。
3つのパーツを追加しました。ヒエラルキーは以下のようになります。
次はアニメーション。
Image が透明になって消えるだけです。 ループはしません。
キャンバスの編集はこれで終わり。
ブループリントを編集していきます。
文字を受け取るために、Text型の変数を一つ追加。
記号なども含めて「文字」のことを英語でキャラクターというので、Character から4文字いただきました。
Expose on Spawn と Instance Editable を有効にしておきます。
このWidgetが生成される際に、同時に値を受け取ることができます。
Event Construct にノードをつないでいきます。
Delay ノードは必要というわけではないので無くても大丈夫。
雰囲気的に調整する目的で置いてますが、0 を入れるとスルーします。
この待ち時間が必要と決まれば、アニメーションの方を編集して尺を伸ばし、このDelayノードは削除しましょう。
このWidgetアセットを アセットブラウザ上で複製します。
名前は 待機用ということで wb_wait にします。
中身をいじっていきます。
キャンバスの TextBlock を 見えなくします。
アニメーションも削除します。
次にブループリント。
Event Construct につなっがているノードをごっそり消します。
Text型の変数 Char も不要なので削除します。
スッキリしたところで、カスタムイベントを追加。
点滅する処理を作ります。
ループアニメーションを使うと、ノードはシンプルになりますが、ただ点滅させるだけなのでタイマーを使います。
Set Timer by Event ノードの 赤いラインは、ちょっと変わっていて イベントとつなぐために使います。つないだイベントを呼び出すことができるのです。なので、この場合時間が来たら再び先頭のイベントが呼ばれるのでまた自身に処理がやってきます。いわゆるループです。
FlipFlopノードは、処理が来るたびに、出口がAとB交互に切り替わるので結果的に点滅することになります。
Set Timer by Event ノードが呼ばれなくなったら実質ループ終了ということになりますが上の場合特に何も条件を付けていないので、放っておくと無限にループしてしまいます。さすがに無限ループはご法度なので、しっかり止める仕組みを入れます。それがタイマーハンドラー。
タイマーハンドラーは、Set Timer by Event ノードの Return Value ピンから作ります。
ドラッグしてPromote to Variable を選択すると、変数が作られます。
これはタイマーイベントをハンドリング(制御)するために用意する変数で、タイマーがセットされて定刻になるまでに中止するときや、一時的にポーズするときなどに使います。
今回は、このWidgetが消されるときに、タイマーを止める処理で使います。
最初から用意されているイベント Event Destruct ノード を取り出して、タイマーを止める Clear and Invalidate Timer by Handle ノードをつなぎます。
仕上げに、点滅用のカスタムイベントを Event Construct につないで2つ目のWidget完成。
そして3つ目のWidget
ゲーム画面ぽいやつ
キャンバスにそれっぽいのをレイアウトします。
必要なパーツは WrapBox
これは子供の要素を左上詰めで並べていって、決めておいた幅を越えるのがわかると、改行してくれるパネルです。ガンガン Add Childs しても右に はみ出すことはありません。
ここにテキストを流し込むので、それ以外は自由なデザインで。
キャンバスがいい感じになったら、グラフの編集。
変数を4タイプ6つ新しく用意します。
文字をタイプしていくための カスタムイベントを用意します。
ブループリントから動的にオブジェクトを生成する Construct Object from Class ノードに、
文字表示用に作っておいた wb_char をセットすると、
Expose on Spawn した変数が入力ピンとして自動的に出てきます。
Outerピンにはそのままだとコンパイルエラーになるので、 Self ノードをつないでおきます。
この生成された結果が、WrapBoxの子供として追加されるようにしています。
このイベントが呼び出されるたびに、一文字追加というイベントになります。
その文字を一文字を抽出しているのが Midノード。
Text型はあまりいじることができないのでString型の状態で扱います。
Stringを操作するノードがたくさんある中に Mid と GetSubstring が用意されています。
ヒストリアさんがきれいにまとめてくださってます。
JavaScriptだと charAt() に相当するやつを探してみたけど用意されていないっぽいですね。
で、続き
ここでも Set Timer by Event が出てきました。タイマーハンドラーもここで作っています。
最後までテキストを表示し終わったら、次のテキストへの待機表現が必要です。
右端は イベントディスパッチャー を Call しています。
作り方は、My Blueprintタブの一番下
特別なことはしないので、名前だけわかりやすくしておけばいいと思います。
取り出すときにポップアップが出るので Call を選択。
全体のノードはこんな状態。
次に
テキストを受け取る関数を用意します。
タイピング中にこの関数が呼ばれることは無いと思いたいけど、isTypingフラグを用意したので、念のためにブランチノードで判定。このフラグは今回はこれくらいの役割しかないけど、タイピングを途中でスキップする時に使えると思います。
WrapBoxはリセットしないと、どんどん追加されてしまうので、このタイミングでクリアにします。
今回見やすくするためにマクロにしました。
あとちょっと
最後の仕上げに、カスタムイベントを2つ用意します。
外から書き込まれるイベントです。受け取った値を変数に入れるだけです。
これでメインのテキスト表示システムは完成。
長くなったので、残りは《後編》に持ち越すことにします。
が一応テストしておきましょう。
適当なレベルを作ってWidgetを表示してみます。
Fileメニューから New Level を選択。
レベルブループリントの編集は ↓ ここから
実行の前に wb_TextType で用意した変数の初期値を確認。
ClassDefaultをクリックすると編集がラクチンです。
これで準備完了。
さっそく表示テストしましょう。
ファイルサイズが大きすぎて全画面の動画は上げられなかったので、部分だけGIFにしました。
↑ 森見登美彦著『有頂天家族』(幻冬舎文庫) 淀川教授のセリフから引用
今回はこの辺で
次回《後編》で調整用のUIを実装します お楽しみに~
キャラをキャプチャして立ち絵にしたい
今回は、キャラ絵としていい感じに抜くためのテクニックを紹介。
UE4には高解像度でキャプチャするツールがあります。
キャプチャツールの使い方は公式に上がってるので、詳しい機能についてはそちらをご覧いただければと思います。
タイトルは明かせないのですが過去にとても大量のキャプチャで四苦八苦したときのノウハウになります。
撮影用のレベルを作っておくと便利です。
ライトとカメラ、キャラの3つのポジションを決めておけば、キャラ修正やポーズ変更の時、以前に撮った絵との差し替えがスムーズにできます。
まず抜きたいキャラに対して、Custom Depth への書き込みを設定します。
Render CustomDepth Pass を有効にします。
これを設定すると背景を抜くことができます。
F8キーを押してビューポート左上の▼ボタンから、
高解像度スクリーンショット を選択します
チェックボックスがいくつか並んでいます。
一番下にある マスクとしてカスタム深度を使用 を有効にします。
CustomDepthを有効にした状態でキャプチャすると、PNG形式で書き出されて、Photoshopだと緑の部分は抜きになっていますが・・・
実際には緑はしっかり残っていて、透明で見えないだけなのです。Photoshopでは確認の術がありません。
しっかりエッジに侵食しているのが分かります。
なので、キャプチャする際はCustomDepthを有効にしたやつと無効にしたやつの2枚をキャプチャするのをオススメします。
キャプチャする前に、撮影用のカメラを切り替えないといけませんね。
F8でゲームを一時的に抜け出すと専用のカメラに切り替わりるので、キャプチャ用のカメラに乗り換えます。
念のためカメラのトランスフォームをロックしておくことをオススメします。
再びワールドアウトライナから同じ撮影用のカメラを選んで右クリックして Transform > Lock Actor Movement を選択するとロックできます。
この流れでもう一度撮影用のカメラで右クリック、今度は飛行機のアイコンがついた Pilot~ を選択します。
これでカメラが切り替わります。
ようやく撮影できます。
倍率はディスプレイに描画しているサイズに対しての倍率になるので要注意です。
エディタのビューポートが小さいと小さくキャプチャされます。少しでも大きくしたい場合は F11 キーを押して全画面(Immersive Mode)にしてからにしましょう。
倍率は、大きくするとポスターなんかでも耐えられる大きさを撮れますが、使っているグラボの限界を超えるとエンジンがクラッシュします。慎重にほどほどに。
ちなみに RTX2060 を使っていますが、 1920x1080 を 7.0倍にしてキャプチャするとクラッシュしました。
6倍だと 11520x6480px にもなります。250~300dpi で 横幅がおよそ1mほど。
A0のポスターサイズです。
比較するために 1.0~6.0 の倍率で撮った画像から、一部分を同じ大きさで切り抜いてみました。
6倍のやつは、PNG圧縮された状態で 22MBありました。
で、いざキャプチャ!
マスクとしてカスタム深度を使用 を するVer と しないVer の2枚を撮ります。
この2枚をPhotoshopで開きます。
Photoshopの隠れた? テクニックを使います。
同じサイズのドキュメント間でのみ使えるテクニックです。
抜きになっている方を、抜きになってない方に移動して、
Ctrl + Alt + Shift キーを押しながらドロップ。
これでぴったり同じ位置に重なります。
次にアルファチャンネルを作ります。
2つほど方法があるのですが、簡単な方で書きます。
後から重ねた方の、抜きになっているレイヤーのサムネイルを、 Ctrl キーを押しながらクリックするとピクセルの存在する部分を選択範囲として取ることができます。
その状態のまま、
チャンネルパレットにあるボタンから選択範囲を保存します。
新しくアルファチャンネルができます。
アルファチャンネルが無事できれば、抜きのレイヤーは不要なので削除できます。
あとは、テクスチャに合うようにトリミングして完成です。
サイズ調整する場合は、スマートオブジェクトにすると非破壊で差し替えがラクです。
フォーマットがどうしてもPNGじゃなきゃ死んじゃう!でなければ Targa形式(*.tga)での保存をオススメします。
エンジンにインポートして確認。
緑のフチドリは出ていません。
完成!
ここからはおまけ
このキャプチャにはブルームが入っていたりします。
ブルームの有り無しで絵の華やかさが変わったりするので、基本有効にしておきたいところですが、輪郭の部分はアルファで容赦なくバッサリ切られます。
また、Sci-Fi系や魔術系のキャラなどの発光パーツでは必須です。ビームサーベルとかw
ポストプロセスで描かれているので、カスタム深度とは無縁なのです。
そこでマテリアルを使って再現します。
使うテクスチャは別に用意したりせずそのまま使えますが、少し地味なので、Photoshopで盛ることにします。
アルファを作ってトリミングしたテクスチャにブラシでブルームを足してやります。
ちょっとはエモくなったかな?
まず、このブルーム用のマテリアルを用意します。
汎用のマスターマテリアルとして使うので、テクスチャはTextureSampleParameter2Dです。
マテリアルドメイン(右端の茶色いノード)の設定を Additive(加算)に設定します。
あとはこのマテリアルからインスタンスを量産して、そこにキャラごとのテクスチャをセットして運用します。
マテリアルインスタンスは、アセットブラウザのマテリアルアセットアイコンの上で右クリックすると一番上に出てきます。
アセットブラウザに並んだ状態 (名前は適当)
これをUMGでレイアウトします。
まずImageを一枚レイアウトしたら、マテリアルインスタンスの方をセット。
加算なので、グリッドが透けているのがわかります。
ここにもう一枚同じ大きさのImageを手前に重ねておきます。同じポジションです。
複製(Ctrl+W)すると速いです。
こちらにはキャラ絵のテクスチャをそのまま貼ります。
ブルームマシマシ再現完了です。
適当なレベルで表示してみると、
おお、まぶしい!
キャプチャした時に床が入り込んでたので、下の方が白くなってます。このようなブルーム再現で使う場合は、カメラに入らないようにするか、Photoshopでレタッチして完全な黒にしておくといいです。
昔はHIGHモデルを表示するのはコストがかかったもんです。テクスチャ容量に頂点数、モーションに、ライティング、さらに口パク。画面に大きく出るので細かいとこまで作りこまないと安っぽさが目立ってしまいます。制作コストもかかるし、ロード時間の問題もあって、キャプチャして 2D で表示するのは、それなりに旨味がありました。
最近はサクサクロードできて、モーションもいい感じに再生できる時代なので、今回のテクニックは、パブ系の素材か、ゲームだとインベントリ内に表示するアイテムのグラフィックぐらいでしょうか。
ちょっと枯れた感じがしなくもないですが、誰かのお役に立てれば幸いです。
ではでは
今回はこの辺で
ステキはキャプチャライフを!
ぷちコン振り返り
暑さが少し和らいだ気がしなくもないですが、確実に日暮れが早くなっているので地軸の傾きを実感しています。
残念ながら紹介されるまでには至らなかったですが、先日応募した第14回ぷちコンの簡単な振り返りを書きます。
今回のぷちコンの参加は、あまり集中して時間が取れなかったのもあって見送る気でいたのですが、締め切り前告知をツイッターで見た直後にネタが降ってきて心残りもあったので、急いで作ってみた次第です。
夏休みといえば宿題。遊びたい誘惑を振り切ったと思いきや、すぐに切れる集中力。遅々として進まなかった苦い記憶がふっと思い出されたので、この集中力をタイマーにしつつ、逼迫した状況に抗うゲーム。というイメージが浮かびました。
RollingTemplate をベースにしています。
- 見えてる範囲を狭くしたい
- おやつ食べて回復したい
- いろんな誘惑がステージに置いてあって近づくと集中力が激減する
- 集中力がなくなってくると徐々に視界の周りがざわざわする
- グレイマンの動きで状況を説明。飽きさせない動きにしたい
というのを最初考えていましたが、さすがに期限が目前すぎて猶予がなかったのでできる範囲で作りました。
後からこちらの記事を読んで、 練度の違いを実感。
トルク制御を知っていればもっといろいろできたかもしれない予感。
勉強勉強。
BPのつながりについて
参考になるかわかりませんが、作例としてBPの構成をご紹介。
GameStateクラスが全体の進行管理を担って、適宜HUDへの指示をします。
UI周りはすべて HUDクラスに一任しています。
このスタイルが結構気に入ってます。GameState と HUD には、ちゃんと調べたことはないですが、ほとんどのブループリントからアクセスすることができるのが理由です。
自分の作ったやつにキャスト(型変換)しないといけないですが、頻繁にアクセスするのがわかっている場合は、変数化しておくとノードのつながりがスッキリしてグラフが見やすくなります。
GameState は状況の変化を感知したり、タイマーを動かしたりしています。画面表示に更新が必要になると、この GameStateから HUD に用意したカスタムイベント経由で値を渡します。
HUD は 画面のWidgetたちをすべて管理するようにしているので、受け取った値を表示しているWidget に流します。
UI表示までが少し遠い印象ですが、値の流れるルートが把握しやすく、改造が楽だったり、デバッグしやすかったりします。
複数人で作る場合、HUD と Gametate とで分担がしやすいのではと思うのですがいかがでしょうね。
視界について
集中すると周りが見えなくなるのをイメージして、スコープを除くような感じにしたかったので、最初 UMGのブラーエフェクトパネルを使ってみたのですが、ボケ具合がイマイチだったので、メインカメラの DOF を有効にしました。
UMGだとガウシアンな感じがしてちょっとエモくない。
DOFだと絞り羽の形がエモい。
中央のはっきり見えている部分はレンダーターゲットテクスチャ。
キャプチャ用のカメラを、メインカメラの子供にアタッチ。
画面中央だけでいいので、レンダーターゲット用テクスチャのサイズは大きいとメモリ消費が厳しいので1024x1024 。 1080p だと上下に若干届かないくらい。
あとはこのテクスチャをマテリアルで抜いてUMGで配置するだけ。
集中度合いをみて視界の大きさを変動させたりしたかったけど時間がなくて断念。
集中力がなくなってくると徐々に画面にノイズが重なってくるようにした。
レーダーについて
今回実装に一番悩んだ部分。
表示位置の計算は、座標から角度を得る atan2 を使って、UMG上で三角関数使えば戻せるなと考えていたけど、問題なのはどうやって散らばったオブジェクトを把握するか。
レーダーに映るのは一番小さなBallのみで、大きいBallにぶつかって分解されることでスポーンする仕組み。結構数が増減するので配列に動的に積むのはやめたほうがよさそう。ということで、マップにセットした大きいBallから総スポーン数を割り出して、最初から全部を配列で管理することにした。
マップには 大きいBallを宿題7科目分置いたので、7×32 = 224個
一番小さいBallがスポーンする際に固有のID番号を渡すようにして、プレイヤーが触れたらそのID番号をバックしてDestroyするようにした。表示のOn/Off はBoolean型の配列を同様に管理して、スポーンしたら true 、いなくなったら false にする。
これをアクターとGameState 間でやり取り。
GameStateは常にスポーンされてるアクターの座標とプレイヤーの座標から角度を計算してFloat型配列を更新し続けていて、レーダー表示用のWidgetは、その配列から角度を取り出して表示に使うという流れ。
XY座標と原点を結ぶ直線の角度(=傾き)を計算してくれます。
今度は角度と半径、2つの値から、サインコサインを使って原点からのXY座標を計算します。あとは画面表示の際のズレを補正してやるだけ。ここがそこそこ面倒だったりする。
このレーダーの実装でそれなりに遊べるようになったので良かった。
これがなかったら、ストレスが溜まるだけだったので正直応募を諦めていたと思う。
グレイマンについて
ちゃんとモーションつけて再生したかったけど、さすがに にわかでサクッとできる気がしなかったので、ポーズだけつけてアニメーションアセットを作った。
指を一本一本曲げていくのは意外に楽しくて、手の形でもかなり表情というか感情を出せるということに改めて気づけたので、グレイマンのように顔がノッペラだとなおさらいい勉強になりそう。グレーマンを使ったグラビアコンテストなんかも面白いかもしれない。完全に日本だけのコミュニティしかウケないだろうけど。
撮影用のレベルを作ってそこで適当にライトを置いて、解像度キャプチャーツールで撮ってテクスチャにしました。
キャプチャして立ち絵にするあたりは、ちょっとしたテクニックがあるので、また改めて記事にしようと思います。
カリキュラムのカウントアップについて
散らばった一番小さいBallには、0~223 の固有のID番号を振るようにしています。
最初マップに配置する一番大きいBallには 0~6の番号を与えておきます。
それぞれがActorでプレイヤーと接触すると2番目の大きさのBallを自身の周りにスポーンさせて自身はDestroyします。
この2番目の大きさのBallをスポーンさせるとき、自身の番号に 32を掛けてさらに +0、+8、+16、+24 を加算して持たせます。
さらにここから一番小さいBallが 8個 スポーンします。ここはシンプルに自身の番号に0~7の数字を加算しています。ForLoopノードのIndex値を加算するだけです。
これで最終的にゲットするBall に固有のID番号振る仕組みができました。
今度はあちこちに散らばってしまったBallから、どのカリキュラムのBallかを逆算していきます。ゲットした番号を 32 で割ります。Integer型の割り算なので少数以下は切り捨てられます。
その値を配列を使ってカウントして、画面の棒グラフに反映しています。
カリキュラムが一つMAXになると、集中力の低下がしばらくストップするようにしました。宿題が一つ片付いたので勢いがつくイメージ。
タイトルロゴについて
まぁゲームが地味なので、タイトルロゴくらいは賑やかにしようと思って頑張ってみました。テーマ回収をここでしてる感がありますが・・・ぶっちゃけあります。
無理やりラノベ感を出そうとしてちょっと痛々しさも感じつつもう少し手を入れたいと思う自分がいたり。というわけでちょこっとコントラスト上げてみた。
濃ゆくはなったけど、まだまだ追いついてない感じです。
諦めたこと
ブラックホール的な誘惑ゾーンを作りたかった。エンジンで床に凹みを作ろうとスタティックメッシュのブーリアンを試してみたのですが、UVのスケールを調整しきれなかったのと、エッジを滑らかにできなかったので、DCCツール使わないと無理そうと分かったので断念。
とにかくエフェクト。分解するときにパーティクルを飛ばしたかったけど、普段からエフェクトツール触ってないのもあって、手が止まってしまった。なんとか回復アイテムを取った時だけ出すことができた。前回もエフェクトを思い通りにできなかったのに解決できていないのがまったく反省していない。
最後に
結果発表の中で、「締め切り駆動」という言葉が何度か出てきて、今回まさにそれだと思いました。実際の仕事ではそんな状況を作ると叱られてしまうのですが、今回は短い時間でのチャレンジがとても楽しかったです。ぱっと思いついてさっと作ってみる。そんな炭酸の泡のようなアイデアでも、無謀にも一人で試してみようと思えるアンリアルエンジンのハードルの低さは素晴らしいです。そこそこ長く触ってきていますが、エンジンを触ること自体のハードルは決して高くないと思います。エンジンの開発およびサポートされている皆様には感謝しかありません。正直使いこなすのにそれほど時間がかからないと思うUE4ですが、厳しい現実と向き合わされることになります。
それは、自分自身が持つゲーム作りの感性。例えば、素晴らしいキッチンと調理道具がそろっている状況で、おかずのレパートリーが少ないのはなぜか?というのを考えると分かりやすいかもしれません。油を熱したことがなければ揚げ物やアヒージョなんて作ろうと思わないと思う。でも食べて感動したら試したくなるじゃないですか。しかも同じ環境、同じ道具で作らてしまうなんて、と。
と言いつつ自分はなかなか試せていない現実が重かったです。
そう考えると ぷちコンはとてもいい環境です。ぜひこのまま続いていってほしいですが、作品数が増えると審査にかかる時間と労力は相当なものになると思います。お忙しい中本当にありがとうございます。
次回はエモいの作りたいな・・・
ではでは
ステキな ぷちコン ライフを!
斜めになったメニューボタンでも~後略 《いろいろ試す編》
当ブログでは、CEDECで取りあげられることのない、ゲームUI開発で明日使えるかもしれない小ネタを主に扱っています。ためになるかもしれないし、ならないかもしれません。
さてさて
最近、斜めになったボタンでもマウスカーソル(ポインタ)を検出できるかという検証をしました。
今回はその続きというかおまけのようなものになります。
斜めに傾けるのはUMGの RanderTransform で設定できるのですが、その名の通り描画時にゴリっと変形させてしまう手法で、アンチエイリアスなど気の利いた処理が入りません。
エッジが見事にジャギってしまいます。解像度が高くて画素が小さいディスプレイだと気づかれにくいですが、背景の色との組み合わせで目立つこともあります。
このジャギー対策はやはりテクスチャで解決するのが簡単なので、専用のテクスチャを用意します。サイズは 128x128px
RGBAの各チャンネルを分解すると ↓ このようになってます。
※上の画像をDLするとPhotoshopで再構成できます
UIパーツは状況に合わせてカラーを変えたりすることが多いので、いちいちバリエーションを用意してたらテクスチャ容量が厳しくなります。そこでできるだけテクスチャに頼らない方法として、このスタイルが最近のお気に入り。
Photoshopで作る場合、各レイヤーのブレンドは、覆い焼き(リニア)-加算 です。
この方法だとマテリアルが必須になってしまうのですが、調整の手軽さや複数人での開発を考えるとコスパはかなり高いと思います。
カラーをどういじるか次第でノードのつなぎ方は変わってはきますが、この程度のノードでボタンの色味は自由自在です。マテリアルパラメータコレクションを使えば、リードUIデザイナーがカラーを一括して調整するのが簡単にできます。複数人で開発する場合それぞれが自身のPhotoshopでカラーを共有管理しながら作業するのは、うっかりや伝達漏れなど、そう簡単ではないと思います。これだけネットワーク環境とゲームエンジンが進化しているのであれば、プラットフォームやデバイスの多様化と能率のことも考えると、ある程度ゲームエンジンという一つの大きな作業台の上でデザインを仕上げていこうという考えはそう悪くはないと思うのです。
このへんちょっと意味わかんないと言われてしまうとアレなんで、オーソドックスだけどマテリアル使う方法も紹介。
テクスチャはこのように2種類の絵を一枚にまとめておきます。256x128px
※上の画像をDLすると PNG形式なのでそのままインポートできるはず
この方法だと2種類の絵を Lerpノード使ってスイッチすることができます。
マテリアルを使わずに作る場合 あらかじめ2枚の絵を重ねておいて、上に乗っている方のアルファ値を操作する方法が普通。2枚のオブジェクトが必要になるうえに、アルファで半透明の部分が重なって濃くなるので、フェードアニメーションするたびに輪郭が太ったり痩せたりします。試しにデバッグ用のノードをつないでみましょう。
Lerpノードの Alpha につなぐだけで簡単に確認できます。
背景が黒なので溶けてわかりにくいですがテクスチャのアルファ値は変化していないので輪郭の濃さは変わりません。
ちなみに2枚重ねだとこうなります。
まぁこの程度は気にしないか気付かない人がほとんどだと思いますが・・・
この青とオレンジの2パターンは、通常時とハイライト時に切り替える予定です。
となると、先に紹介した方はUMGのアニメーションでカラーを変化させる方法になります。
ただ、この方法はプロジェクト全体で一括調整というのが難しいのと、CPU負荷も少し気になるので、マテリアルパラメータコレクションに変更します。ということで、最初のマテリアルをもう少しいじっていきます。
まずコンテンツブラウザからマテリアルパラメータコレクションを新たに作成。
編集して4色を用意します。カラーは RGBAと4種類の値がひとつにまとまっているので Vector Parameter を選択します。
これを組み込んでいきます。
マテリアルエディタのグラフで右クリックして CollectionParameter ノードを取り出し、
Detailsタブからさっき作ったマテリアルパラメータコレクションをセット。
これらをLerpで補間してやります。
Lerp(線形補間:Linearinterporate)も処理コスト的にそんなに安くないので、ここはカーブアトラスを使った方法も試してみます。
カーブアトラスとカラーカーブについてはこちらの記事がおすすめ。
で用意したのはこの2本。
これを組み込んでみます。
すっきりコンパクトになりました。
ちゃんと負荷検証をしたわけではないので勝手な推測になるのですが、カーブアトラスを置くためのメモリ容量を犠牲にしてでも、カラーのアニメーション計算の方が処理付加的に高くつくからというのが、その存在理由なのかなと思ってみたりしてます。事前に計算した変化の度合いをテクスチャに焼き付けておくことで計算を省力化してるのかなと。
ただ正方形テクスチャで非圧縮だと思われるので、テクスチャ容量に余裕がないプロジェクトは厳しい手法になりそうです。なのでカーブアトラスを使うと決めたらめいっぱいカラーカーブを仕込んでいきたいですね。
マテリアルはこの辺にしておいて、ボタンWidgetを作っていきます。
UMGでキャンバスに Image を配置。先ほど用意したマテリアルをセットします。
TextBlockも配置してキャンバスはこんな感じで構成。
前回の記事で書いたように左右のスキマ調整用に SizeBox で Wrapしています。
ここに、マウスカーソル(ポインタ)のヒット検出用に Image を重ねます。
半透明で色を付けた状態で作業して、位置と形が決まったら完全に半透明にします。
Hierarchy はこのようになりました。
次にアニメーションを追加します。
アニメーションはボタンのマテリアルに用意していたパラメータに対して付けます。
トラックが追加出来たら今度はそのトラックにマテリアルのトラックを追加します。
ここでようやくプロパティが追加できるようになります。
あとはキーを打つだけ。
このアニメーションは 通常時(デフォルト状態)用で状態をリセットするために使うので特に尺は必要ないので、キーはひとつだけ打ちます。最初に5秒分の尺が設定されているので、終了点を詰めます。※ゼロにはできないようです。
次に ハイライト用のアニメーションを作ります。
こちらは尺を0.25秒でキーを打ちます。キーの値は 0.0 ~ 1.0
とりあえずアニメーションはこの2つ。
次はマウスカーソル(ポインタ)が重なった時に呼び出されるイベントがあって、その検出をスムーズに行うために、Visibility(ビジビリティ) の設定を変更します。
Visibility は Behavior(ビヘイビア)の中にある設定項目です。
最後に追加した ヒット検出用の Image 以外を Non-Hit-Testable(Self Only) にします。
あとはTextBlock の isVariable を有効にしておきます。
キャンバスの準備ができました。
ブループリントを編集します。
マウス用のイベントをオーバーライドします。
前々回の記事でも説明しましたがもう一度載せます。
~Enter と ~Leave の2つを取り出します。
ここに 作ったアニメーションをつなぐのですが、アニメーション管理用の変数を追加します。
VariableType は Widget Animation型
これを使ってマクロを作ります。
グラフ内である程度つないだら、選択して 右クリック > Collapse To Macro を選択。
これをダブルクリックして編集します。空のマクロから作成しても問題ありません。
実行ピンがつながってないので、ノードからドラッグして追加します
Inputs と Outputs のノードにドロップすると、自動で実行ピンが追加されます。
このマクロを経由してイベントとアニメーション処理をつなぎます。
まずは On Mouse Enter から
次は On Mouse Leave
残るはテキストの変更する処理。
Integer型の変数を一つ追加して、外部から値を受け取れるようにします。
Expose on Spawan と Instance Editable を有効にします。
この変数を、Event Pre Construct で使います。
マウスオーバーしか機能はないですが、これでボタンのWidgetは完成です。
確認用にボタンを配置する 親のWidgetを新しく用意します。
キャンバスに HorizontalBox を一つ置いたら、作ったボタンWidgetを並べたいだけ置いていきます。
UE4はプロジェクト内に作った Widget は、User Created というカテゴリにエントリーされるようになります。CanvasPanel や TextBlock、Image をPaletteタブから取り出すようにこのリストから取り出せます。フォルダが違えば同じ名前のWidgetが作れるので、適当に作ってるとこうなります。マネしないようにしましょうw
なんとか探して、Hierarchey の HorizontalBox にドロップします。
↑5つばかり置いてみました。
ここ面白いのが、先ほど ボタンWidgetで作った Integer型の変数にアクセスできるところ。
追加したボタンWidgetを選択した状態で、 エディタ右にある Detailsタブを見てみると、ここから値を設定できるようになってます。
値を書き換えるだけで、コンパイルしなくてもどんどん変わっていきます。
これはなかなか面白い仕様です。
これで完成です。
レベルブループリントからViewportに追加して確認します。
動画はツイッターに上げました。
矩形じゃないヒット判定のアレンジで作ってみた#UE4 #ue4UMG #UIdev pic.twitter.com/hWUAZPtLV0
— みつまめ杏仁 (@MMAn_nin) 2020年9月6日
前回の記事では、ブループリントから動的にボタンUIを生成しましたけど、今回は手で並べてみました。変動せずに決め打ちで作れるならこの方法でも大丈夫だと思います。
決定処理とか作り始めると、また長くなるので今回はこの辺にしておきます。
UIの表現は、見た目は同じでも作り方としていろんなアプローチがあって、正解というかベストを探る感じで作ることが多いです。UE4はいろんな方法を試せるのでとても楽しいです。
UI制作において作り方マニュアルが欲しいという話をチラホラ耳にしますが、欲しかったらまずはUIを作ってみるのが一番の近道だと思います。この辺いい感じのアドバイスができないのが個人的に口惜しく感じているところではありますが、おっとまた長くなるところでした。
ではでは
ステキなUI制作ライフを!
斜めになったメニューボタンを並べてみる《テスト編》
前回のつづき
検証もできて満足したので、そのまま選択するとこまで作ってしまおうと勢いづいて作り込んでみました。
あらかたできた時点でTwitterに上げたのですが、このブログで記事にしていく途中で、いろいろ足りてないところを発見。不具合を直したりあれこれ触っているうちに、気が付けば それなりに機能の入ったメニューUI ができていました。
ゲームの UI は状況と仕様に合わせて柔軟に作っていくものなので、あくまでも一例として参考程度に流し見してくださると助かります。
いろいろメンテナンスとか、融通の利く作りを考えてたら、このようなアセット構成になりました。
今回の記事では、とりあえず並べてボタンのアイコンとラベルをテスト表示するところまでを書こうと思います。
ひととおり動くやつをGoogleドライブにアップしましたので、興味のある方はこちらをDLして触ってみてください。
さてさて
最終的に扱うWidgetは前回作ったのと、今回作る2つ。
(命名が適当・・・)
ひとまず並べるための新しいWidgetブループリントを用意します。
前回の記事で作ったボタンWidgetを 子とするなら それを管理する 親Widget になります。
キャンバスに HorizontalBox を一つ、ドラッグ&ドロップします。
画面中央に配置します。
正確に中央に置くためには、ターゲットになるプラットフォームの解像度を調べて計算してもいいのですが、ここは アンカー と アライメント を利用します。
キャンバスの適当な位置にドラッグ&ドロップして、Anchors の設定を変更します。
ついでに Is Variable にもチェックを付けて有効にしておきます。
このチェックは ブループリントから触るためです。
Position X を ゼロ にして、Alignment X を 0.5 にすると 画面の中央に配置されます。
Size To Content にチェックを付けます。
これは 内容物に応じて自動的にHorizontalBox のサイズが変動するようにするフラグです。
有効にすることで、Alignment の効果も効いて、常にセンタリングされるようになります。座標計算しなくていいし簡単!ありがとうUE4!
この HorizontalBox に ボタンWidgetを並べる方法は2つ。
あらかじめキャンバスにある程度の数を配置してしまう方法と、ブループリントで必要な数のみ動的に配置する方法があります。
事前にキャンバスに置く場合↓
ゲームの仕様によりますが最大数を8個としています。初期表示設定 は Collapse で。
Widgetブループリントから動的に必要な数だけ生成する場合 ↓
試しにこの2種類のWidgetを用意して Reference Viewer で確認してみるとどちらも同じ構成でした。(下図は 動的配置した場合のもの)
となるとWidgetのViewport配置時のスパイクが心配になるので、何度か再生してみた。
あらかじめ追加する方法は、保険の意味もあって8個分 Collapse の状態で仕込んでおいて、必要なボタンだけを表示してアクティブにしています。実際に表示するのは6個。こちら再生するたびにピークが上下するので、ちょっと不安定な印象。
一方のブループリントから動的に生成する法は安定して同じカーブになる印象。
GPU(黄色いライン)にわかりやすく差が出ました。 環境やらプロジェクトによって同じになるかわからないのと、検証の方法が合ってるか自信がないので情報をお持ちの方はそのあたりを共有していただけると嬉しいです。
ということで、ひとまず動的に生成する方法について書いていきます。
ブループリントから、他のWidgetアセットを生成するのは、
Construct Object from Class ノードを使います。
このノードは名前が変わるので、スクショでの説明が難しいやつです。
取り出した直後 ↓ Object~ が NONE(設定無し)になっています。
前回の記事のWidgetをセットしたところ ↓
このノードで召喚されたWidgetが Return Value から出てくるので、あとはそれを HorizontalBox に子供として追加します。
この流れを ForLoop ノードで表示したい数ぶん繰り返せば並びます。
前回の記事で、レベルブループリントで簡単な表示テストをつなぎました。
そこの Create Widget にセットしたWidgetを 親Widgetに差し替えます。
再生してみると並んでいるのが確認できます。
スキマもいい感じでバッチリ。
さすがに味気ないので、ボタンのアイコンやラベルを管理する部分を作っていきます。
ゲームのメカニクスやプレイサイクルなどは、なかなか最初から完成しているというのは少なくて、やっぱりスクラップ&ビルドによって育てていくのがゲーム開発の常です。特にUI は遊び方が変われば、そっくり作り直しなんてこともしょっちゅうです。デザインを差し替えるだけならまだましですけどね。
よし遊びができたぜ! となっても今度は、ゲームの進行に合わせてUIが変化する仕組みも必要になることがよくあります。キーアイテムを拾ってない、まだレベルが解放されていない、魔法を覚えていない、などなど。最初から見せるのもモチベにはなりますが、たいていはネタバレになってワクワクが減るし、解放された感を無駄に強めにアピールする必要があったり、また情報が多いのでUIの学習コストが高くなります。
というわけで、選択肢の数と状態、並び順をコントロールできる仕組みを考えてみました。
ストラクチャ(User Defined Structure)を用意します。
アセットブラウザで右クリック > Blueprints > Structure を選択します。
ダブルクリックすると専用のエディタがポップアップします。
New Variable ボタンをクリックして必要な数の変数を定義します。
今回用意したのは以下の4つ
ID ・・・ 選択肢の内容を特定するための管理番号 Integer型
ItemLabelText ・・・ ボタンのラベル名 Text型
IconIndex ・・・ アイコンの絵を指定するIndex番号 Integer型
isEnable ・・・ 有効か無効のフラグ Boolean型
必要最小限だと思いますが、あとはお好みで。
次にアイコンテクスチャを用意します。
解像度が256x256px で アイコンひとつの大きさが 64x64pxです。
これも最初からデザインと並びがきっちり管理できていれば問題ないですけど、やはり途中で意味が合わなくなって使わなくなったり、追加で増えたりするので、並び順と管理番号が噛み合わなくなることがよくあります。テクスチャ容量も有限ですし、欠番を空けたままというのも勿体ない話。なので、ここはデザイナー都合で並べることにします。
これをインポートして Grayscale テクスチャにします。
インポートできたら、このテクスチャからマテリアルを用意します。
アセットブラウザのアイコンの上で右クリック > Create Material を選択。
マテリアルエディタが開きます。
ノードはこのようになります。
アイコンの大きさは一律なので、TexCoordノード(赤いやつ)の Tiling 値はUとV共に 0.25を入れてあります。
ブループリントからアイコンのIndex番号のみを受け取って計算するようにしています。もしGPUの負荷をちょっとでも減らしたい場合は、ブループリントの方でUV座標を用意して渡すようにします。
今回は、マテリアル内で計算するタイプを採用します。
このマテリアルを、ボタンWidget の Imageパーツにセットします。
セットする場所は、Appearance の Brush のところに アセットブラウザからマテリアルをドラッグ&ドロップするとセットできます。
ボタンの状態をアニメーションで用意します。
尺はなくて、色をけるだけのキーがあるだけです。
以下の2つはアニメーションをつけています。
まずは決定時の余韻。
選ばれなかった未練。
変数を5つほど追加。
変数を用意できたところで
ボタンをセットアップするためのカスタムイベントを用意します。
Inputs にピンを2つ追加します。ひとつは用意したストラクチャの型を選択。
ストラクチャ →
この追加したピンからドラッグして、Breakノードを取り出します。
このBreakノードで、中の変数に対して個別にアクセスできるようになるので、受け取った値を処理していきます。
最初の始めのほうにあるInteger型の変数はここで保存しておいて、決定処理をした際に親のWidgetに通知するときに使います。
右端のブランチノードはBoolean型の変数 isEnable が false のときに DISABLE のアニメーションを再生するようにつなぎます。基本 true なので、false のときだけ処理します。
作ったアニメーションを再生するためのカスタムイベントを用意します。
下段の2つは、ボタンをクリックした後のイベントになります。
決定時だけいつもと違う再生ノードを使っています。
これは最近のUE4で追加されたノードで、アニメーションの再生終了を待って処理を行いたいときに便利です。ここでは、イベントディスパッチャーをつないで、親Widgetにアニメーション終了を通知しています。
クリック処理は onMouseButtonDown という関数をオーバーライドして作成しますが、ちょっと判定がややこしいので今回は説明を省きます。
ボタンWidget(子)はいったんこの辺にしておきます。イベントディスパッチャーやフラグの取り扱いは、コンテンツをダウンロードして確認していただけると助かります。
次に、親Widgetを編集していきます。
まずは変数を新たに2つ用意。
デバッグ用にマクロを用意します。DebugButtonDataと命名。
ボタンを6個分用意しています。
左のでっかいノードたちは、順番に取り出していくとスムーズに取り出せます。
まずストラクチャ型の変数(あらかじめ配列にしておく)を Set のかたちで取り出します。その左のピンから、ドラッグして "make" で検索。Make Array ノードを見つけて取り出します。Add pin + ボタンを必要な数だけクリックしてピンを増やします。
今度もそこからドラッグして "make" で検索。 Make menuButtonData を選びます。
あとは複製(Ctrl + W)
このMakeノードを配列に入れた数が画面に並びます。そこで数を調べて変数に保存します。
このマクロをボタンを並べるForLoop の前に置きます。
ボタンを生成する部分を改造します。
それぞれ並んだボタンをブループリントから処理するために、ボタン型の配列を作ってセットしています。
作り方は Construct ノードの Return Valueピンからドラッグして Promote to variable を選択。
できた変数を適当にリネーム。今回は Buttons と命名。(複数形にすると配列であることがわかりやすい気がする)
Details(詳細)タブ から、配列に変更します。
右端の部分。
子Widgetの持つ関数やイベントにアクセスするには、Construct ノードの Return Valueピンからドラッグして検索すると出てきます。 このときUE4のエディタを英語環境にしていると、"call" で簡単にリストアップできます。
具体的な名前を憶えていなくてもこれで探せるので私はこのために英語環境にしているといってもいいくらいです。
試しに再生してみます。
グレイマン イイケツしてんな~
ではなく、無事アイコンとラベルがセットできました。
並べ替えや追加も簡単です。
さて、あとは入退場の動き作って、クリックされたら決定して、また次に備えて・・・
という流れを作っていくことになります。
汎用性とか柔軟性とか拡張性、そういったことを考えるとUIの実装難度はどんどん上がっていきます。さらに仕様バグが出やすくなるのでチェックコストも増えます。手間やら気遣いやらを考えると、作る側はあまり報われないのがUIなのかと、つい思ってしまいます。おっと目から水が・・・
とはいえ、ここっていうタイミングでそっとハンケチを差し出すようなUIが作れたら、それこそが UI 作ってて良かった、という喜びみたいなもので心が満たされるのです。
ひとまず、記事ではここまでの解説にしようかと思います。
なかなかのボリュームにもかかわらず読んでいただきありがとうございます。
途中で放り出しやがってけしからん!という方は以下のリンクからダウンロードして、お使いのUE4のプロジェクトフォルダの Content/Developers フォルダに解凍すると、エンジンで触れるようになるはずです。Verは 4.25.3 です。
クリックすると下のようなポップアップが出ますが無視して問題ありません。
不明な点や不具合等あれば、ブログのコメント欄かTwitterにてツッコミいただければ対応します。
ではでは
ステキなメニューライフを!