みつまめ杏仁

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

UE4のUMGでクイズゲームができた《記事まとめ》

アンリアルエンジン4でクイズゲームづくりにチャレンジということで、約1ヶ月8回に渡って記事を書きました。ここで一覧にしてまとめておこうと思います。

f:id:hiyokosabrey:20210705001532p:plain

 

完成したクイズゲームとしての仕様

  • 複数の問題から出題
  • 選択肢は 4つ
  • 時間制限がある
  • 制限時間内に一定数正解するとゲームクリア
  • 制限時間内に一定数正解できなければタイムアップ
  • 選択肢の配置はランダム

 

UIの 素材として作るものは

  • タイトル画面
  • 共通背景
  • 出題画面
  • 正解画面
  • 不正解画面
  • タイムアップ画面
  • クリア画面
  • タイマー表示
  • 正解数カウント表示

 

 

UE4のUMGを触ったことをない方でも試していただけるように、操作についてはなるべく省略しないように書いてます。

 

 まずは《準備編》と《組み立て編》を試していただくと、

紙芝居のような画面切り替え方式で一応のクイズゲームができます。

 

UE4 の バージョンは 4.26 を使っています。

汎用的なしくみを使うようにしてるので、少し前のバージョンでも問題ないと思います。たぶん。

 

limesode.hatenablog.com

 WidgetSwitcher(ウィジェットスイッチャー)という、ストックさせた複数のWidgetを切り替えることができるコンポーネントを利用します。

《準備編》ではビジュアル素材を揃えます。画面ごとにWidgetアセットを用意するところまで。

 

limesode.hatenablog.com

《組み立て編》ではブループリントで複数の画面を切り替えられるようにします。

ボタンクリックがトリガーとなって切り替えられるようにします。

まだクイズと選択肢は固定です。

 

 

 

ここからは、ゲームとしての機能を充実させつつ、実践で役立ちそうなテクニックについて紹介しています。

 

 ただの数字にラベルを付けて扱いやすくする仕組み。

WidgetSwitcherの切り替えは数字で行うのですが、いちいち覚えていられないので、Enum を利用します

limesode.hatenablog.com

 

 UIデザインに関わる立場として、プロトタイプから本番に向けて効率のいい作業フローを模索してみた成果。

無事プロトタイプが採用された体での本番デザインの差し替えについて。

limesode.hatenablog.com

 

 よりゲームらしく。

「タイマー」を扱うための仕組み。一時停止と再開のコントロール

limesode.hatenablog.com

 

さらにゲームらしく。

クリア条件を決めるとその進捗が必要になる。記事のボリュームが大きくなったので前後編に分割。

limesode.hatenablog.com

 さらにゲームらしく。

 クリア条件ができると、失敗条件もまた必要。

limesode.hatenablog.com

 

 クイズゲームらしく。

たくさんの問題の中からランダムに選ぶ。ランダムで選ぶことの難しさと面白さ。

limesode.hatenablog.com

問題を用意するのが結構大変ですが、探してみるとクイズの問題集を公開しているサイトが見つかるので、利用できると手っ取り早く楽しめます。

 

ここまでで完成とします。

youtu.be

 

今回のクイズゲーム制作を通して、UE4でのUI制作に慣れ親しんでもらえたら、またそのきっかけになれば嬉しいです。ぜひカスタマイズやアレンジに挑んでみてください。

 

ではでは

すきな UMGライフを!

そうだ、QUIZゲームを作ろう《おまけ編》たくさんの問題

 UE4クイズゲームを作る記事の続きです

 クイズゲームなのでここで扱う「問題」はトラブルではありません。なんか、内容に問題ありな題をつけてますが、「問題だらけ」とか「問題がいっぱい」よりはマシかなぁとか考えてみたけど、あまり変わらない気もする。

 繰り返し遊ぶとなると、問題がたくさんあると面白くなります。知ってる問題が何度も出題されるとラッキーだけど、ちょっと残念な気持ちになります。クイズって、自分の知識を試す場でもあるけど、自分の知らない知識に出会う場でもあると思うのですよ。自分はこの問題について全く知らないけど、知ってる人がいるから、出題されてるんだと思うと、ちょっぴり悔しいし、へぇっていう場面もあるのが面白いです。少なからず知識欲が刺激されてるんだと思います。

 黒猫のウィズとかは買い切りじゃないからオンラインでアプデできるし、メモリが問題になるなら場面ごとにダウンロードしてもいいわけで、そうなると、メンテすればいくらでも問題を作って増やせるよな、とか思ってるんですけど実際どうなってるんでしょうかね? 

 

さてさて、

ここまでで、一通りのゲームサイクルができたと思っています。

f:id:hiyokosabrey:20210625230121p:plain

超シンプルな構成です。

あとはここにお好みで素敵な絵やストーリー、スゴロク的マップ、育成などの要素を盛り込んでいけばグレードアップ!あとジャンルセレクトとかも!

 

はい 盛り上がりもほどほどに、問題を用意してランダムに選択する仕組みを作っていこうと思います。

 

仕様は、1つの問題文に対して選択肢が4つ。テキストが合計 5つ必要です。

今回少しアレンジを加える想定です。

 

目次

 

 

ストラクチャとデータテーブル

最終的にDataTable(データテーブル) というアセットを用意するのですが、その前にStructure(ストラクチャ=構造体)を作る必要があります。

f:id:hiyokosabrey:20210625232307p:plain

MicrosoftExcel を触ったことがある方なら少しはイメージしやすいかもしれないです。

Excelは列やセルに、データの表示形式を設定できます。 「数」なのか「数」なのか入力する人がルールを決めないと判断がつかないからです。UE4のデータテーブルは、Excelでいうところの各列(A~)が変数のタイプと連動するように設計されています。

 

Structureの作り方は、コンテンツブラウザで右クリック

コンテキストメニューから Blueprints > Structure を選択

f:id:hiyokosabrey:20210626104506p:plain

f:id:hiyokosabrey:20210626105015p:plain

QuestionStruct と命名。このあと検索するので、なるべく見つけやすい名前にするといいです。

ダブルクリックして編集します。

f:id:hiyokosabrey:20210626105247p:plain

エディタが開きます。

ブーリアン型の MemberVar_0  という変数名がすでに一つ登録されています。

名前を変更して、変数タイプを Text に変えます。

f:id:hiyokosabrey:20210626105830p:plain

変数の中身は、エディタ下部の Default Value に適当なテキストを入力しておきます。

構造体というのは複数の変数を一式として扱うことができます。

New Variable ボタンをクリックします。 5回。

同じ要領で、変数の型をText に変えておきます。

f:id:hiyokosabrey:20210626110947p:plain

変数名はとりあえず A0~A4 としました。アレンジ用に選択肢を5つにしました。

これで保存します。

 

Structureが用意できたので、今度は問題と解答を用意します。

 

問題と選択肢をCSVで用意する

 問題と解答のテキストは、CSV形式でUE4にインポートします。

CSV(カンマ区切り形式:Comma-Separated Values)はメモ帳みたいなテキストエディタでも簡単に作れますが、管理するならExcelのような表組ベースのエディタが便利です。

 ぼくは Office製品を手元に持ってないので、 Googleスプレッドシートを使いました。

Googleアカウントでのログインが必要になります。

f:id:hiyokosabrey:20210626121316p:plain

重要なのが、一行目に半角英数で変数名を入力することです。

Structureの変数名と同じにしておく必要があります。

 

 左上の A1 のセルは何を入力しても無視されるので、空欄でOK。

 A列は Name型という型で扱われることになります。今回は数字で連番にしておきます。

 なお、正解はひとつなので、C列 を 正解として扱うことに決めます。D~G列は間違いとしてそれっぽい、ひっかけ的な答えを適当に用意します。

 

 ある程度数が用意できたら、これをCSV形式でダウンロードしてローカルPCに保存します。

 ファイル名に拡張子 .csv がついていればUE4にドラッグ&ドロップできます。

 適当にDLしてからインポート前にリネームしてもOK。

 メモ帳で開くとこうなってました。

f:id:hiyokosabrey:20210626123815p:plain

 カンマで区切られてるけど、間が詰まりすぎて編集しにくいですね。

 一応 Tab文字(Tabキーで入力) を入れて整形できますが、

f:id:hiyokosabrey:20210626124240p:plain

 問題文が複数行だとあまり解決できている気がしません。

 複数行の場合、改行を意味する文字が見えないので、ダブルクォート → " " でくくることで範囲を示します。

 例えば3行あるとき

f:id:hiyokosabrey:20210626130303p:plain

CSV専用のエディタがあったほうが良さそうです。

 

UE4にインポートする

 で、このCSVUE4のコンテンツブラウザにドラッグ&ドロップすると、インポートダイアログが出てきます。

f:id:hiyokosabrey:20210626145934j:plain

Choose DataTable Row Type: のプルダウンから、用意した Structure の名前を検索します。

f:id:hiyokosabrey:20210626150112p:plain

インポートが完了するとCSVと同じファイル名のアセットが誕生します。

f:id:hiyokosabrey:20210626150325p:plain

 

カンマの数が多かったり少なかったりすると、警告されます。

f:id:hiyokosabrey:20210626151353p:plain

f:id:hiyokosabrey:20210626151024p:plain

OKボタンを押すとそれなりにインポートはしてくれます。

足りないときは、初期値が入れられて、多すぎるときは、その行はスキップされます。

CSVを修正して再インポートすればキレイにインポートされます。

インポート時にエラーで突っぱねるのではなく、ひとまず事故らないようにインポートしてくれるのはありがたいですね。

 

インポートしてできた DataTableを開いてみましょう。

f:id:hiyokosabrey:20210626155929p:plain

上の Reimport ボタンで、CSVファイルを何度でも再読み込みできます。

CSVファイルを更新したら、毎度 Reimport → Save という操作が必要です。

このエディタを開かなくても、コンテンツブラウザ上でもできます。

f:id:hiyokosabrey:20210626173721p:plain

再インポートがうまくいくと、右下から表示が現れて成功を知らせてくれます。

f:id:hiyokosabrey:20210626173826p:plain


また、このエディタからデータの追加や編集が可能です。

 

いまのところ改行の方法が見つけられていません。\n みたいな改行文字を入れておいて別途置換する方法を使えば1行で書いてしまうこともできますが、今回は問題を増やしやすくするために外部のエディタをオススメしておきます。

Googleスプレッドシートなら思いついたときに出先からでも追加できますしね。

共有すれば共同作業も可能です。インポートの手間はかかるけども、外部のエディタで作業するのはバックアップを取りやすいのもメリットじゃないかなと。

 

f:id:hiyokosabrey:20210627150848p:plain



 

 DataTabeleが保存できたら、あとはブループリントから読みだして表示してやるだけです。

 

データテーブルを読む関数

 クイズ本体の wb_Mainを編集します。

f:id:hiyokosabrey:20210626211141p:plain


 新しい関数を作ります。

 編集モードを Graph にして、My Blueprint の Functions から +ボタンをクリック。

f:id:hiyokosabrey:20210626211256p:plain

 関数名は getQuestionInDataTable と命名

 

関数を編集していきます。

getDataTableRowノードでDataTableへアクセスします。

f:id:hiyokosabrey:20210626212844p:plain

f:id:hiyokosabrey:20210626212833p:plain

 DataTable ピンのところに、用意したDataTableアセットをセットします。

 

 Row Name のピンには、数字を渡して中身を引き出すのですが、Name型というタイプじゃないと受け付けてくれません。

 

 今回は 番号 でクイズの問題を管理しているので、キャスト(型変換)が必要になります。

f:id:hiyokosabrey:20210626214054p:plain

 キャストには しりとりのように順番があります。

 数値 → 文字列 → Name型 の流れです。

 

 逆から進めると少し手間が減るので下の画像は逆引きでつないだ場合です。

f:id:hiyokosabrey:20210626212539p:plain

 関数のInputsピンを追加してからでも問題ありません。

 

 これで整数を使って任意のクイズを取り出すことができます。

 

 取り出したクイズは、 青い Out Row ピンから出力されます。

 そこから ドラッグして Breakノードを取り出します。

f:id:hiyokosabrey:20210626215504g:plain

 Breakノードはストラクチャ(構造体)の中身を個別に取り出せるようにするノードです。ちなみにストラクチャに戻すのは Makeノードになります。

 

 Structureで設定したピンがずらりと並びます。

 この内容を保持するために、この関数専用のローカル変数を追加します。

 関数編集中にのみ現れる Local Variables から +ボタンを 3回。

f:id:hiyokosabrey:20210626220800p:plain

 Text型、Boolean型、Integer型の 3つ。

f:id:hiyokosabrey:20210626221135p:plain

 Text型とBoolean型は配列にします。

 Variable Type の 右端にあるアイコンをクリックすると、小さなリストメニューがポップします。

f:id:hiyokosabrey:20210626221541p:plain

 9マスのグリッド状のアイコンが配列です。

 

 名前に  temp_ と付けているのは、一時的なという意味の temporary(テンポラリー) の略です。

 関数が呼び出されるたびに作られて、関数の処理が終了すると破棄されます。

 刹那的な変数なので関数の外に持ち出すことができません。

 

 この一時的配列変数をグラフにドロップして、Add ノードを取り出します。

f:id:hiyokosabrey:20210626222729p:plain

 もう一つのBoolean型の配列も同じように Add ノードを取り出します。

 

 getDataTableRowノードの Row Found ピンとつなぎ、Breakノードの A1 ピンともつなぎます。

f:id:hiyokosabrey:20210626223008p:plain

 残りの A2~A4 のピンをつなぐために、Add ノードを複製します。

f:id:hiyokosabrey:20210626225711p:plain

 新たに3セット追加。
f:id:hiyokosabrey:20210626225933p:plain

 この灰色のノードたちに魂を入れます。

f:id:hiyokosabrey:20210626230033p:plain

 とどめに Shuffle ノードをつなぐ。

f:id:hiyokosabrey:20210626230412p:plain

 配列のGetノードは下に移動しました。

f:id:hiyokosabrey:20210626230541p:plain

 この Shuffle ノードは配列の中身を文字通りシャッフルしてくれます。

 A0 のピンは 正解で配列には含めていません。

 A1~A4 はすべて間違いなので、どれだけシャッフルしても何の問題もないのです。

 

 次に、正解を上書きするためのランダムな値を作ります。

f:id:hiyokosabrey:20210626231442p:plain

 Random Integer ノードは 0 ~ Max-1 の間でランダムな値を生みだします。

 この配列は 4回の Addノードをつないでいるので、 Maxには  4 を指定。 0 ~3 の4面ダイスを振るイメージ。

 こうして選ばれた数字を ローカル変数に取り置きします。

 

 次に、配列の中身に対して、指定したIndex番号の中身を書き換えるのが Set Array Elem ノードです。これをつなぎます。

f:id:hiyokosabrey:20210626232410p:plain

 Item ピンのところに DataTable から取り出す A0 のピンをつなぎます。

f:id:hiyokosabrey:20210626232904p:plain

 正解を上書きする場所(Index番号)が決まったので、おなじく判定用の配列にも反映させます。

f:id:hiyokosabrey:20210626233239p:plain

 

 配列の中身が変化する流れとしては3つのステップ。

f:id:hiyokosabrey:20210627154012p:plain

 

 

 関数の最後はReturn (リターン)ノードを取り出してつなぎます。

 関数にだけ用意されているノードです。

 何もないところで右クリックして、 .  ←ピリオドで検索すると出てきます。

f:id:hiyokosabrey:20210626233736p:plain

 この Returnノードは 戻り値(もどりち)とか Return Value とかいう外に値を渡すための ピンを追加することができます。

 

 このReturnノードに、 Breakノードでバラした問題文 Qピン をドロップしてつなぎます。

f:id:hiyokosabrey:20210627111921p:plain

 ラインが重なるのと見づらいので、途中でダブルクリックしてリルートノードを使って経路を整えるのをオススメ。

f:id:hiyokosabrey:20210627112218p:plain

 あとは、配列変数を 2つを同様に Returnノードに渡します。

f:id:hiyokosabrey:20210627112503p:plain

 これで関数は完成です。

 この関数のInputs(入力) と Outputs(出力)はこのようになりました。

f:id:hiyokosabrey:20210627112736p:plain

 Detailsタブから、先に Inputs も Outputs もピンを追加しておくこともできます。

 +ボタンを押して追加するのですが、ピンの名前が NewParam みたいなテキトーな名前になります。

 ノードに直接変数からドロップすると変数名を使ってピンを追加してくれるので、小さなことですが、手間を減らしつつブループリントが分かりやすくなるのでオススメです。

 Inputs と Outputs ピンは変数の型を設定して利用します。

 これは、フィルターというかラベルみたいなものなので、変数が作られているわけではありません。古くからある知育玩具で、形を合わせないとブロックが通過できないアレに似ていますね。

 

 "temp_" とかは関数の外では不要な情報です。気になる場合は整えます。

f:id:hiyokosabrey:20210627113547p:plain

 

 

クイズを選択

 関数の編集が終了したので EventGraphに戻ります。

f:id:hiyokosabrey:20210627115627p:plain

 

 クイズを選択してセットするカスタムイベントを用意します。

 空いてるところでカスタムイベントを取り出します。名前を chooseQuiz としました。

 そこへ関数を グラフにドロップしてつなぎます

f:id:hiyokosabrey:20210627115834p:plain


この関数の戻り値(ReturnValue)を保持する変数を新しく追加します。

Variables の+ボタンから2つ変数を追加して配列にします。

f:id:hiyokosabrey:20210627120719p:plain

 この配列の中身は数が予め決まっていて、ゲームの開始から終了まで変動しないので、サイズを固定してしまいます。

 

 まず追加した2つの配列変数をドロップ。

f:id:hiyokosabrey:20210627121201p:plain

 Event Pre Construct で初期化します。

 そこに Resize ノードをつないで、値を 4 にします。

f:id:hiyokosabrey:20210627121314p:plain

 配列変数って、作っただけだと何個空きを用意するか決まっていないので、可能な限り前もって確保しておくようにします。

 

 この配列変数を、クイズ選択の関数につなぎます。

 併せて、クイズの問題文もキャンバスのTextSetTextノードでセットします。

f:id:hiyokosabrey:20210627130024p:plain

f:id:hiyokosabrey:20210627125652p:plain

 この続きに、回答ボタンへのSetTextをつないでいきます。

f:id:hiyokosabrey:20210627130411p:plain

 

 あとはこのカスタムイベントを、クイズスタートイベントのところに挿入します。

f:id:hiyokosabrey:20210627131510p:plain

f:id:hiyokosabrey:20210627131848p:plain

 これで問題文と回答ボタンが更新されます。

 

回答ボタンに配列をセット

 つぎに、回答ボタンに対して正解、不正解をセットしてやります。

今までは、正解ボタンを決めて処理してましたが、ここで配列を反映させます。

f:id:hiyokosabrey:20210627132335p:plain

 

あともう少しで完成です。

いったんコンパイルして確認してみましょうか。

f:id:hiyokosabrey:20210627133449p:plain

 回答ボタンが毎回シャッフルされています。同じ問題で答えが判っていてもそれなりに戸惑うはず。消去法に頼れなくなるやつです。これが今回アレンジとして入れたかった仕様です。

 

完成一歩手前!

 

ランダムチョイス

 最後に いっぱい考えた問題をランダムで選ぶ処理を追加します。

f:id:hiyokosabrey:20210627134020p:plain

 このピンに問題番号を渡せばいいのですが、ただランダムにすると、重複することがあります。

f:id:hiyokosabrey:20210627134502p:plain

 重複を回避する方法は いくつかあるみたいですが、今回のクイズゲームでは制限時間があり、それほど多く回答できる機会はないという想定です。

 問題の数が少ないうちは トランプ同様に 配列を Shuffle するのが簡単なのですが、問題数が多くなると、Shuffle用の巨大な配列を作る必要が出るし、処理負荷が気になります。

 なので、

 今回はランダムな値を記録用の配列に積んでいく作戦を採用します。

 このランダムな値を積むときに、重複していないかをチェックして、重複を発見すると、積むのを止めて、もう一度ランダムな値を作るところからやり直します。

 

 選択履歴を保存しておくための配列変数を新しく追加します。

f:id:hiyokosabrey:20210627164450p:plain

 SelectedQuizと命名

 これをクイズゲーム開始時に初期化しないといけないので、初期化用のカスタムイベント Initialize で CLEARノードと合わせてつなぐ。

f:id:hiyokosabrey:20210627164744p:plain

 CLEAR は 配列の中身をキレイサッパリ空っぽにしてくれるノードです。

 いろいろ履歴が積まれていてもここでお掃除。

 

 ランダムセレクト用に新しく関数を作ります。

 ローカル変数を 3つ。 Integer型(整数型)が 2つ。Boolean型が 1つ。

f:id:hiyokosabrey:20210627165721p:plain

temp_NewNum は、選択決定した問題の番号。

temp_Max は、用意した問題の最大数を意味します。

temp_isFound は、既に選んだことがあるかどうかの判定用に使います。

 

 関数の中身はこうなります。

f:id:hiyokosabrey:20210627170211p:plain

 まずクイズの最大数を受け取って、WhileLoop で最大数までのランダムな値をゲット、重複チェックをしています。

 重複がなければ選択履歴用の配列変数 SelectedQuiz に追加して完了というものです。

 

 WhileLoop ノードは、 Condition(条件)が true だと、LoopBody を実行し、false になると Loopを止めます。

 WhileLoop のセリフ

 「君 (temp_isFound) がッ false になるまで 僕は LoopBody の処理を止めないッ!」

 

 LoopBody の処理については以下のようになってます。

f:id:hiyokosabrey:20210627184844p:plain

 

今回時間制限タイプのクイズなので、制限時間が長くなって間違いを選びまくると問題数が足りなくなるのが予想されます。すべての問題を出題してしまうと無限ループになってしまいます。

解決策として思いつくのは 3つ

  • 問題数を増やす(最短時間で間違いを連打しても大丈夫なくらい用意する)
  • 間違いを選択するとタイムを減らす(ペナルティな印象が強くなるが・・・)
  • すべての問題を出題したらリセットする

安全で確実なのは、3つ目のリセット。

ということで、選択履歴用配列の数が問題数と同じになれば、CLEARするようにします。

f:id:hiyokosabrey:20210628220810p:plain

LENGTH(レングス)ノードは、配列の数を調べてくれるノード。

これで安心です。

 

 

 これで関数ができました。

 ここに挿入すればOK。

 

Max の値は、今回用意できた問題数ということで 12 を入力しています。

ここは用意できた数に合わせて変更してください。

f:id:hiyokosabrey:20210627185258p:plain

 

 編集は以上です。

 

wb_Main の全貌

※関数とマクロは除く

f:id:hiyokosabrey:20210627185652p:plain

 

  おつかれさまです。

 これで一通りの実装ができました。

 コンパイルして保存したら確認してみます。

 

youtu.be

 

完成!

 UI要素だけで完結できるゲームとしてクイズゲームをチョイスしました。なかなかのボリュームになり、途中くじけそうになりましたが、無事なんとかカタチにはできたので胸をなでおろしています。

 基本操作の説明から始まって、できるだけ現場で使える実践向きの考え方やテクニックを織り込んで記事を書いてみました。

 ビジュアルについてはあえて力を抜いています。デザイナーの方はぜひアレンジを楽しんでほしいし、画像系の作成環境に自信のない方でも進められるようにしたかったからです。どっかのストレージにアップするのも考えましたが、このぐらいのクオリティでプロトタイプがサクッと作れる、というのをアピールしたかったというのもあります。

 

 多少の手戻りっぽい修正を意図的に入れましたが、実際には試行錯誤しながら、確実に成功する方法を記事にしています。できればもっとデバッグの方法も紹介したいのですが、さすがに面白くないでしょうね。

 

 UE5が早期アクセスを開始され、世間が驚愕のグラフィックに沸いてる中、UMGを使って地味にクイズゲームを作るという、なんだかイベント会場を間違えて露店を開いたような気分になりつつも、UE5にゲーム開発の未来を見たUIクリエイター志望の方々、またUE4を初めて触る現役UIクリエイターの皆様へ、少しでもエールを送れたらなというのをモチベーションにしてここまで来れました。

 エディタの操作方法についてもなるべく丁寧に説明を書いたつもりですが、分かりにくいところや要望などあれば、お気軽にお問い合わせくだされば対応します。

アレンジのやり方なども内容次第ですがアドバイスできるかもしれません。

このブログのコメント機能(承認制なので応答まで時間がかかります)かTwitter@MMAn_nin)にてお待ちしています。

 

 

ではでは

ステキなクイズゲームライフを!

 

 

 

今回参考にさせていただいた記事

キンアジちゃんのブログ。現場で使えるテクニカルな記事満載!

kinnaji.com

 

 

そうだ、QUIZゲームを作ろう《おまけ編》クリアへの道 後編

前回タイマーの改造に始まって、ForLoopやらSelectノードやら配列やら説明してたらボリュームがえらいことになったので、2部構成にしました。

 

クイズゲームとして、制限時間内に規定回数分正解するとゲームクリア。という仕様に決めました。前回の記事でその正解数をカウント表示するWidgetを作ったのでいよいよ組み込んでいこうと意気込んだところで、タイムアップの時にタイトル画面に戻るようにしてなかったのを思い出しました。

 

目次

 

 

タイトルへ戻る

タイムアップしてもまたクイズ画面に戻るようになっているので、タイトル画面に移行するように変えます。

ゲーム本体の wb_QuizGame を編集します。

f:id:hiyokosabrey:20210620005150p:plain

 

NEXTボタンを押したときの処理はこうなってました。

f:id:hiyokosabrey:20210620002349p:plain


タイムアップの時だけ、専用のカスタムイベントを用意してやります。

f:id:hiyokosabrey:20210620002917p:plain

これでOK。コンパイルして確認してみる。

 

・・・

 

タイトル画面にちゃんと戻ることができているけど、リトライするとタイマーがリセットできていないことが判明。もう少し詳しく調べるために変数を確認することにします。

タイマーを管理しているのは wb_Main です。

f:id:hiyokosabrey:20210620104633p:plain

効率よくチェックしたいので、タイマーの長さを短くするために 変数 Seconds の初期値を 5.0 にして再生してみると。

f:id:hiyokosabrey:20210620104818p:plain

 タイトル画面で一度出たきりでした。

何度かプレイしてみて判ったことが 2つ。

  1. 残り時間を保持する変数 RemainTime はリセットされる機会がない
  2. WidgetSwitcher に登録したWidgetは、表示されていなくても(アクティブになてっていなくても)EventConstruct は実行される

変数がどこでどう使われているかの確認は、Find References を使います。

f:id:hiyokosabrey:20210620110131p:plain

Variablesリストの変数の上から右クリックメニューで選択できます。

 

エディタ下に Find Results というタブがあって、そこに検索結果が表示されます。

f:id:hiyokosabrey:20210620215138p:plain

このリスト上でダブルクリックすると、配置されている箇所にワープします。

変数で 検索すると、 Get と Set が変数名の前に付きます。

これは、ジャンプするとわかるのですが、値の 入力 と 取得 を表しています。

 

タイムをリセットするには、Seconds の値と同じにする必要があります。Set ~ のところを見てみると、Event Construction のところと、 ポーズをかけたときに関数内の2か所で、 Event Construct はプレイ開始時 1回だけしか通ってませんでした。

なので、回答ボタンを押してポーズしたら、RemainTime は値が変更されます。そしてそのままタイムアップになってリトライしても、変更されたままになっていました。

 

ということで、初期化用のイベントを作って、別の場所からも呼べるようにします。

カスタムイベントを追加して、 initialize と命名。そこへ Event Construct でつないでいたのを移植します。

f:id:hiyokosabrey:20210620223011p:plain

Seconds もこの際なので、 Set の形で置きなおして値を設定しやすくしました。

f:id:hiyokosabrey:20210620222406p:plain

Event Construct には カスタムイベントをつなげば元の通りに動きます。

コンパイルして保存します。

 

 

再び、クイズゲーム本体にに戻ってきました。

この初期化のイベントを、ここに挟めば完了。

f:id:hiyokosabrey:20210620223708p:plain

 コンパイルして問題なければ、保存します。

タイマー問題はこれで解決です。

 

 

組み込む

ようやくカウント表示を組み込んでいきます。

 

組み込む場所は 2か所。

  1. クイズの回答する画面 ← 現在の状況を確認したい 演出は不要
  2. 正解したときの画面 ← 獲得したことを演出付きで見せたい

 

この 2つ タイミングを合わせる必要がありそうです。

流れとしては、

回答ボタンを押した瞬間、正解画面に切り替わります。

f:id:hiyokosabrey:20210621223300p:plain

演出をじっくり見せるには、時間制限のある回答画面では嫌われるので、正解演出画面で見せるのがベスト。

NEXTボタンを押して画面が切り替わると、すぐに次のクイズが始まるので、その時点でカウント表示は更新されていてほしい。

f:id:hiyokosabrey:20210621223557p:plain

流れはこれで行こうと思います。

 

時間制限については、メインの wb_Main でのみ表示されるパーツだったので、wb_Main で管理していました。今回は複数画面に配置しているので、カウント数の管理はもう一段上の wb_QuizGame でやるほうがよさそうです。

 

ということで組み込みを進めます。

まずはクイズのメイン画面から。

f:id:hiyokosabrey:20210619231108p:plain

 

キャンバスに、wb_Count をドロップして配置します。

f:id:hiyokosabrey:20210620000057p:plain

いい感じの場所に配置したら、編集モードをGraphにします。

 

カスタムイベントを新しく追加。

配置したばかりのカウントWidget の関数を呼び出すだけの簡単なお仕事。

f:id:hiyokosabrey:20210621221809p:plain

 このイベントは外から呼び出されるようにしています。いわば中継ってやつです。

 

これで、wb_Main の編集は完了です。

コンパイルして問題がなければ保存します。

 

つぎは、正解演出画面。

f:id:hiyokosabrey:20210621231702p:plain

目立つ位置に置きました。

f:id:hiyokosabrey:20210621232321p:plain

ここで提案。

演出表示が増えてきました。

緊張感のある回答画面から遷移してきて、時間も止まっているので一息つける画面でもあります。

NEXTボタンの表示タイミングに間を開けるのが気遣いというものです。※諸説あり

デフォルト状態として Visibility を Hidden にしておきます。

f:id:hiyokosabrey:20210621234655p:plain

 

キャンバスはここまで。

編集モードを Graph に切り替えます。

 

こうなっています。

f:id:hiyokosabrey:20210622003002p:plain

ここに、カウントのエフェクト再生とNEXTボタンの表示にタイムラグをつくるDelayノードをつなぎます。

f:id:hiyokosabrey:20210622005405p:plain

PlayAnimation ノード と wb_Count の NewGetWithEffect イベントを呼び出したあと、Delay ノードで、少し待ってから、 NEXTボタンの Visibility を Visible に変えます。

NEXTボタンを押したときのイベント OnClicked で、再び NEXTボタンの Visibility を Hidden に戻しておきます。

 

Delayノードは、流れに一呼吸おいたりしてリズムを作ることができるので、演出を重視したい時には大変重宝しますが、使用には注意が必要です。

f:id:hiyokosabrey:20210622222027p:plain

  • 後ろの処理は待たされます(処理にポーズがかかるイメージ)
  • 関数の中では使用できません(右上の時計アイコンが目印)

 

一定時間後に○○ という場合は、Set Timer by Event や Set Timer by Function との違いを理解して使いこなせるとカッコいいと思います。

 

 

NewGetWithEffect イベントには パラメータを受け取るようになっているので、どこかからパラメータ用の値を持ってこないといけません。

f:id:hiyokosabrey:20210622222814p:plain

先頭のイベント StartAnim はBPインターフェイスで作ったものです。

クリックしても Detailsタブは空欄です。

f:id:hiyokosabrey:20210622010417p:plain

 

 

ここでBPインターフェイスを再び編集します。

f:id:hiyokosabrey:20210622010851p:plain

エディタを開いたら、Inputs のところから +ボタンを押して Integer型を選択します。

f:id:hiyokosabrey:20210622011232p:plain

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

再び、wb_Right に戻ってみると、

f:id:hiyokosabrey:20210622011614p:plain

ピンが追加されています。

このピンと、カウントWidgetのイベントをつなぎます。

f:id:hiyokosabrey:20210622011834p:plain

まっすぐつなぐとノードと重なるので、リルートノードを使っています。

 

最後に繰り返し遊ぶときのために、カウントの初期化イベントを用意します。

f:id:hiyokosabrey:20210623010944p:plain

 

この正解演出用のWidgetは これで編集終了です。

コンパイルして保存します。

 

 

つなげる

組み込んだカウントを機能させるために、クイズ本体 wb_QuizGame を編集します。

f:id:hiyokosabrey:20210622012048p:plain

 

まずは 編集モードを Graph にします。

 

カウント数を管理するための、Integer型の変数をひとつ追加します。

 f:id:hiyokosabrey:20210622224422p:plain

 

最初にカウントアップしたいのは正解演出画面なので、

正解演出画面に移行するタイミングで カウントをプラスします。

f:id:hiyokosabrey:20210622224846p:plain

正解ボタンを押したときだけ、Count変数に +1 します。

そして、そのプラスされた値を、StartAnim 関数に渡してやります。

f:id:hiyokosabrey:20210622231003p:plain

足し算するための ノードは、 int + int で探します。

f:id:hiyokosabrey:20210622231216p:plain

 

次に正解演出画面から帰ってきたときの処理。

ここでようやくメイン画面 wb_Main のカウントを増やしてやります。

 

メイン画面に切り替えるのは、正解演出画面と、誤答演出画面からの帰り道。

同じところに戻ってくるので、ここを切り分けます。

f:id:hiyokosabrey:20210622232450p:plain

 

正解画面専用のカスタムイベントを追加します。

f:id:hiyokosabrey:20210622232729p:plain

ちょっと狭くなってきたので、少しスペースを作りました。

このイベントでやることは、カウントが規定数に達したかどうかを判定することです。

f:id:hiyokosabrey:20210622233153p:plain

≦ (小なりイコール)で判定します。 ノード検索は半角で <=  と入力します。

f:id:hiyokosabrey:20210622233417p:plain

これで 3 以下 か 3 と同じなら True 、それ以外なら False という判定結果になります。

 

入力ピンが縦に並ぶので直感的じゃないですが、こうなってます。

f:id:hiyokosabrey:20210622234323p:plain

 

判定結果が True ということは、まだノルマは達成していないので、

f:id:hiyokosabrey:20210622235125p:plain

ここで、メイン画面のカウントを更新します。そのまま元の流れに戻します。

 

判定結果が False の場合。つまりは Count に 4 という値が入っているときです。

正解演出画面にいく直前に Count は +1 するので、3の状態で正解すると、まず 4 になってカウントアップ演出を見せてから戻ってきます。

ここで メイン画面に行くのではなく、ゲームクリア画面に行けばいいわけです。

 

いったんこの状態でコンパイルして確認してみましょう。

まだ設問が一つでさみしいですが、想定通りになっているでしょうか?

 

 

Congratulations!

クリアおめでとう画面を作ります。

 

構造的に変わったことをしないのと、タイトル画面に戻るボタンを置くので、 wb_Title を複製して改造することにしました。

f:id:hiyokosabrey:20210623001916p:plain

名前は wb_Clear にしました。

f:id:hiyokosabrey:20210623000327p:plain

やっぱり紙吹雪とか散らしたくなりますね。

いい感じの画像でも配置してみてください。

 

ここはオマケのオマケでテクスチャを使わないマテリアルを使ったキラリをご紹介。

マテリアルエディタの操作の説明は省くので要望があれば別の記事で説明します。

コンテンツブラウザで、Materialを作ったら、下のようにノードをつなぎます。

f:id:hiyokosabrey:20210623002456p:plain

右端のノードを選択した状態で、 Detailsタブから

Material Domain を User Interface

Blend Mode を Transparent

にすると、Widgetで使用できるようになります。

 

コンパイルして保存したら、コンテンツブラウザから、直接Widgetのキャンバスにドラッグ&ドロップできます。

f:id:hiyokosabrey:20210623003021p:plain

 

ブループリントは特に何もしないので、クリア画面はこれで完成です。

コンパイルして保存します。

 

画面を新しく作ったら、WidgetSwitcher に追加しないといけません。

ゲーム本体の wb_QuizGame を編集します。

f:id:hiyokosabrey:20210622012048p:plain

 

編集モードを Designer にします。

 

f:id:hiyokosabrey:20210623003601p:plain

WidgetSwitcher の上にドロップすると、一番下に追加されます。

 

いーなむの編集をします。

f:id:hiyokosabrey:20210623004059p:plain

Newボタンを押してクリア用のいーなむを追加します。

f:id:hiyokosabrey:20210623004203p:plain

保存したら閉じます。

 

ゲーム本体の wb_QuizGame に戻ります。

 

Branchノードの False に画面切り替えする関数をつなぎます。

NewScreenState に クリア画面のいーなむが追加されているので選択します。

f:id:hiyokosabrey:20210623004454p:plain

 

つぎに、クリアおめでとう画面からの戻り先を用意します。

今回はタイムアップと同じにします。

f:id:hiyokosabrey:20210623011648p:plain

 

あと少し

クリアしてもしなくても、タイトル画面に戻るようにしているので、繰り返し遊ぶことができます。

カウントをリセットしないと続きからカウントしてしまうので、カウントの初期化処理を入れます。場所はこのタイトル画面へ切り替えるところ。

f:id:hiyokosabrey:20210623012839p:plain

変数を 0 に戻すのと、正解演出画面の表示を 0 個に戻します。

 

今回はこれで完成です。

このグラフの全貌です。

f:id:hiyokosabrey:20210623013600p:plain

コンパイルして保存したら確認してみましょう。

 

youtu.be

  

今回はここまでです。

 わからないところやツッコミなどあれば、このブログのコメント機能をご利用いただくか、Twitter@MMAn_nin)にてメッセージを送ってください。

 

それなりにゲームぽくできたので、次回でおまけ編を最終回にしようと思います。

クイズゲームらしく設問を増やしてランダムに出題できるようにします。

 

ではでは

ステキなクリア画面ライフを!

 

 

 

 

そうだ、QUIZゲームを作ろう《おまけ編》クリアへの道 前編

引き続きクイズゲームを作っていきます。

 

ここでいったん過去記事リンク

『そうだ、UE4でQUIZゲームを作ろう』

  1. 《準備編》
  2. 《組み立て編》
  3. 《おまけ編》いーなむで切り替えをわかりやすく
  4. 《おまけ編》ボタンのデザインをどうにかする
  5. 《おまけ編》時間制限をつける

 

 1、2まではUMGのチュートリアル記事として書いてみたので、ある程度の基本的なオペレーションに慣れるのと、複数のWidget間のやり取りをゴールとしています。

3以降はおまけ編としていますが、実践的で使えそう(だといいな)なテクニックを紹介しつつそこそこクイズゲームらしく仕上げたいという思いで書いています。自転車の補助輪として見ていただけるといいのかなと。

 

目次

 

 

 

クリアの条件

 タイマーを実装して緊張感を演出できるようになったところで、今回はゲームクリアを実装することにします。

始まりと終わりがあって、そこに成功と失敗が加われば最低限ゲームとしての体をなすことができます。

 

ゲームクリアには条件があったほうが達成感が得られて、嬉しくなりますよね。

そこでクリア条件を考えました。

 

 クリア条件: 制限時間内に設定された正解数を達成すること

 

となると、失敗の条件はおのずと、タイムアップのみ。

 

 

タイマーのしくみを変える

というわけで、さっそく タイマーを改造するところから。

f:id:hiyokosabrey:20210612212145p:plain

前回までのつくりでは、回答して戻ってくるたびに、タイマーがリセットされます。

それを、

回答ボタン押すタイマーストップMain画面に戻ってくるタイマー再開

という流れに改造します。

 

タイマーを再生するのは、Play Animationノード。

そこに Start at Time というパラメータが用意されています。

f:id:hiyokosabrey:20210612222350p:plain
ここが 0.0 だと、頭から再生します。再生終了は 1.0 です。

ということは、ここにストップした時の時間=進捗率 を値にして入力すればよさそうです。

 

UMG では アニメーションをポーズした際に、どこで止まったかを知ることができるようになっています。

f:id:hiyokosabrey:20210612222306p:plain

Pause Animationノードの ReturnValue  を変数に取り置きしておいて、次回 Play Animation ノードに来たときに、Start at Time として渡すだけで再開できます。

 

ですが、今回はこの方法は使いません。0に戻す処理が必要だし、今後 wb_Main の部分的な表示パーツ になる予定なので、メンテナンスしやすいように、wb_Main から受け取る構造にします。

というわけで、Start at Time のピンをカスタムイベントとつなぎます。

f:id:hiyokosabrey:20210612222116g:plain

これでタイマーは改造終了です。

ラインのクロスを解消するのは、カスタムイベントを選択状態にして、Detailsタブから行います。

f:id:hiyokosabrey:20210612222854p:plain

コンパイルして問題なければ、保存して閉じてOK。

 

 

タイマーの制御

となると、タイマーのカスタムイベントに異変発生!ということで、きっと大騒ぎのはず・・・

wb_Main を開こうとすると、

f:id:hiyokosabrey:20210612223930p:plain

アセットのアイコンに未保存のマークがついています。

参照される側が更新されたので、参照してる側としてはコンパイルし直してね、という意味です。

開いてみるとピンが増えてるのが確認できます。

f:id:hiyokosabrey:20210620142516p:plain

 ※《おまけ編》 時間制限をつける の最後のほうで変数管理にした状態で説明します。

 

ここで、残り時間を保持するための変数を一つ追加します。

変数の増やし方は

Variablesリストの右上にある +ボタンを押します。

f:id:hiyokosabrey:20210613000508p:plain

名前は RemainTime

タイプは Float を選択

f:id:hiyokosabrey:20210613000708p:plain

残り時間をゲットして、この変数にセットすることになるのですが、場所は回答ボタンを押した直後。

f:id:hiyokosabrey:20210620142805p:plain

まずVariablesリストからグラフにドロップして取り出しますが、Set~ を選択します。

f:id:hiyokosabrey:20210613001559p:plain

f:id:hiyokosabrey:20210613001849p:plain

Alt キーを押しながらドロップすると、選択の手間がスキップできてラクチンです。

ちなみに Ctrl キーを押しながらだと、 Getタイプでノード化できます。

 

次に、白い4本のラインをつなぎ替えるのですが、スマートにやりましょう。

五角形の白いピンの上で、Ctrlキーを押しながらドラッグを開始すると、複数のラインがまとめて動かせます。

f:id:hiyokosabrey:20210613002634g:plain

 

セットしたタイマーをリセットする Clear~ノードを後ろにつないで挿入完了。

f:id:hiyokosabrey:20210613103200p:plain

実は タイマーハンドラーから、残り時間を取り出せるのですが、クリアしてしまうと 0 になってしまうために、クリアする直前でGetする必要があるのです。

 

Get Timer Rimaining Time by Handle ノードを取り出して変数に入力します。

f:id:hiyokosabrey:20210613104255g:plain

 

ここでアニメーションも止めないといけません。

ノードの配置を整えつつ wb_Timer に用意したイベント StopTimer を呼び出します。

f:id:hiyokosabrey:20210613104956p:plain

中央上部にある青い玉は、リルートノードといって、ラインの流れを意図的に変えることができる便利なノードです。ラインの上でダブルクリックすると間に挿入されるのであとはドラッグしていい感じのポジションに移動させるとつながりが整理できます。

 

これで 残り時間保存タイマーストップアニメーションストップ の流れができました。

 

一見するとスマートにまとまってる気がしますが、タイムアップがゲームオーバーになるという時間にシビアなゲーム性なので、時間を止めるまでの処理は少ないに越したことはありません。クリックした瞬間に止まってほしいと思うはず・・・。

 

 

ブループリントマクロを使う

そこで順番を変えようと思います。画面切り替え通知のイベントディスパッチャーの前にしたいと思います。

f:id:hiyokosabrey:20210620143034p:plain

 といっても、コピペで並べるようなことはしません。マクロを使います。

f:id:hiyokosabrey:20210613122730p:plain

 マウスでドラッグしてマクロにするノードをまとめて選択します。

どれでもいいので、ノードの上で右クリックして、Collapse to Macro を選択。

f:id:hiyokosabrey:20210613123100p:plain

すると

f:id:hiyokosabrey:20210613123753p:plain

ひとつのノードにまとまりました。

ダブルクリックして中身を整えます。

f:id:hiyokosabrey:20210613124001p:plain

右端のOutputノードまでがつながっていないのでつなぎます。

f:id:hiyokosabrey:20210613124251g:plain

これでマクロ編集終了です。

 

元のグラフに戻るには、 マクロのタブを閉じるか、Event Graphのタブにきりかえます。

f:id:hiyokosabrey:20210613124815p:plain

マクロの名前を変えておきます。

エディタ左の Macros リストのところに作ったばかりのマクロが追加されているので、右クリックするか、F12キーでリネームします。

f:id:hiyokosabrey:20210613135055p:plain

pauseTime と命名しました。

次につながったラインを切ります。

右クリックでまとめて切ることができます。

f:id:hiyokosabrey:20210613135701g:plain

このマクロを間に入れます。

f:id:hiyokosabrey:20210620143234p:plain
 

残りの分もグラフにマクロをドロップして同じようにつなぐのですが、ちょっとだけラクをしましょう。

カスタムイベントのピンからドラッグして検索するだけでも、挿入できます。

f:id:hiyokosabrey:20210620145027g:plain
 

こうなりました。

f:id:hiyokosabrey:20210620143500p:plain


マクロは、同じノードのまとまりが、別の場所でも使われるときに便利です。

いくつかのノードがまとまると、ある一つのタスクになります。タスクに名前を付けて管理できるので、何をしているか分かり易くなるし、ブループリントがスッキリします。マクロは関数やイベントと違い、自身以外のアセットから呼び出せません。ローカルな存在です。

 

 

停止から再開へ

あとは再開した時の処理。

その前に、残り時間を保持する変数は、初期値を制限時間と同じにしておかないと、残り時間がゼロでゲームスタートしてしまいます。

Event Construct で 2つの変数 RemainTime と Seconds の値を揃えておきます。

f:id:hiyokosabrey:20210613142717p:plain

これで、Seconds の初期値をいじっても、RemainTime も同じ値にできます。

10秒かな、15秒かな~とかいろいろ試すには、Seconds の値をいじるだけで済みます。

 

改めて再開処理。

これも下の部分を改造していきます。

f:id:hiyokosabrey:20210620143643p:plain


まず Set Timer by Event の Time につながっているのを、 RemainTime に替えます。

f:id:hiyokosabrey:20210620143823p:plain


つぎは Start at Time です。

 

変数 RemainTime には 残りの秒数が値として持っています。wb_Timer では PlayAnimation ノードでアニメーションを管理するのですが、再開するために時間指定ではなく進捗率を指定することになります。進捗率はアニメーションがすべて再生したときに 1.0 になる値です。 ちょっと面倒ですが計算してから渡すことになります。

例えば、制限時間が 10秒で、たまたま 6秒が経過したときに回答ボタンを押した場合。

 

f:id:hiyokosabrey:20210613152658p:plain

残り 4.0秒 という数字を何とかして 進捗率である 0.6 に変えなくてはいけません。

そこでこの計算で進捗率を出します。

 

f:id:hiyokosabrey:20210613154041p:plain

 

ノードにすると、こうなります。

f:id:hiyokosabrey:20210620143926p:plain
 

タイマーの改造はこれで完了です。

これでコンパイルしてテストしてみましょう。

f:id:hiyokosabrey:20210613172946p:plain

f:id:hiyokosabrey:20210613173124p:plain

f:id:hiyokosabrey:20210613173137p:plain

回答ボタンを押して、結果表示から戻ってきた時に、ちゃんと続きからタイマーが動いていればバッチリ。

 

 

 

カウント表示をつくる

タイムアップの流れができたところで、新しく正解数をカウントする表示を作ります。

新しくWidgetブループリントを用意します

f:id:hiyokosabrey:20210612101123p:plain

アセット名は wb_Count としました。

 

『ノルマ』にしようかと英語のスペルを調べてみると、普段よく使ってる割にあまりいい歴史背景の言葉じゃなかったのでしばらく彷徨った結果『カウント』にしました。いろいろ使いまわしの利く単語でもあるということで。

 

見た目には『数字』と『ゲージ(アイコンタイプ)』のどちらにしようか考えてみます。

f:id:hiyokosabrey:20210612102804p:plain

 

制限時間があるし、どうしてもクイズゲームは文章を読む時間が多くなります。少しでも脳に情報処理負荷をかけないほうが喜ばれると思うので、『ゲージ』 を採用します。

『数字』 は分母がどれだけ増えてもコンパクトで場所を取らないのがメリットですが、数字の表記から「あとどれくらい」を判断する必要があるので、直感的な把握には向いていません。ゲームのテンポにゆとりがあったり、判断できる時間のあるゲーム画面で採用できます。

一方の 『ゲージ』 は全体の長さ(量)がネックになります。

あまり多くなるようだと、アイコンではなく棒ゲージにしたほうが良さそうですが、クイズゲームでそれほど多くならない想定。長くなるとしたら、おそらくラスボス戦。その場合だけノルマ制を廃止して、ボスに体力ゲージを設定、それを削った方が特別感が出て盛り上がると思います。時間いっぱいになると大技喰らってGAME OVER。

そういった、シチュエーションの変化で、UIの構成が変わることは日常茶飯事なので、いろんなアイデアを持っておくといいことありますよ。

 

アイコンタイプのゲージを採用するということで画像を2つ用意しました。

「あるとき」左と「ないとき」右。

f:id:hiyokosabrey:20210612112020p:plain

f:id:hiyokosabrey:20210612114806p:plaintex_circle_0.png

f:id:hiyokosabrey:20210612114901p:plaintex_circle_1.png

 

ブログの下敷きと同じ白なので見えないけどいます。

右クリックで保存してお使いください。

 

画像を2つUE4にインポートしたら、キャンバスに配置します。

ノルマの数を可変させたい場合は HorizontalBox を使ってレイアウトすると可能ですが、説明が長くなりそうなので今回は固定の数で進めます。

Image を4つ等間隔に並べて、「ないとき」のテクスチャをセットします。

f:id:hiyokosabrey:20210612115418p:plain

テクスチャが 64x64px スキマは 16pxなので、80px ずつ並んでいます。

 

「COUNT」 は Text、下線 は Image で作りました。この2つは装飾です。

 

 

カウントアップ演出

ここにエフェクトキャラを追加します。

もう一つ Image を追加します。テクスチャは「あるとき」です。

f:id:hiyokosabrey:20210614000738p:plain

通常は非表示なのと、ブループリントで位置を変更するので、場所はどこでもよいのですが、レイアウトするときに余計なサイズ計算されて配置しにくいので、左上に重ねて置きました。

通常で非表示にするには、DetailsタブにあるBehabior(ビヘイビア)のVisibility(ビジビリティ)を Collapse にしておきます。

f:id:hiyokosabrey:20210614001900p:plain

Collapse はゲームを再生中に存在しないものとして扱われるので、処理負荷軽減になります。一方同じ非表示設定の Hidden は見た目に見えないだけなので、Collapse よりは負荷が高くなります。

このエフェクトはたまにしか出ないうえに、他のパーツのレイアウトに干渉しないので、Collapseを選択しました。

 

そしてアニメーションを付けます。名前は NEWGET としました。

f:id:hiyokosabrey:20210614001335p:plainVisibility と、 Transform の Scale 、 Render Opacity の3つ。

f:id:hiyokosabrey:20210614001737g:plain

 

ヒエラルキーはこうなってます。

f:id:hiyokosabrey:20210614002753p:plain

ちょっと気づきにくいですが、飾りの下線とCOUNTの文字は、Is Variable を無効にしているので、[ ] がつけられています。

 

これでキャンバスでのレイアウト作業は完了です。

 

編集モードをGraph に変えます。

 

 

ふるまいを作る

まず Image配列 にします。

Variablesのリストに並んでいる Image を 4つ グラフにドロップします。

f:id:hiyokosabrey:20210615001708p:plain

順番は左から順番に扱うので配列にする際にタテに並べます。

 

キャンバスにはこう並んでました。

f:id:hiyokosabrey:20210615000147p:plain

 

前もって、 変数タイプがImage の配列変数を作ってもOKですが、変数の型がよくわからないうちは、逆引きみたいな作り方がオススメ。

f:id:hiyokosabrey:20210615000301g:plain

名前を変更して Event Pre Construct につなぎます。
Images と命名

f:id:hiyokosabrey:20210615001613p:plain


配列  というのは、同じタイプの Variable(変数)がたくさん存在していて、いちいち名前を付けて管理するのが面倒なときに、配列という形態にします。上のMake Array ノードのピンに書かれているように [0] [1] [2] [3]  と番号をつけて管理するのです。この番号のことを Index番号とか 添字(そえじ)と呼びます。

身近な例で、コインロッカー がイメージしやすいと思います。

コインロッカーは入りさえすればなんでも入れられますが、配列変数の場合は、型(タイプ)が1種類固定なので、どちらかといえば下駄箱のほうがイメージが近いかも。

 

 

ここで関数を用意します。

この関数は指定したカウント分の画像を「ないとき」から「あるとき」に切り替えます。

エディタ左の My Blueprintタブにある Functionsリスト の +Function ボタンをクリックします。

f:id:hiyokosabrey:20210615235406p:plain

名前は SetCount にしました。
で、最終的にこうなります。

f:id:hiyokosabrey:20210615235618p:plain

 

順番にいきますね。

まずは For Loop ノード。とてもメジャーなのでよく出会います。

f:id:hiyokosabrey:20210615235831p:plain

指定した回数だけ繰り返すノードです。

 

ループについて

ノード右にある  Loop Body から先の処理 をぶん回します。

回数分回すと  Complete に移ります。

ループ処理が終わった後の処理は、 Complete 以降につないでいきます。

Loop Body の先には、できるかぎり単純で軽くてレスポンスのいい仕事をさせるようにします。

特にアニメーションやDelayなど時間を扱うような処理はForLoopには向いていません。コンパイルは通るけど、玉突きが起こって引きつったり警告がでたりします。

回す回数は、 First IndexLast Index という2つのパラメータで指示します。

First Index がループ開始の番号。

陸上のトラックをIndex番号がゼッケンに書かれたランナーがリレーで走るところをイメージしてみてください。

First Index が0 つまり最初のランナーのゼッケン が 0番ということです。

0番のランナーが無事走り終えると、ゴールに待ち構えていた Last Index と比較します。0番とは違うので、次は ゼッケン1番のランナーが走ります。帰ってきたら、またLast Index と比較。違うので次2番。という風に順番にカウントを増やしながら次々走っていきます。

ある時 Last Index と同じ ゼッケン番号のランナーが帰ってきました。そのタイミングでようやく For Loop は仕事を終えることができます。

 

今回は ノルマの表示が  4つ なので、計 4回 まわします。

なので、 First Index は 0(初期値) Last Index は 3 になります。

  

    f:id:hiyokosabrey:20210616003653p:plain

 

First Index は 0以外の値ももちろんOKです。

First Index を 1 にすると、Last Index はまんま 回数と同じ値になります。

なぜ 0 始まりなのか?それは 配列の Index が 0 始まりで、ForLoopと相性がいいからです。他にも 0始まりが便利な場面がたくさんあります。

 

 

次は、配列の中身を取り出す Get ノード。

配列は Index番号を指定するので、専用の Get ノードが用意されています。

まず Variablesリスト にある Images を グラフにドロップします。 Get タイプで。

f:id:hiyokosabrey:20210616005413g:plain

For Loopノード右(出力側)にある Index ピンとつなぎます。

f:id:hiyokosabrey:20210616004958p:plain

これで配列の中身 Index 0~3 を順番に取り出すことができます。

 

取り出したものをどうするか?

画像を差し替えるためのノード、 Set Brush from Texture ノードをつなぎます。

f:id:hiyokosabrey:20210616010014p:plain

Image に直接テクスチャを渡してくれるノードです。

次にこの左にある Texture ピンからドラッグして Select ノードを取り出します。

f:id:hiyokosabrey:20210616234550p:plain

Select ノードはつなぐ対象によって見た目が変化します。慣れないうちはつないでいいのか戸惑うと思うので、不安を感じる場合、この方法だとひと手間減るのでオススメです。

f:id:hiyokosabrey:20210616235059p:plain

Option ~ のピンのところに テクスチャを セットします。

セットする方法は 3つあります。

  1. Select Asset のプルダウンから探す
  2. コンテンツブラウザで選んでおいてから白い矢印ボタンをクリック
  3. コンテンツブラウザからドラッグして直接ドロップ

 

f:id:hiyokosabrey:20210617000115p:plain

Selectノードは条件によってReturnValue から出されるデータを変えることができます。

その条件とは、

指示されたカウント数とForLoop の Index番号と比較した結果です。

というわけで 指示してもらうために、この関数に 値を受け取るための Inputs を一つ追加します。

変数のタイプ(型)は Integer(整数)、名前は Count としました。

f:id:hiyokosabrey:20210617001739p:plain

 

あとは 条件式 をつなげば完成。

f:id:hiyokosabrey:20210617002642g:plain

Count を いきなり マイナス 1 しているのは、個数を 配列に合わせるためです。

f:id:hiyokosabrey:20210615232803p:plain

 

<= は 「小なりイコール」です。日本では「 ≦ 」で習いますね。プログラミングでは使わないのでキータイプする際は半角の < と = を並べて書きます。

 

条件の評価について

どうなっているかというと、例えば カウントが 2個 の時。

今回 ForLoopでは 4回 処理します。毎回 Index番号と比較するのです。

 

f:id:hiyokosabrey:20210617010121p:plain

 

実は Selectノードと小なりイコールのノードをつないだ瞬間、Selectノードは変化していたのです。

f:id:hiyokosabrey:20210617010740p:plain

 

ちなみに0個の指示が来たときはこうなります。

f:id:hiyokosabrey:20210617011355p:plain

4個だと すべて True になります。

Selectノードは判定結果にもとづいて渡す内容を振り分けてくれるので結構便利なヤツなのです。

 

これでこの関数は完成です。

この関数さえあれば、必要な機能は揃ったことにしてもいいのですが、あとひとつ

アニメーションの再生が残っていました。

 

 

イベント グラフに戻ります。

 

呼び出されたときのために

Integer(整数)型の変数を一つ用意します。

f:id:hiyokosabrey:20210615232107p:plain

名前をCount にしました。

この変数は、表示してほしい数を受け取って保持するために使います。

 

新しくカスタムイベントを用意してそこにつなぎます。

カスタムイベントは NewGetWithEffectと命名

f:id:hiyokosabrey:20210617230337p:plain

 

そろそろ基本的な操作は慣れてきたころだと思うので、少しずつテンポアップします。

 

つぎにやるのは、エフェクトを新しくゲットするカウントのところに置きなおす処理。

f:id:hiyokosabrey:20210617231108p:plain

UMGのキャンバスに配置したものは、Position というパラメータで管理しています。

f:id:hiyokosabrey:20210617231322p:plain

なので、ここを書き換えるときは、Slot as Canvas Slot というノードを間に挟むのが基本になります。

この場合、いきなり Slot as ~ ノードを探すより、

Image Effect をドロップしてから、Slot as ~ をドラッグで取り出して、そこから Set Position です。

 

次に 指定する座標(In Position)が 0, 0 のままだといけないので、キャンバスに並べたImage から取得します。

Set Position ノードがあるということは 、Get Position もあるということになります。

f:id:hiyokosabrey:20210617232208p:plain

 ここでもマイナス1して配列から取り出します。

f:id:hiyokosabrey:20210615232803p:plain

実は0個の指定でこのイベントが呼ばれると、Index に -1 という番号は存在しないので、エラーになります。

f:id:hiyokosabrey:20210617234005p:plain

4個までしか置いていないので、当然 5以上の指定でも Index番号[4]以降が存在しないのでエラーになります。

そもそも新しく追加するのに 0個の指定はおかしいし、その場合指定する側が修正すれればいいのです。5個以上については、今後の拡張性を決めてからでも問題ないでしょう。今回はひとまず4個獲得したらゲームクリアにするので、5以上の値がくることもないはずです。そのクリアの判定はこのWidgetで行うことでもありません。

 

ちなみに

先の関数で -1 を許容していたのは、Index番号として使用しないからです。ForLoopから出てきたIndex値は 0~3 で、配列の範囲を越えることがないためエラーはでません。

 

ここまでで、指定した個数のところにエフェクトを置くことができました。

ようやくPlay Animation です。

f:id:hiyokosabrey:20210618002315p:plain

Get End Time ノードは アニメーションの終了時間を教えてくれるノードです。

なので、 Set Timer by Event に渡すと、アニメーションの再生が終わったタイミングで任意のイベントを呼び出すことができます。

Play Animationノードは再生を開始するだけして処理は次のノードに移ります。

ほぼタイムラグを意識することなくタイマーを発動させることができます。

UIではアニメーションが終わったら○○して、それが終わったら今度は◇◇で、という風に演出の終わりを待つことがよくあります。上記のはそういう場合の対策のうちの一つです。個人的にメンテナンスがしやすいと思うのでよく使います。

 

あと少し

Set Timer by Event にカスタムイベントをつないでいきます。

仕上げはこのようになります。

f:id:hiyokosabrey:20210618005848p:plain

 演出のアニメーションが終了しているので次回のために、エフェクトは非表示(Collapse)にします。タイムラインでVisibility を 表示(Visible)にしているので、流れ的にちゃんと交互になります。

初期状態: Collapse

この 表示・非表示のタイミング制御が結構表示系バグのなかでも特にメジャーなやつという印象があります。Visibility の状態は タイムラインでしかやらない、ブループリントだけで制御する、という縛りを作ったほうがいい場合もあります。

最後に、先に用意しておいた関数 SetCount をつなぎます。

 

これで準備完了。

説明が長くなりましたが、イベントグラフの全貌はこれだけです。

f:id:hiyokosabrey:20210619112310p:plain

 Event Construct と Event Tick は使いません。

 

この wb_Count の仕様をまとめると、

  • 表示したい個数を指示された → SetCount 関数を呼び出してもらう
  • カウントアップしたい → NewGetWithEffect イベントを呼び出してもらう
  • 個数は 0~4 で指定してもらう

 

コンパイルして保存したら閉じて大丈夫。

 

あとはこのカウントを表示したい場所に仕込むだけですが、さすがに長くなったので、前後編に分けることにしました。

今回はこの辺までにします。

 

次回 クリアへの道 後編 で組み込んでいきます。

そんなに間があかないように頑張りますので、お付き合いくださる方はもうしばしお待ちいただけると嬉しいです。

 

記事内容について、わからないところや、ツッコミ等あれば当ブログのコメント機能か、Twitterの方からメッセージいただけると対応します。お気軽にどうぞ~

 

ではでは

ステキなカウンター表示ライフを!

 

 

 

 

 

 

 

 

 

 

 

 

そうだ、UE4でQUIZゲームを作ろう《おまけ編》時間制限をつける

 クイズゲームには時間制限は当たり前ということで、作ってみたいと思います。

表現方法によっていろんな作り方があるので、どういう形にしようか迷ってしまいました。

 ただ一口にタイマーといっても、目立ち具合というか、主張する強度みたいなのがあって、レースゲームみたいなタイムアタック系のゲームと脱出劇や爆弾解除みたいな時限系のシチュエーションとは与える印象は変えた方がいいですよね。この辺またどこかで記事が書けたら書こうと思います。

 

 チュートリアルって最適解なものを無駄なく教える場というのが求められているんだろうなとは感じつつ、ゲームUIのように解がたくさんあるとチョイスがむずかしいのですよ、と一応言い訳を。

 

 シンプルにUMG使ったやつと、マテリアルを使ったやつ、どちらにしようかちょいとばかし悩んで、UMGだけで作るのを紹介しようと思います。

 見た目はゲージタイプですが、プログレスバーは使用しません。アニメーションを使いつつ、あとから調整できるタイマーにします。

 

仕様としては、

  • 一定の秒数をゲージで表現
  • 時間の長さを調整可能
  • 徐々に短くなることで焦らせる
  • 0になるとタイムオーバー

 まだノルマの仕様が入ってませんが、タイマーができたら実装してもいいかな。いずれ問題もたくさん仕込んでランダムチョイスもやりたいので、今のうちから問題を集めておくとしましょう。

 

 目次

 

 

専用のWidget

新しくタイマー用のWidgetブループリントを用意します。

f:id:hiyokosabrey:20210605104803p:plain

wb_Timer と命名

 

さっそくキャンバスに Image2つ 配置します。

f:id:hiyokosabrey:20210605110650p:plain

ひとつは下敷き、ひとつは伸び縮みするゲージ本体です。

それぞれ、 Image_Gauge、 Image_base と命名

 

いい感じにカラーをつけて重ねます。

下図は Text を配置していますが飾りなのでお好みでアレンジしてみてください。

f:id:hiyokosabrey:20210605110940p:plain

f:id:hiyokosabrey:20210605110953p:plain

 

テキストの代わりに画像が用意できるなら、ストップウォッチのアイコンとかが合いそうです。

ゲージ本体と下敷きの重ね方次第で立体的に見せることもできます。

f:id:hiyokosabrey:20210605112737p:plain

 

 

キャンバスにはこのようにレイアウトしました。

f:id:hiyokosabrey:20210605113127p:plain

参考までに、

ゲージ本体の長さ(Size X)は 1200  、高さ(Size Y)は 8

ゲージ下敷きの長さは 1200  、高さは 16

 

一度ここでコンパイルして保存します。

 

クイズのメイン画面担当のWidgetに配置してみましょう。

f:id:hiyokosabrey:20210605114427p:plain

 

編集モードをDesignerにして、新しく作った wb_Timer を 探してドロップします。

f:id:hiyokosabrey:20210605114817p:plain

サイズ感や色味を確認して気になるようだったら戻って調整します。

 

OKなら、そのままコンパイルして保存して、wb_Timer に戻ります。

f:id:hiyokosabrey:20210605161039p:plain

 

 

ゲージが減るアニメーション

ゲージ本体の Image に 長さを変えるアニメーションを付けます。

 

ゲージの長さを変えるには、Detailsタブの下のほうにある、Render Transform の Scale X を変化させます。

f:id:hiyokosabrey:20210605161849p:plain


ただ、初期状態で Pivot X の 値が 0.5 なので、ゲージの真ん中が中心になっています。

f:id:hiyokosabrey:20210605161601p:plain

 

そこで、Pivot X の値を 0.0 に変えます。

f:id:hiyokosabrey:20210605161905p:plain

Pivot はそのパーツを変形させる基準となる点です。

f:id:hiyokosabrey:20210605162335p:plain

同じくDetailsタブの Slot(Canvas Panel Slot)にある Alignment(アライメント) と同じ認識で問題ないと思います。

 

Alignment はレイアウトのために使用するパラメータで、Pivot は変形のためのパラメータという扱いです。

 

パーツの左上が (0, 0)右下が (1, 1)になるので、初期値の(0.5, 0.5)というのは、ド真ん中になります。

f:id:hiyokosabrey:20210605163152p:plain

 

この状態でScaleのアニメーションを付けます。

水平方向だけなので、 Scale X に 1.0 → 0.0 の変化をつけます。 尺 は 1 秒

f:id:hiyokosabrey:20210605164025p:plain

この ボタンをクリックしてCurveエディタを開くと確認できるのですが

f:id:hiyokosabrey:20210605164750p:plain

UE4は初期状態でEase In-Out が設定されています。

f:id:hiyokosabrey:20210605164639p:plain

f:id:hiyokosabrey:20210606161826g:plain

タイマー表示に緩急は不要なので、リニアに変えます。

このカーブエディタからも操作できますが、普段のTrack編集中でも変更できます。

Keyを打ったポイントを選択して、右クリックメニューから Linear を選びます。

f:id:hiyokosabrey:20210605165423g:plain

Curveエディタの場合は、Keyを打ったポイントを選択して、右上にある、アイコンボタンからリニアにできます。

f:id:hiyokosabrey:20210605173306p:plain

 

f:id:hiyokosabrey:20210606161536g:plain

 

1秒のアニメーションなんて一瞬です。

どんだけ早押しゲーだよ!無理ゲーだよ! という声が聞こえてきそうですが、これにはブループリントで答えることにしましょう。

 

おっと、その前にひとつやることがありました。

ゲージ本体の初期状態を確認します。

アニメーションを選択していない状態で、

f:id:hiyokosabrey:20210606113852p:plain

ゲージ本体の Scale X を 0.0 にします。

f:id:hiyokosabrey:20210606114028p:plain

見た目にサイズが 0 になって消失します。

f:id:hiyokosabrey:20210606114229p:plain

これは、アニメーションを再生しきった状態と同じにしておくためです。

UMGのアニメーションは再生終了すると、自動でデフォルト状態に戻ります。

その時、デフォルト状態が満タンな見た目だと、せっかく減らしたゲージが回復したように見えてしまうのです。

 

では気を取りなおして 編集モードをGraph に切り替えます。

まず、何もないところで右クリックしてカスタムイベントを取り出します。

f:id:hiyokosabrey:20210605181047p:plain

Add Event の中にあります。上の方にあるので検索しなくてもすぐ見つかるはず。

イベント名は、 startTimer と名付けました。

f:id:hiyokosabrey:20210605181303p:plain

このイベントで アニメーションを再生させます。

f:id:hiyokosabrey:20210606150942p:plain

 


ちょっとひと工夫

再生するだけならここまでなんですが、ひと工夫加えます。

 

カスタムイベントにパラメータ(引数)のピンを追加します。

Detailsタブから Inputs で + ボタンを押す以外にグラフ内でドラッグする方法を紹介していおきます。

f:id:hiyokosabrey:20210605182041g:plain

型がわかっていなくても正確にピンが追加できるので、結構便利です。

間に計算ノードを追加するので、つないでから切断してます。

 

ピンの名前を Seconds (秒)に変えます。

f:id:hiyokosabrey:20210605194913p:plain

 

カスタムイベントからは、秒数を受け取る想定。

PlaybackSpeed は 1.0 (初期値)が 通常のスピード。

2.0 で 2倍速。 0.5 で1/2の速度でスロー再生になります。

ということで、割り算のノードを追加します。

 

割り算は / (半角スラッシュ)で検索できます。

f:id:hiyokosabrey:20210605195949g:plain

カスタムイベントのパラメータと、PlaybackSpeed が float型(浮動小数)なので、  float / float を選択する必要があります。

 

秒数をプレイバックスピードに変換するには、 1.0 を秒数で割ります。

f:id:hiyokosabrey:20210605200856p:plain

 

 

f:id:hiyokosabrey:20210606150845p:plain

用意したアニメーションの尺は 1秒。

例えば5秒の再生速度にしたい場合  1÷5 ということになって、 PlaybackSpeed は 0.2 です。

本来のスピードの1/5の速度ということで、5倍の時間をかけるとようやく1秒の長さを再生したことになります。まぁゆっくり再生します。

割り算というのはちょっとイメージしにくいですよね。

 

この仕様にしておくとメリットがあります。

  • あとからタイマーの長さを調整しやすい
  • ゲーム中にアイテム使用やデバフ、ボス戦等、特別な状況で変更が可能
  • アニメーションのトラックを再編集しなくていい
  • タイマーの終了タイミングを管理しやすい

最初から最後まで制限時間が変化しないのであれば、決め打ちでタイムラインにキーを打っても大丈夫ですが、親のWidgetとの連携が少しだけ複雑になるので、今回はシンプルに作ります。

 

あと必要そうなイベントを2つ用意します。

f:id:hiyokosabrey:20210606150829p:plain


readyTimer

アニメーションがスタートしないと満タンにならないので、一時的に満タン状態にするカスタムイベントです。今はクイズの設問をいきなり表示していますが、「第1問」とか演出が増えるかもしれないので、一応用意しておいてもいいかな~という程度です。

 

stopTimer

何かしらのポーズがかかったり、タイムボーナスの計算したりするときに必要になりそう、という程度です。

 

残りをつなぐとこうなります

f:id:hiyokosabrey:20210606150536p:plain
Image_Gauge はキャンバスに配置した ゲージ本体 の Image です。

Scale に 1.0 を入れると、本来のサイズで表示してくれます。

 

アニメーションを停止するのは Pause Animeation ノードです。

Stop Animation ノードが用意されているのですが、こちらは停止したあと、状態をアニメーションする前の初期状態にリセットしてしまうので、今回の場合ゲージが消えてしまうことになります。時間が止まった感を出すためにポーズさせることにしました。

 

ちなみに

イベント名を ”stopTimer” にしているのはノード名とちぐはぐな感じですが、イメージとの距離感で、”stop” という言葉を採用しました。イベント名を ”pauseTimer” にすると再開を期待したりしそうだし、クイズゲームというのもあって、ポーズ状態を見せることもないのが理由だったりします。まぁ後々なるべく誤解が起こりにくいのがいいなぁという程度の考えです。こういうイベントや関数は自身で呼ぶより、他の場所から呼び出して使うことが多いですし。

 

タイマーはこれで完成です。

コンパイルして問題なければ保存します。

 

 

実装

早く減らすところを見てみたいので、wb_Main を編集していきます。

f:id:hiyokosabrey:20210605114427p:plain

 

カスタムイベントを追加します。

f:id:hiyokosabrey:20210606150439p:plain

wb_Timer が持っているカスタムイベント startTimer を呼び出しています。

制限時間は10 秒ということでパラメータに 10 を入力しています。

 

関数ノードやイベントノードを取り出したい場合、先にVariablesリストにいる wb_Timer を Getタイプで取り出して、ドラッグして検索すると見つけやすいです。wb_Timer は、キャンバスに置くと同時に、Variables リストに追加されます。

f:id:hiyokosabrey:20210606153113g:plain

エンジンの言語環境が English の場合は、ざっくりと "call" で検索できて便利です。

 

これでコンパイルして問題なければ保存します。

 

 

次にタイマーをスタートさせるために、ゲーム本体の wb_QuizGame を編集します。

f:id:hiyokosabrey:20210606154401p:plain

 

タイマーをスタートさせるタイミングは 2か所。

f:id:hiyokosabrey:20210620115508p:plain


メイン画面に切り替わるタイミング。

 

まずは 最初のタイトル画面でSTARTボタンを押したとき。

 

以下の画像はこちらの記事を適用しています。

そうだ、UE4でQUIZゲームを作ろう《おまけ編》いーなむで切り替えをわかりやすく - みつまめ杏仁

f:id:hiyokosabrey:20210606155142p:plain

 

もうひとつは、回答ボタンを押して、正解か不正解かの画面から帰ってきたとき。

f:id:hiyokosabrey:20210606155410p:plain

 

ひとまずこれで確認できるはず。

コンパイルして再生してみましょう。

f:id:hiyokosabrey:20210606162157p:plain

 

無事動いたのを確認したら、タイムアップ画面を追加します。

 

 

タイムアップ画面

ちょっとラクするために正解演出画面の wb_Right を複製することにします。

f:id:hiyokosabrey:20210606163415p:plain

 

テキストを 『TIME UP』に変更して、アニメーションもちょっといじってみる。

f:id:hiyokosabrey:20210606165327g:plain

これで編集終了。

コンパイルして保存します。

 

 

これを WidgetSwitcher に追加します。

クイズゲーム本体のWidgetを編集します。

f:id:hiyokosabrey:20210606201148p:plain

 

Paletteタブの UserCreated から wb_TimeUp を探します。

f:id:hiyokosabrey:20210606191555p:plain

f:id:hiyokosabrey:20210606192103p:plain

 

いーなむにも登録します。

f:id:hiyokosabrey:20210526004846p:plain

f:id:hiyokosabrey:20210606192330p:plain


これで切り替える準備完了です。

 

今 切り替えのつながりのフローイメージはこうなりました。

f:id:hiyokosabrey:20210606200132p:plain

 

まだこの状態では切り替わりません。

 

切り替えを行うのは → wb_QuizGame

タイマーをコントロールしているのは → wb_Main

 

ということで、wb_Main がタイムアップとした時に、wb_QuizGame に通知してやる必要があります。すでに wb_Main は 4つの回答ボタンをクリックしたとき wb_QuizGame に通知しています。このイベントディスパッチャーに相乗りできると、新たにイベントディスパッチャーとバインドのセットを新設しなくても済みそうです。時間切れも無回答というひとつの答えですし。

 

切り替えるためには

ということで、今度は wb_Main を編集します。

f:id:hiyokosabrey:20210606201310p:plain

回答ボタン用のイベントディスパッチャーを選択して

f:id:hiyokosabrey:20210620115635p:plain


Detailsタブから、 Inputsのパラメータを追加します。

変数のタイプは Boolean。

f:id:hiyokosabrey:20210606201412p:plain

パラメータ名を TimeUP にしました。

パラメータ名を変更してコンパイルすると、エラーが出てしまいます。

かといって New Paramのままだと後で意味が分からなくなるので、ここでエラーをつぶしておきます。

f:id:hiyokosabrey:20210606201859p:plain

 

f:id:hiyokosabrey:20210620120232p:plain

グラフを見てみると、置いたやつ全部エラーになってますね。

f:id:hiyokosabrey:20210620121710p:plain



よくみると、追加したパラメータの名前が New Param のままです。更新が中途半端に行われた感じに見えます。仕方ないので、ノードをリフレッシュしてやります。

ERROR! になっているノードの上で右クリックして Refresh Nodes を選択。

f:id:hiyokosabrey:20210620122845g:plain


f:id:hiyokosabrey:20210620122031p:plain


これでコンパイルエラーは消えます。

 

あとはタイムアップのタイミングでこのイベントディスパッチャーを Call すればよさそうです。

QuizStartのカスタムイベントのところで、タイマーを開始しているので、ここに仕込むことにします。

 

Set Timer by Event ノードを使います。

f:id:hiyokosabrey:20210607220707p:plain

これは 指定した時間が経つと、カスタムイベント を呼び出してくれるノードです。

カスタムイベント は バインドしたときと同じようにつなぎます。

f:id:hiyokosabrey:20210607221300p:plain

ここにイベントディスパッチャーをつなぎます。

f:id:hiyokosabrey:20210620141904p:plain

TimeUp にチェックをつけておきます。

 

いったんコンパイルして保存したら、

クイズゲーム本体でタイムアップ画面に切り替える処理を作ります。

 

f:id:hiyokosabrey:20210606201148p:plain

 

回答ボタンを受け取るバインド処理のところに変化が!

f:id:hiyokosabrey:20210620141804p:plain

 

ここの ブランチノードの前にタイムアップの分岐を挿入します。

f:id:hiyokosabrey:20210620141657p:plain

タイムアップ優先で判定します。

正解、不正解で使ってる 画面切り替え用の関数を使います。いーなむを登録しているので、TimeUpを選ぶだけ。簡単!

カスタムイベントの赤いラインのクロスが気になる場合は wb_Main のイベントディスパッチャーの方で順番を変えてやると解消できます。

f:id:hiyokosabrey:20210607224130p:plain

コンパイルして保存すると反映されます。

f:id:hiyokosabrey:20210607224300p:plain

ここで再生して確認してみましょうか。

 

f:id:hiyokosabrey:20210607233944p:plain

無事切り替わりました。

 

かんせー! と叫びたいところですが、バグを発見。

普通に時間切れだと問題ないのですが、回答してNextボタンを押さずに放置していると、きっちり時間が来てタイムアップ画面に切り替わります。

真面目過ぎ!と突っ込みたくなりますが、タイマーを使っているのが原因なので対策を講じます。

 

 

 

f:id:hiyokosabrey:20210606201310p:plain

 

Set Timer by Event ノードにある Return Value ピンからドラッグして、変数を作ります。Promote to Variable は日本語環境だと 「変数へ昇格」となります。

f:id:hiyokosabrey:20210620123707g:plain

 

この変数を TimerHandler と命名。制御するので ハンドラー です。

f:id:hiyokosabrey:20210607235159p:plain

こうしておくと Set Timer by Event ノードが見えないところでやってるタイムカウントに介入することができます。

いろいろ制御できるのですが、今回やりたいのは、タイマーのキャンセル。

 

ノードだと Clear and Invalidate Timer by Event というノードになります。

f:id:hiyokosabrey:20210608000352p:plain

この辺のワードは WebでJavaScript を経験された方ならなじみ深いかもしれません。

f:id:hiyokosabrey:20210608000412p:plain

 

これを、回答ボタンイベントのところにつなぎます。

f:id:hiyokosabrey:20210620123319p:plain


タイマーが動いている間に、回答ボタンを押すとタイマーをなかったことにします。

これで完成!

コンパイルして保存したら再生してみましょう。

 

youtu.be

 

最後にちょっとだけ

秒数の指定を変数にしておきます。

f:id:hiyokosabrey:20210620141513g:plain

調整しやすくするためと、うっかり防止と、タイムボーナスなんかの対応に使用します。

この方法で変数を作ると、Default Value に自動的に値が入れられます。(※コンパイル後に確認可能)

f:id:hiyokosabrey:20210608002311p:plain

この値を変更してコンパイルすると、タイマーの時間を自由に変更できます。

 

 

おつかれさまでした!タイマーの実装完了です。

ボタンを押して遷移するのに比べて難易度が上がった感じがしますね。

 

いろんなタイマーを紹介してみたいのですが、先に進まなくなりそうなのでここまでにします。

リクエスト頂ければどこかでぶっこみましょう。

 

わかりにくいとか質問やツッコミなどあれば、このブログのコメント機能かTwitterにてお待ちしています。

 

ではでは

今回はこの辺で

ステキなタイマーライフを!

そうだ、UE4でQUIZゲームを作ろう《おまけ編》ボタンのデザインをどうにかする

 UE5の早期アクセスが公開されましたね。まだ触れていないので祝祭のツイートを眺めてるとマウスを操作する中指がつりそうです。UIがスッキリしてモダンな雰囲気を醸しているのがなんだかさみしくもあります。

 UE4のアイコンはそれなりに丁寧で考え抜かれてる感があって好きです。似たような機能を差別化するには、ある程度情報量が必要で、UE5の開発者がどんな解決を見せてくれるかとても興味があります。

 パネルの開閉に期待です。シングルモニターでの作業が捗りそうな予感。

 エディタとしての操作方法が変わっても、UE4での経験値はそのまま引き継げそうな話もチラホラあるので、安心してUE4を触っていこうと思います。

 

 

単純な構造ではあるけどひとまず動くものができたので、見た目に手を入れていこうと思います。

f:id:hiyokosabrey:20210529105931p:plain

UMGに馴れるのを主軸にして書いていこうと思っているので、まずはデフォルトのボタンデザインから作りかけ感がにじみ出るのを抑えることにします。テクスチャを作れる環境が必要となります。PNGをブログに貼っていますので、右クリック保存ができれば利用してみてください。テクスチャのインポート操作についても説明しています。

 

 

今回の内容は、以下の2つの記事の内容を作り終えた後のおまけ編となっています。

limesode.hatenablog.com

limesode.hatenablog.com

 

 目次

 

 

Buttonの見た目を変える方法

UMGのボタンコンポーネントである  Button の見た目を変える方法は、デザインを変更したい Button を選んでDetailsタブから各種パラメータをいじっていくのが基本の操作になります。

公式ドキュメントにさらっと書かれています。→ スタイリング

f:id:hiyokosabrey:20210529113412p:plain

 

 Appearance(アピアランス)の項目を編集します。

f:id:hiyokosabrey:20210529110250p:plain

Normal ・・・ 通常時の見た目

Hovered ・・・ マウスカーソルが重なった時の見た目

Pressed ・・・ クリックした時の見た目

Disabled ・・・ 使用不可の時の見た目

 

それぞれ中を開けると

f:id:hiyokosabrey:20210529113149p:plain

Image ・・・ テクスチャをセット

Draw As ・・・ 描画する方法  None / Box / Border / Image の4種

Margin ・・・ Draw As が Box または Border の時に設定可能

Tiling ・・・ Draw As が Image の時に設定可能

 

Draw As の Box と Border は 古いところで Flashでは Scale9Grid 、Unityだと 9Slice と同じものです。四隅を引き延ばすので小さくミニマムなテクスチャで高解像度な見栄えが出来上がります。

Border は 9分割した中央部が描画されないので、描画負荷を下げたいときに指定します。

ボタンひとつにつき、Normal、Hoverd、Pressed、Disabled(※必要に応じて) 毎に、テクスチャセット、Margin再設定 なので、結構手間を必要とします。

f:id:hiyokosabrey:20210529204037p:plain

セットするだけで、このふるまいを得られるので安いかどうかは検討案件です。

f:id:hiyokosabrey:20210529121649g:plain

 

Disabled は無い想定で3種類のテクスチャを用意しました。

サイズは 64x64ピクセル

f:id:hiyokosabrey:20210529122119p:plain   tex_MoveBtnBase_Normal.png

f:id:hiyokosabrey:20210529122129p:plain  tex_MoveBtnBase_Hoverd.png

f:id:hiyokosabrey:20210529122139p:plain  tex_MoveBtnBase_Pressed.png

 

 ↑ ブラウザからDLしてご利用いただけます

 

ゲーム開発には可能な限り PNG を避けておきたいと考えているのですが、3分クッキング的にブログに乗せられるフォーマットとしてPNG にしました。

いろいろ理由があって書きだすと長くなるのでやめておきます。

UE4にはちょっとだけステキな機能があるので、PNGでもリスクが減るのはうれしい限り。

 

 

テクスチャをインポート

テクスチャが用意できたら、UE4へのインポートが必要です。

 

アセットが増えてきたので、テクスチャ専用のフォルダを作ります。

 

まずは、コンテンツブラウザの何もないところで右クリックして新しいフォルダを作成します。

f:id:hiyokosabrey:20210529124357p:plain

 

f:id:hiyokosabrey:20210529123940p:plain

Widget 専用テクスチャなので、 Widgets フォルダの中がおすすめ。

 

メッシュやテクスチャなど、外部のDCC系アプリでテクスチャを作成することになりますが、UE4で扱う場合、Windowsエクスプローラでファイルをコピーしたり移動するのはできません。かならず インポート という操作を経て、UE4内で使用可能なアセットに変換する必要があります。このアセットのことを中間データと言ったりします。

 

ファイル形式をもとに説明すると PSD は完全に作業データです。

ゲーム制作においては、TGA や PNG 、DDS も 中間データ に含みますが、しいて分類にするなら、テクスチャインポート用データといったところでしょうか。

 

ゲームハードやサービスごとにネイティブなデータ形式がそれぞれ存在します。

ゲームエンジンがパッケージを作る際にターゲットになったプラットフォームに合わせて最適化してくれます。

使い勝手のいい状態を維持しつつ、用途に応じたいろんなメタデータをくっつけたり、メモリ使用量をコントロールしたりするために、インポート という操作フローが必須なのです。

PSDのような自由度の高い作業データは、思い出や不安がたくさん詰まっています。

プロジェクトフォルダの中に直接PSDを置かないのは、そういった個人的な感傷を共有の場にコミットさせないということでもあります。そんなセンシティブで巨大なPhotoshopデータはそっとしておくワークフローになっているのが素敵です。

 

 

 

続きに戻りましょう

 

ダウブルクリックしてフォルダを開いたら

また、何もないところを右クリックします。

f:id:hiyokosabrey:20210529124501p:plain

Import to ~ (~は今開いている場所=フォルダパス)を選択すると、Windows形式のファイル選択ダイアログが出てくるの、インポートしたいPNGファイルを選択します。

f:id:hiyokosabrey:20210529174743p:plain

開くと

コンテンツブラウザにアイコンが現れます。

f:id:hiyokosabrey:20210529175201p:plain

ダブルクリックするとインポート設定のダイアログが開きます。

f:id:hiyokosabrey:20210529125219p:plain

確実にチェックしておきたいのが、品質 と ミップマップ

 

まずは上から

f:id:hiyokosabrey:20210529175544p:plain

UMGで使用するため、

Compression Settings は UserInterface2D(RGBA) を選択します。

 

次は、サイズとPNG、sRGB対策

f:id:hiyokosabrey:20210529175935p:plain

Power Of Two Mode は、自動判別されるので、基本気にしなくても大丈夫ですが、2のべき乗の対応が必要になればここを変更します。強制的に2のべき乗サイズのテクスチャにできます。

 

PNGをインポートした場合に限り、Padding Color が大事になります。

Photoshopが出力するPNG形式は、完全透明の部分のカラーが白になるので、BC5などピクセル圧縮が掛かると、意図しない白いゴミピクセルがうっすら見えることがあるためです。表示されたときにフリンジのようなゴミピクセルが見えたら、このPadding Colorを変えてみてください。

 

普段のPhotoshopでの作業はカラーマネジメントを変更しない限り sRGB 環境になっているので、チェックがついている状態できちんと見えていればOK。

f:id:hiyokosabrey:20210529182654p:plain

 sRGBに応じた補正が不要なリニアなデータを作成した場合は、このチェックを外します。

 

 

次は Level Of Detail

f:id:hiyokosabrey:20210529182947p:plain

これがミップマップを生成するかどうかの設定です。

ゲームの描画は基本リアルタイムレンダリングで、カメラからの距離と、ポリゴン面の角度に合わせて、最適なテクスチャの解像度を動的に変更する仕組みがあります。その仕組みに対応するために事前にいくつかの解像度のテクスチャを作ってしまいます。

ゲーム中にリアルタイムに作るのは負荷が大きいので、インポート時に作ってしまえというわけです。内容は単純でオリジナル面積の1/4、1/16、1/64 ・・・と、何段階かの縮小テクスチャが作られ並べられます。

 

2D表示が主な UI はUMGのキャンバスに描くのでカメラは存在しません。ミップマップは不要なのです。作ったテクスチャにさらにミップマップ用のデータが追加されるとなると、データ容量がもったいないので、Mip Gen Settings は NoMipMaps を選択します。Gen はおそらく Genarate = 生成 の略記でしょうね。

64x64とか 256x256、1024x1024 みたいなべき乗の正方形テクスチャをインポートすると、自動で ミップマップを生成する設定になっているので、必ず確認しましょう。

 

ちなみに、テクスチャサイズを長方形にしたり2のべき乗でないサイズにすると、自動的に NoMipmaps にされ変更できなくなります。

 

Texture Group はテクスチャとしての用途を明示しておくことで、強制的なクオリティコントロール時に効果を発揮します。ここは UI を選択しておきましょう。

 

 ・・・

 

ひとまず大事なインポート設定は以上です。

特殊な状況じゃなければ、通常のテクスチャの場合はこれで問題は起こらないと思います。

残りの必要なテクスチャをインポートしましょう。

 

テクスチャ一枚ごとにちまちま設定するの大変という場合は 一括して行う方法もあります。

コンテンツブラウザで複数選択して、右クリック

Asset Actions > Bulk Edit via Property Matrix... が便利です。

f:id:hiyokosabrey:20210529201012p:plain

 使い方は今回は説明を省きますので、興味のある方は 公式ドキュメント かこちらをどうぞ。

unrealengine.hatenablog.com

 

 さあカスタマイズを始めようか

必要なテクスチャをインポート出来たら、

f:id:hiyokosabrey:20210529202459p:plain

Button の設定を編集していきましょう。

f:id:hiyokosabrey:20210529204037p:plain

Image にインポートしたテクスチャをセットします。検索で探してもいいし、コンテンツブラウザからの ドロップも便利。

 

Image Size はテクスチャをセットした際に自動で読み取ってくれるので基本放置。

 

Margin  は 0.25 で問題ないデザインなので、そのままにしています。

 

Disabledの下にある、~ Padding というパラメータは、クリックした際に凹む動きを実現する数値です。WEB の CSS で見かけることも多いかな。マイクロインタラクションってやつで、クリックした感が出るので地味にありがたい設定です。動いてほしくないときは、Normal Padding の値と、 Pressed Padding の値を同じにします。

SEの設定もできます。

 

 

これでようやくボタンひとつぶんの設定完了。

まじか! これを1個1個設定するの?なんかメンドクサくない?

はい。がんばれー!

 

で、ここまでは、マニュアル通りの答え。

 

とはいえ効率悪いよね?

ゲームのプロトタイプを作るとき、最初からおしゃれでいい感じのデザインで作ることはマレです。まずはアリものでアイデアが消えないうちにサクッと作りたいものです。

とはいえ、見た目でテンション上がるのは間違いないので、少しでも見映えはよくしたい。でもコストはかけたくない。

 

で、ここからは、ゲーム開発者としての答え。

後からボタンのデザインを差し替える方法を紹介します。

一時的に解決する方法と、後々のことを考えてメンテナンスできる形に変更する方法があります。

 

今判明している方法を3つご紹介。

  

 

まず、手っ取り早くコピペで!

設定の済んだ Button の Details > Appearance > Style のところで右クリックすると小さなメニューがポップアップするので、Copy を選びます。

f:id:hiyokosabrey:20210529210529p:plain


次に、設定のされていない Button で 同じように Style に 今度は Paste を選びます。

f:id:hiyokosabrey:20210529210543p:plain

これだけで移植できます。

この方法はデザインを変更するたびに、複数のアセットを開いて編集する必要があるのと、うっかりペーストし忘れが起こるかもしれないので注意が必要です。

 

 

オリジナルのButtonに差し替える!

新しくButtonアセットを作って、それと入れ替えます。

f:id:hiyokosabrey:20210529212154p:plain

そうすれば、このアセットをひとつ変更するだけですべてに反映されます。

問題なのは、入れ替える際、OnClickなどのButtonにまつわるイベントが解除されてしまうことです。

差し替えにかかる手間はこの一度きりなので、後々のことを考えてキリのいいところで一気にやってしまうのも手です。

 

作り方は、

コンテンツブラウザの何もないところで右クリックして Blueprint Class を選択

f:id:hiyokosabrey:20210529212600p:plain

 

Class 選択のダイアログがでてくるので、 button で検索

f:id:hiyokosabrey:20210529213027p:plain

ヒットした Button をハイライトしたら、Select ボタンをクリックします。

 

f:id:hiyokosabrey:20210529213253p:plain

適当に名前を付けるとアイコンが変化します。

f:id:hiyokosabrey:20210529213455p:plain

ダブルクリックして編集します。

f:id:hiyokosabrey:20210529213827p:plain

キャンバスが無く、Detailsタブも Appearance が一番上にいます。

ちょっと様子が違うようですが、スタイル設定をいじっていきます。

カスタマイズが終了したらコンパイル

保存したら閉じてもOK。

 

ではタイトル画面のWidgetを開きます。

f:id:hiyokosabrey:20210520230318p:plain

Designer モードにすると、左の Common リストのところに さっき作った Button が追加されているので、キャンバスにドロップします。

f:id:hiyokosabrey:20210529235448p:plain

じつはこれ、この後削除します。リプレイスするために一時的に必要になるためです。

 

ヒエラルキーの中で先に置いてあった、Button を選択して右クリックメニュー

Replace With... からの Replace With 作ったやつ_C を選択。

f:id:hiyokosabrey:20210530000212g:plain

入れ替わりました。

f:id:hiyokosabrey:20210530001645p:plain

 

ドロップしたやつは削除します。ついでに文字の色も見やすくなるよう変更。

f:id:hiyokosabrey:20210530001712p:plain

ここでコンパイルしてみると・・・!

f:id:hiyokosabrey:20210530001903p:plain

どうやら少々機嫌を損ねてしまった様子。

編集モードを Graphに替えます。

原因はこれでした。

f:id:hiyokosabrey:20210530002043p:plain

Button が別の Button に替わってしまったので、この OnClicked イベントが無効になってしまいました。

ここも差し替えます。

 

Variables リストにある、新しい Button を選択した状態で、

f:id:hiyokosabrey:20210530002548p:plain

Detailsタブの下のほうにある 緑色のボタンをクリック。

f:id:hiyokosabrey:20210530002702p:plain

グラフに新たな On Clickedイベントのノードが現れるので、古いのと交換します。

f:id:hiyokosabrey:20210530003153p:plain

 

f:id:hiyokosabrey:20210530003350p:plain

 

これでコンパイルできれば大丈夫。

f:id:hiyokosabrey:20210530003550p:plain

 

この方法だと、確実に差し替わるので、今後のデザイン変更にも強い構造になりますが、ブループリントもそれなりに編集することになるので、状況によっては手間取ることもあると思います。

 

デザインの差し替えを想定するなら、先に自前の Button をデフォルトの状態のままで組んでいくのはとても効率が良い気がします。

 

 

いろんなデザインを試したい!

もう一つデザインを差し替える方法があります。

ブループリントを使ったコピペ方式のようなものです。

手でコピペするのと同じで局所的な対処になるのですが、デザインの付け替えが気軽に行えます。

新しく作ったオリジナルボタンアセットをキャンバスにドロップします。

f:id:hiyokosabrey:20210530104901p:plain

すぐに 非表示にしておきます。

f:id:hiyokosabrey:20210530105003p:plain

同じ非表示でも Hidden より Collapsed の方が描画の負荷が少ないです。

この設定は、レイアウト作業中には反映されなくて、ゲームをプレイすると反映されます。

 

編集モードを Graph にします。

Variables リスト から Button をドロップ。

f:id:hiyokosabrey:20210530105925p:plain

それぞれのGetノードから、スタイルに関するノードを取り出します。

styで検索すると見つけやすいです。

 

スタイルを上書きする Set Style ノードは2つあります。

fアイコンのついた方が関数タイプで、複数の Button に対して対応可能。

青い コッペパンの形のアイコンの方は自身のパラメータ専用なので、1対1 にする場合。

今回は 関数タイプにします。

f:id:hiyokosabrey:20210530110759p:plain

 

スタイルを読み出す Get Style ノード

f:id:hiyokosabrey:20210530110750p:plain

 

取り出したノード同士をつなぎます。

f:id:hiyokosabrey:20210530111852p:plain

 

 これを Event Pre Construction につないで確認してみましょう。

f:id:hiyokosabrey:20210530112053p:plain

Designer モードにしてから コンパイルすると変化する瞬間が見れます。

f:id:hiyokosabrey:20210530112946p:plain

いくつかのデザインを用意してからキャンバスに非表示設定で並べておいて、つなぎ替えるだけで、結構手軽に見た目を変えられます。しかもスタイルだけを差し替えるので、On Clicked などのButtonイベントはそのままなのもうれしい。

デザインを検討している段階では助けになるのではないでしょうか。

 

不要なアセットが増えるのと、Get → Set の処理が負荷になるのを放置するのはプロジェクトにとって迷惑になる恐れがあるので、デザインがFIXしたら、タイミングを見てお掃除はしたほうがよさそう。

削除する際に警告が出ることがあります。

f:id:hiyokosabrey:20210530115130p:plain

誰か使ってそうだけど、ほんとに消していいの?

 

 

最近のAAAクラスのゲームは、アクセシビリティにも配慮をするのが当たり前みたいな空気になってきた感があるので、この方法を使うと、色覚特性に合わせたボタンデザインを用意して、オプション設定を見て差し替えることができます。むしろモダンな実装方法になっていくかもしれない。

 

 

適用していく

とりあえずで、この3つの方法で一番試しやすそうな方法を実践してみてください。

 

今回の用意した緑のボタンデザインは、画面遷移を伴うところに使いたいので、クイズの回答ボタンは別のデザインが好ましいです。

 

改めて

メイン画面の 回答ボタン のテクスチャも用意しました。

f:id:hiyokosabrey:20210530120812p:plaintex_BtnBase_Normal.png

f:id:hiyokosabrey:20210530120822p:plaintex_BtnBase_Hoverd.png

f:id:hiyokosabrey:20210530120831p:plaintex_BtnBase_Pressed.png

 

このデザインだと、Margin の値は 0.25 ではなく 0.5 になります。

Button のサイズも 縦方向は このテクスチャに合わせて 84 に変更したりしてます。

その辺は 見た目にボケたり、ガビったりしなければOK。

 

差し替えが面倒に感じたので、ひとまずブループリントで差し替える方法を採用してみました。SetStyle を関数タイプをにすると便利ですf:id:hiyokosabrey:20210530122321p:plain

 

 

 このようになりました

youtu.be

 

いかがでしたでしょうか。

ずいぶん 見た目に仮っぽさが減ったと思います。

このUMGの Buttonコンポーネントについては、まだ知らない仕様や賢い使い方がありそうなので、発見できれば記事にしていこうと思います。

 

個人的にこの手の『エンジンに最初から付いてるコンポーネント』系は、機能が豊富すぎて敬遠してしまいがちなんですが、そこはEpicさんなんで無駄のない仕様に仕上がっていると信じてます。いまのところは。

 

 

今回のおまけ編はここまでです

わかりにくいとことか、ツッコミなどあれば、このブログのコメント機能か、Twitterにてお待ちしています。

あとリクエストとかもあればお待ちしてます。

 

ではでは

ステキなUMGライフを!

 

 

そうだ、UE4でQUIZゲームを作ろう《おまけ編》いーなむで切り替えをわかりやすく

いーなむを使って遷移をわかりやすくしよう。します。いや、しませんか?提案させてください。ということで、先日のクイズゲームを作ろうという記事で、WidgetSwitcher を使って複数のWidgetを紙芝居的に入れ替えるというのを作りました。そのとき 0、1、2みたいに番号で管理していたので、後から忘れてしまっても大丈夫で、わかりやすくするしくみを導入したいと思います。

今回は 開発手法的なテクニックの紹介になるので、特に機能が増えたり、見た目が変わることはありません。変わるのはブループリントのノードです。関数ノードを作ります。

 

今回の内容は、以下の2つの記事の内容を作り終えた後のおまけ編となっています。

 

limesode.hatenablog.com

limesode.hatenablog.com

 

 目次

 

 

 

いーなむを作ろう

まず専用のアセットを用意します。

コンテンツブラウザの何もないところで右クリックします。

リストメニューから Blueprints > Enumeration を選択。

f:id:hiyokosabrey:20210525233127p:plain

f:id:hiyokosabrey:20210526004846p:plain

EScreenState という名前にしました。

EngineContent内を見てみると、頭に大文字の E を付けるのが慣例っぽいです。

これをダブルクリックしてエディタを開きます。

f:id:hiyokosabrey:20210526010300p:plain

右上の New ボタンをクリック。

今回は 4つの画面Widgetがあるので 4回クリック。

f:id:hiyokosabrey:20210526010427p:plain

Display Name と書かれたところに、いい感じに分かりやすくなるような名前を入力します。

WidgetSwitcher に登録した順番を元に名前を付けます

f:id:hiyokosabrey:20210526011326p:plain

 

f:id:hiyokosabrey:20210526011945p:plain

 wb_Title (タイトル画面) Title
 wb_Main(クイズ画面) Main
 wb_Right(正解演出)Right
 wb_Wrong(誤答演出)Wrong

 

後でWidgetSwitcherの順番を入れ替えることになった場合でも、入力しなおす必要はありません。右端の▲▼ボタンでここのリストも順番の入れ替えが可能です。

ここでしっかりと 1 対 1 に紐づいていれば大丈夫。

例えば、wb_Main2 が増えました~ しかも最後じゃなくてMain の次にしたいです~

という事態になっても、ヒエラルキーの順番を見ながら修正します。

 

 

いーなむを使ってみよう

QuizGame のWidgetを編集します。

f:id:hiyokosabrey:20210520233701p:plain

 

エディタを開いたら、Graphモードにします。

 f:id:hiyokosabrey:20210526213634p:plain

 

タイトル画面のボタンとバインドしている部分、ここのカスタムイベントにつながっているノードをまとめて選択状態にしたら、

f:id:hiyokosabrey:20210526213954p:plain

その上で右クリックして メニューから Collapse to Function を選択。

f:id:hiyokosabrey:20210526214123p:plain

すると、New Function ~ という1つのノードに変化しました。

f:id:hiyokosabrey:20210526215317p:plain


これは、関数化 という操作になります。

同じ仕事(タスク)を別の場所なんかで繰り返し行うときに、関数(ファンクション)としてまとめておくと、何度も同じノードをたくさん置かなくても、関数を一つ取り出してつなぐだけでスッキリします。しかも仕事の内容に名前を付けることにもなるので、あとからブループリントを見たときにも何をしてるかわかりやすくなります。

Photoshop で例えると アクション がイメージ的に近いです。

 

まずは New Function の名前を変更します。

変え方は 2通り

エディタ左 My Blueprintタブ の Functions リストから 右クリック > Rename する方法

選択して F2キーでも同じようにリスト内で変更することができます。

f:id:hiyokosabrey:20210526215737p:plain

もう一つは、この関数を編集している最中に変える方法。

 

このノードをダブルクリックします。

f:id:hiyokosabrey:20210526220404p:plain

関数の中だけのグラフに切り替わります。

f:id:hiyokosabrey:20210526220352p:plain

左端にある紫色のノードを選択して、右上の Detailsタブからリネームできます。

関数化したら、そのまま編集する流れになることが多いので、ひと手間減るかどうかぐらいの差でしかないですが、一応 2通りの方法を紹介しました。

 

ここは、WidgetSwitcher の切り替えを行っていたので、changeScreenState としましょうか。

続いて、そのまま 少し下にある、 Inputs というカテゴリの +ボタンをクリックします。

マウスオーバーすると +New Parameter というボタンに変わります。

f:id:hiyokosabrey:20210526221017p:plain

クリックすると、何やら出てきました。

f:id:hiyokosabrey:20210526221130p:plain

イベントディスパッチャーを追加したときにも、同じような操作をしました。

これはこの関数が受け取るパラメータ(引数=ひきすうとも言います)を追加する操作です。

デフォルトだと Boolean 型が出てくるのですが、すでに別のタイプをいじったことがあると、上の画像と違うのが出ることがあります。

 

この ▼ のついた プルダウンリストを開けて、用意してある いーなむ を探します。

f:id:hiyokosabrey:20210526004846p:plain

f:id:hiyokosabrey:20210526222217p:plain

これを選択します。ついでに 名前も NewScreenState に変更しました。

f:id:hiyokosabrey:20210526222404p:plain

 

グラフ上ではこうなります

f:id:hiyokosabrey:20210526222526p:plain

最終的に、この NewScreenState のピンと、 Index のピンをつなぐわけですが、方が違うので、キャスト(型変換)が必要です。

 

ところが、ドラッグしても 互換性がないと突っぱねられます。

f:id:hiyokosabrey:20210526222907p:plain

ここは自動ではやってくれないのです。

しかたがないので、手動でキャストするノードを取り出します。
toint で検索。Integer形へ という英文で書くと  to Int  が元です。

f:id:hiyokosabrey:20210526223544p:plain

変換アダプタのようなものですね。

f:id:hiyokosabrey:20210526223633p:plain

無事つながりました。

これで関数は完成です。

 

元のグラフに戻るには、タブを切り替えます。

f:id:hiyokosabrey:20210526224243p:plain

最初からあるグラフは、イベントグラフ EventGraph という名前がついてます。
ブループリントの編集に慣れてくると、いろんな関数やらマクロやらを開くことが多くなるので、編集が終わったら閉じるようにするとEventGraphに戻りやすくなります。

 

戻ってきてみると、

f:id:hiyokosabrey:20210526224857p:plain

あらまぁ 少し見ない間に~ てな見た目になります。

 

かつての様子 ↓

f:id:hiyokosabrey:20210526225742p:plain

 

いーなむ を入力のパラメータにすると、プルダウンメニューが追加されました。
ここはタイトル画面のスタートボタンが押されたら、メインの画面に遷移する という処理なので、Title を Main に変えます。

f:id:hiyokosabrey:20210526225332p:plain

 

ここで再生して問題ないかテストしてみましょうか。

 

問題なく動作すればOK。 あとは 似たような処理を見つけて入れ替えていきます。

グラフを見てみるとWidgetSwitcher の切り替えをしているところが3か所あります。

f:id:hiyokosabrey:20210620114758p:plain

ここを入れ替えていきましょう。

 

関数ノードを取り出すのは、リストからドロップしてもいいし、

f:id:hiyokosabrey:20210620114207p:plain


何もないところで右クリックして検索、でも取り出せます。

f:id:hiyokosabrey:20210526232747p:plain

 

f:id:hiyokosabrey:20210620114419p:plain

f:id:hiyokosabrey:20210526233127p:plain

 

すごくわかりやすくなったと思いますが、いかがでしょうか? 

 

いーなむは、インデックスのように数字で管理しているものに対して順番にわかりやすく名前を付けることができるのです。

今後 切り替えるものが増えた時も、いーなむアセットを編集するだけで済みます。

f:id:hiyokosabrey:20210526011945p:plain

これで 切り替えるときに、あれ?何番だっけ?という悩みが解決します。

さらに、このいーなむエディタの Description (説明)欄に書き込んだメモが、マウスオーバーで表示されるという新設設計。

f:id:hiyokosabrey:20210527001716g:plain

ぜひご活用ください

 

今回のおまけ編はここまでです

 

ここからはおまけのおまけ

 

 

逆引きで確認する方法 (おまけのおまけ)

デバッグするときの確認で便利なのが、番号を 文字 に戻す方法。

関数が呼ばれるたびに、PrintString したいと思います。

 

関数の中身に細工します。

f:id:hiyokosabrey:20210526234627p:plain

ちょっと引くぐらいキャストしまくってます。

 

WidgetSeitcher をGetタイプで取り出して、そこから

Get Active Widget Index で現在アクティブな(表示されている)番号を取り出して、

f:id:hiyokosabrey:20210526235824p:plain

いったん Byte型(バイト)にキャスト

f:id:hiyokosabrey:20210526235411p:plain

今度はバイトから いーなむ EScreenState にキャスト

f:id:hiyokosabrey:20210526235421p:plain

そこからようやく String型(ストリング=文字列)にキャストしてもいいし、

PrintString ノードにつないでも、ここは自動でキャストしてくれます。

f:id:hiyokosabrey:20210526235545p:plain

 

こういう機能追加や改造がしやすいのも関数化するメリットだったりします。

 

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

f:id:hiyokosabrey:20210527000022p:plain

 

なんか開発してるって感じになりませんか?

PrintString はいろいろ動作確認のときに大変便利なノードです。

今どうなってる? って時に役に立ちます。


 

今回は以上です

わからないところや突っ込み、リクエストなどなどあれば、このブログのコメント機能かTwitterにて

 

ではでは

ステキななUMGライフを!