みつまめ杏仁

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

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

 

ではでは

今回はこの辺で

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