みつまめ杏仁

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

HPゲージをいろいろいじってみよう ー 成長するゲージ

 先日公開した動くHPゲージをもっと触っていこうという内容です。

 

limesode.hatenablog.com

この記事で作ったゲージを元にいろんなアレンジや機能を試していきます。

 

UE4の最新バージョン 4.27 がリリースされましたが、この記事シリーズが終わるまでは 4.26 で進めます。

 

 

ゲージ全体の長さを変更できるようにする

キャラのLVアップや、アイテムゲットなどでゲージの上限が増えるとプレイに対しての報酬になりモチベーションのアップにつながります。

まず準備として、キャンバスに配置したゲージの長さを変数に入れて基本の長さとします。

キャンバスに配置した状態を取得してそのまま変数に保存します。

f:id:hiyokosabrey:20210820233859p:plain

サイズを入れておく変数は、Get Size ノードにある Return Value ピンから 右クリックして作るとラクです。型は Vector2D になります。

この仕組みのおかげで、キャンバスに置いたゲージの形はいつでも自由に感覚的にいじっても大丈夫になります。画面デザインの調整のたびにブループリントを調整する必要はありません。

 

Slot as Canvas Slot ノードは Image が持ついくつかのパラメータの中でも Canvas Slot というカテゴリにアクセスするときに使うキャスト系(型変換)ノードです。

 

Scale で長さを変更するというのは、画面に描画する際に掛け算してから描くということになります。その掛け算の元のサイズが、 Canvas Slot のSize になるのです。

f:id:hiyokosabrey:20210821102851p:plain



ということで、サイズを変更するためのカスタムイベントを用意します。

growGaugeと命名

f:id:hiyokosabrey:20210821011813p:plain
引数として、最新のHPと、最新の増分を受け取るようにしています。

増分、増えた分は、パーセント(%)で管理する想定。

 

ゲージは Image を2枚重ねているので、それぞれを処理しますが、Set Sizeノードには同時にいくつもつなぐことができます。。

 

真ん中左付近の見慣れないノードは ↓ こうやって作ります。

f:id:hiyokosabrey:20210821004126p:plain

いろいろ計算して、最後にゲージの長さをセットする関数をつないでいます。ゲージの上限が増えるだけなので、ゲージの長さが変化しない想定です。

f:id:hiyokosabrey:20210821004943g:plain

Sizeを変更すると、ゲージの基本の長さを伸ばすことになるので、Scaleで縮めているだけのゲージは一緒に伸びてしまいます。そこで下敷きのみが変化しているように見せています。

例えば ゲージが60%の状態で、長さが 20%増えた場合。

f:id:hiyokosabrey:20210821010602p:plain

 

この補正処理はあくまでも表示バグ対策です。

ゲームの仕様次第で変わってきますが、ゲージ全体の長さが変わったとき、その時点でのHPの量が変わらないのであれば、見た目に長さが変わるのはおかしいという理屈です。

 

ということで、テスト用のレベルブループリントも、もう少しちゃんとしようと思います。

前回の記事で MyHP という名前の Float型の変数で、ゲージの長さを比率で管理していましたが、ゲーム的に扱いにくいので、Integer型の変数を使った管理方法に変更します。

最大値も必要なので、2つの変数を用意します。HP と MaxHP と命名

f:id:hiyokosabrey:20210821130802p:plain

右下の方にあるGet Gauge HP ノードは関数です。

内容は ↓ このようになってます。この後 数か所で利用するので関数化しました。

f:id:hiyokosabrey:20210821131021p:plain

引数を受け取らず、計算結果を返すだけの内容なので、ピュア型というカタチにしています。

f:id:hiyokosabrey:20210821131302p:plain

f:id:hiyokosabrey:20210821131504p:plain

ピュア型にすると、ちょっとだけノードの流れ(白いライン)が短くできます。

 

カーソルキーの増減処理のところも変更します。

f:id:hiyokosabrey:20210821132108p:plain

ここまで改造で、前回の動作はキープできました。

 

次にようやく今回のネタの部分、ゲージの長さを変えるためのトリガーになる処理。

f:id:hiyokosabrey:20210821132819p:plain

キーボードの Bキーを押すと 50ずつHPが増えていき、その都度ゲージWidgetに補正後の割合と、ゲージの増分を割合にして渡しています。

f:id:hiyokosabrey:20210821133609g:plain

 

このゲージの上限が増える表現については、ゲームの仕様によってさまざまなスタイルがあります。

いつどのタイミングで増えたことを見せるか、というのが設計のときに大事になります。

ちなみに ゲージの最大値が増えると、安心感が増える一方で難易度が下がったり、緊張感が減ったりするので、用法容量にはご注意ください。

 

 

 今回はここまで

次回は見た目を工夫してみようと思います

 ゲームの数だけゲージデザインがあるので助けになるかわかりませんが、ヒントになれれば幸いです。

 

ではでは

すてきなゲージライフを!

 

  

 

 

 

 

おまけ

UMGのチュートリアルでゲージといえば、プログレスバーを使ったゲージが紹介されていることが多いかと思います。

f:id:hiyokosabrey:20210821092126p:plain

f:id:hiyokosabrey:20210820224840p:plain

すでにこのProgressBarコンポーネントでUIを作られている場合でも、前回の記事を利用できます。

前回の記事ではImage のスケーリングで長さを変更していました。

その際の関数内の編集のみでサクッとプログレスバーに差し替えることができます。

 

f:id:hiyokosabrey:20210820225422p:plain

↑これを
↓こうします

f:id:hiyokosabrey:20210820225246p:plain

編集完了。

 

f:id:hiyokosabrey:20210820230005g:plain

 

動くHPゲージを作ってみませんか

季節は残暑。まだまだ真夏のはずですが、ここ最近の空模様のせいかセミの声も遠く感じる今日この頃いかがお過ごしでしょうか。先日のクイズゲームの記事でちょっとばかり燃え尽きてたりして間が空いてしまっていました。参加しそびれたのですが、ぷちコンのエンドレスランを作るイベントを見て、何か自分もぷちコン制作のお役に立てるものがないかと考えた結果、ゲージについて書くことにしました。

今までも度々ゲージに触れてきましたが、エンジンもずいぶんアップデートされてますし、復習もかねて記事にしてみようと思います。

 

UE4の基本的な操作方法については、「まぁまぁできるよ」という方を想定していますが、わからない操作等あれば、みつまめ杏仁 (@MMAn_nin) | Twitter かブログコメントにて問い合わせいただければ対応します。

 

 

  

 

シンプルなゲージとはいえ

ゲージが減った時に パッ、パッ、と途中段階のアニメーション無しに変化するタイプ。作るのは比較的易しいですが、常にゲージを見つめながらプレイは無理です。どれだけ減ったのか、いつ減ったのか、変化に気づきにくいので動きをつけるとグッと見た目がアップします。

f:id:hiyokosabrey:20210817001105g:plain

 

f:id:hiyokosabrey:20210817002313g:plain

 

 

今回はテクスチャを使わずシンプルなパーツで作っていきますが、増減する処理のところは、汎用的な仕組みなのでいろんなスタイルのゲージにも使えると思います。

 

 

作ってみよう

まずはWidgetのキャンバスにゲージの見た目として、Image を2つ重ねます。

f:id:hiyokosabrey:20210818012524p:plain

同じサイズで同じ位置に重ねると1枚に見せることができますが、いろいろアレンジできます。上図のように(下敷きの色が見にくいですが)ドロップシャドウぽく見せたり、下敷きを大きくしてみたり。

f:id:hiyokosabrey:20210818233049p:plain

大事なのは、後ろの背景に紛れてしまわないようにすることです。

そのためには手段を選ばないのがゲージを存在させるために必要な心意気です。

 

下敷きは、ゲージが減った状態でも、満タンからどれだけ減ったかがわかるので、とても重要な存在です。

 

上に置いたゲージを本体とします。

 

UMGで重ねた時の順番をコントロールする方法は2つあります。

ひとつはヒエラルキーでの位置

f:id:hiyokosabrey:20210819223224p:plain

もちろん後から描くほうが手前です

もう一つは ZOrder の値の大きさ

f:id:hiyokosabrey:20210819223400p:plain

数字の大きい方が手前になります。

ヒエラルキーで同階層、ZOrderの数字が同じ場合は、ヒエラルキーの順番で描かれます。またヒエラルキーで親子階層になっている場合は親同士が比較対象で、子供たちは親に依存します。

 

ゲージの長さは横方向のスケールを変えることで可変させるので、Pivot 位置を変更する必要があります。

f:id:hiyokosabrey:20210819000203p:plain

Render Transform にある Pivot X をゼロにすると、左端がスケールの基準点になります。デフォルトは (0.5, 0.5) なので、ど真ん中が設定されています。
Photoshopの変形時に表示されるバウンディングBOXをイメージするといいかも。

f:id:hiyokosabrey:20210819000720p:plain

 

Pivot X を 1.0 にすると、 右に減っていくゲージが作れます。

 

 

必要なパーツが配置できたので、ブループリントを編集します。

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

 

 

まず変数を2つ用意します。VariableType は Float型

f:id:hiyokosabrey:20210818234100p:plain

f:id:hiyokosabrey:20210819001546p:plain

CurrentValue と NewValue という名前にしました。

 

 

 

次にゲージの長さを変更するための関数を一つ作ります。

変数同様に、My Blueprintタブの Functions の右にある + ボタンをクリックすると新しく追加できます。

名前を changeGauge と命名して編集開始。

f:id:hiyokosabrey:20210819001726p:plain

① My Blueprintタブの Variables からゲージ本体の Image(キャンバスにレイアウトしたやつ)を取り出します。

 

② そこから Set Render Scale ノードを取り出してつなぐ

 

ここで、Scale の X だけを引数として受け取りたいので、ひと手間かけます。

f:id:hiyokosabrey:20210819002417p:plain

③ Set Render Scale の Scale ピンのところで右クリック

④ リストから Split Struct Pin を選択

 

こうなります。

f:id:hiyokosabrey:20210819002603p:plain

あとは、 Scale X のピンをドラッグ ⑤ して、 関数のノードにドロップ ⑥。

 f:id:hiyokosabrey:20210819002804p:plain

 

仕上げに 引数の名前と、Scale Y の値を変更。

f:id:hiyokosabrey:20210819003051p:plain

関数はこれで完成。

 

ちなみにピンの分解をせずに Make系のノードを使っても同じ結果になります。

f:id:hiyokosabrey:20210819224525p:plain

濃い青色のピンからドラッグして、make で検索すると取り出せます。この色のピンは特定の型を示すものではないので、慣れてないと不気味な感じですが、こうやってドラッグしてMakeノードを取り出したり、先の右クリックしてSplit(分割)する方法を使うと、中身がわかるようになってます。基本的に色の違うピン同士はつながらないのですが、これらの方法でどうにかなったりするので、馴染みのないピンが現れたらぜひ試してみてください。

 

 

つぎに EventGraphに戻って、カスタムイベントを一つ追加。

用意しておいた Float型の変数を使って計算と判定を行います。

f:id:hiyokosabrey:20210820003912p:plain

NewValue には 新しいゲージの値が入る想定。最新の結果が外から送られてきます。

CurrentValue は現在のゲージの値。

CurrentValue と NewValue に差分が発生するとアニメーションさせます。

差がなくなるとアニメーションは終了です。

アニメーションは簡単な減速処理です。イーズアウト的な動きになります。

左の方にある 0.25 という値を小さくするとゆっくり、大きくすると素早く動くようになります。

 

詳しくは過去記事で解説しているのでそれを貼っておきます。

Widgetで追跡するカーソル - みつまめ杏仁 (hatenablog.com)

 

続きの部分

f:id:hiyokosabrey:20210819013030p:plain

ここで、作っておいた関数を呼び出して、変化した変数を見た目に反映しています。

 

この計算式では、Current~とNew~が限りなくゼロに近づくだけでゼロにはなりません。

0.00000000001.....  みたいな感じで桁が増えていくばかり・・・

NewValue と CurrentValue の差がゼロになったら、 つまり 同じなら、というのを厳密にやると、一向に終わらないということになります。

そこで

Nearly Equal(Float) ニアリーイコールノードを使って、もういいだろうという範囲まで来たら、ゼロとみなすという判定を行っています。

 

Set Timer by Event ノードは、 指定した時間が経過したらイベントを呼び出してくれるノードです。

前のカスタムノードにつないでいるので、NewValue と CurrentValue の差が埋まらない限りは、何度でもカスタムイベントを呼び続けます。

 

これでエンジン的な処理が完成しました。

 

仕上げに、このイベントを始動するカスタムイベントと、ゲージを初期化するイベントを作ります。

 

新しくカスタムイベントを用意します。

f:id:hiyokosabrey:20210820004044p:plain
右端のノードは先に作った増減アニメーションのカスタムイベントを呼び出すときのノード。

 CurrentValue はこのWidget内だけのもので、外からNewValueに対して値を更新してもらうのが今回の仕様です。

 

ほぼほぼ完成ですが、ゲームをやり直したりする場合にゲージをリセットする必要があります。またコンティニューでその場復活HPちょっとだけ回復とか、あったりするので、アニメーション無しに一瞬でゲージの長さをセットしてしまう処理を用意しておくとゲーム的に何かと都合がよかったりして便利です。

そこでこのカスタムイベント。

f:id:hiyokosabrey:20210819231629p:plain

 

これでゲージのWidgetは完成です。

 

ぼくの場合Widgetの表示は、HUDクラスに担当してもらうのですが、手っ取り早くテストするために、レベルブループリントで試します。

 

テストする

ゲージ用の変数を一つ追加します。とりあえず MyHP と命名

ゲームにおけるHP管理用に相当します。

 

Create Widget ノードを取り出して、作ったゲージ用Widgetをセット。

f:id:hiyokosabrey:20210819232415p:plain

Return Value のピンから 変数に昇格(Promote to Variable)させて UserWidget型の変数を作ってつなぎます。

あとは、Add to Viewport ノードで画面に描画、HPの初期値をセットして、ゲージWidgetの カスタムイベントを呼び出します。

 

この状態で、任意の長さのゲージが表示できます。

 

次にキー操作で増減させます。

input ノードを使います。

Left と Right は それぞれカーソルキーの左右です Left[←] Right[→]

f:id:hiyokosabrey:20210820002116p:plain

二つとも内容はほぼ同じなので、片方を作って複製するとラクができます。

違うのはここ↓(緑の部分は上と同じ)

f:id:hiyokosabrey:20210820002618p:plain

これで再生してみると、カーソルの左右キーを押すたびに、ぎゅんぎゅん動くはずです。

f:id:hiyokosabrey:20210820003251g:plain

減るだけじゃなく増えるのも問題なしです。

満タンと空っぽのチェックをきちんとやれば、Widgetの カスタムノード UpdateGauge を呼び出して HPの値を渡してやるだけで勝手にアニメーションします。

 

ちなみに、スペースキーでリセットする場合はこんな感じにつなぐだけです。

f:id:hiyokosabrey:20210820011347p:plain

いつでも満タンにできます。

 

ひとまず想定していた仕組みが完成したので今回はここまでにします。

次回はこのゲージを使ってアレンジを楽しんでみようと思います。

ゲームの数だけ、仕様の数だけ、ゲージがあるので、作り方や実装方法も実に様々です。

こんなゲージ作ってみたいけど作り方がわからないとか、ちょっと無茶ぶりしてやろうとかあれば、チャレンジして作ってみようと思いますので、お気軽にメッセージ等々でネタ提供をお待ちしております。

 

ではでは

素敵なHPゲージライフを!

 

 

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にてお待ちしています。

 

ではでは

今回はこの辺で

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