UNREAL→LABØ

Unreal Engine4の情報をまとめます。

【第7回】条件分岐・タイマー

概要

前回は、変数の基礎や簡易的な演算を学びました。

今回は発展させて、新たな機能を追加しながら、条件分岐やタイマー機能などを学習していきます。

f:id:miccak:20210427134722p:plain

 

なかでも「条件分岐」、つまりある条件をもとに処理を分ける機能はもっとも使用頻度が高いといっても過言ではありません。

f:id:miccak:20210426235150p:plain

最初は理解するのが難しいかもしれませんが、今回だけでなく今後もじっくり学習していく範囲ですので、じっくり進めていきましょう!

 

また、キャラクター速度を管理したり、着地の通知、などについても学びます。

 

少し長いので、休憩をはさみつつやっていきましょう!

 

 

新機能のインプットアクションイベントを作成する

新しく「ジャンプしたときにキャラクターを飛行させる」仕組みを作ってみましょう。

 

まず実行するアクションを追加します。

 

以前のようにインプットアクションを追加しても良いですが、すばやくチェックするために今回は「キーボードイベント」を配置してみます。インプットアクションのイベントは複数のキーをまとめてひとつのイベントにしますが、キーイベントはひとつのキーのみのイベントです。

 

「ThirdpersonCharacter」のイベントグラフを開き、空いている場所を右クリックして、Fキーのキーイベントを配置します。

 

ここでは「F」としていますが、何のキーを指定しても構いません。ちなみに、”F”だけで検索すると検索結果が多くて調べるのが大変なのでご注意ください。

 

f:id:miccak:20210426235318p:plain

 

これは、「F」キーを押したときに処理が実行されるイベントです。

「Pressed」に「Print String」を接続して確かめてみても構いません。

f:id:miccak:20210427121150p:plain


問題なく実行されていますね。

 

重力の操作でキャラを浮かせる(ホバリングさせる)

キャラクターを飛行させる仕組みをつくる方法はさまざまですが、今回は学んできた「CharacterMovement」コンポーネントの機能を使って実装してみましょう。


「CharacterMovement」コンポーネントを配置しましょう。

f:id:miccak:20210427122336p:plain

「CharacterMovement」コンポーネントの配置方法については前回などを参照してください。

 

「CharacterMovement」コンポーネントの出力ピンからワイヤーを伸ばして「Set Gravity Scale」ノードと「Set Air Control」ノードを配置します。

 

これはキャラクターの受ける「重力」と「空中制御力」のパラメータを変更するノードです。

f:id:miccak:20210427122407p:plain

詳細パネルの図では、この部分にあたります。

 

f:id:miccak:20210427122417p:plain

ちなみに、ワイヤーをダブルクリックすれば、Reroute(中間ポイント)がつくれます。
処理内容に変化はありませんが見やすくなるので、適宜使ってみてくださいね。

f:id:miccak:20210427123419p:plain

 

変更後の値は、仮の値として以下のように変更しておきます。

  • 「GravityScale(重力)」を 0.1
  • Air Control(空中制御)」を 3.0

f:id:miccak:20210427123448p:plain

 

プレイしてみましょう。
さまざまな値を入れてみて挙動を確かめてみてください。

 

「Gravity Scale」をマイナス値にするとキャラは浮き始め「Air Control」の値を上げると空中でキャラを自由に動かしやすくなります。

さらに、キーを離したときパラメータを戻したいので、「Released」以降に戻す処理をつくります。

f:id:miccak:20210427123717p:plain

値の部分は、「変数」にしてしまいましょう。

 

f:id:miccak:20210427123739g:plain

f:id:miccak:20210427124514p:plain

しかし、このままでは変数は空(初期値)のままですね。
「Begin Play」に初期値を保存する仕組みもつくりましょう。

f:id:miccak:20210427124526p:plain

これでゲーム開始時にもともとの値を保存する仕組みができました。

 

ちなみに「CharacterMovement」のノードをひとつにして、まとめてもOK。

適宜わかりやすい方を使ってくださいね!

f:id:miccak:20210427145659p:plain


コンパイルからプレイして、確認してみましょう。
ジャンプしたあとFキーを押したら、ふわっと浮くようになれば成功です!

f:id:miccak:20210427124553g:plain

適宜好きな値に微調整してみてくださいね。

変更後の値は、前回のように変数に対して計算式を接続してみても良いかもしれません。

f:id:miccak:20210427124629p:plain

「CharacterMovement」コンポーネントを書き換えるだけですぐに作れてしまいましたね。

ここからどんどん機能を調整していきましょう。

 

 

加速度を緩和させる

機能の気になる点を、改善していきながら学習しましょう。

ひとつめは、動かずにジャンプして「F」キーを押したときと、移動しながら実行したときで、ジャンプの高さなどが大きく変わってしまうこと。これは、処理が実行されたときにキャラクターの加速度(Velocity)が多く残っているのが原因です。

 

物理機能や「CharacterMovement」コンポーネントを使ったアクタ(キャラクター)の動きは、「Velocity(ベロシティ)」というパラメータで「移動方向とその大きさ(速度)」を表しており、「Vectorベクター・ベクトル)」という「X,Y,Z」で構成される単位を使って管理します。移動方向と速度を変更するときは、この値を上書き(更新)しなければなりません。

物理に詳しくないと言葉だけでは理解しづらいかもしれません。
正直なところ筆者はそこまで物理に詳しくないのでこの程度の説明ですが、詳しく知りたい方は公式ドキュメントなどを読まれることをオススメします。
ただ慣れていけば、詳しくなくてもある程度使っていけます!

 

実際に作ってみて、使い方に慣れていきましょう。

 

まずは、Velocityの値を見てみることにします。

 

「CharacterMovement」コンポーネントの出力ピンからワイヤーを伸ばしてください。
「Get Velocity」を検索して配置します。

 

そして「Print String」を配置。
「Get Velocity」の出力ピンからワイヤーを伸ばして「In String」に接続します。

f:id:miccak:20210427131723p:plain

 

ちなみに「Get Velocity」という単一のノードも存在します。

どちらを使っても同様の結果を得られますので適宜好きな方を使ってください。

f:id:miccak:20210427131805p:plain

 

プレイして確認してみます。

キーを押した瞬間に、キャラクターの「Velocity」の値が確認できました。
ジャンプしているため、高さ方向への値(Z)が大きいですね。

f:id:miccak:20210427131859p:plain

 

では、キーを押したとき、この値を強制的に下げてみましょう。

「CharacterMovement」から「Set Velocity」を検索して配置します。

 

ここでは値を変更したいので「Set」ですね!

 

処理が流れるように実行ピンを接続します。
確認しましょう。
キーを押したとき、キャラクターがぴたっと止まれば設定できてます。

 

f:id:miccak:20210427132016p:plain

 

しかし、急停止しすぎるのも若干気になりますね。
そこで、Velocityをゼロにするのではなく、半減するようにしてみます。

 

以前学習した除算演算子を使いましょう。

 

「Get Velocity」の出力ピンからワイヤーを伸ばして「/」を入力します。
Vector / float」ノードを配置します。これはVector値を除算するノードです。
値は「2」にします。

 

f:id:miccak:20210427132048g:plain

f:id:miccak:20210427132118p:plain



 

空中にいるときだけ実行するよう分岐させる

せっかく処理を作ったものの、処理を入れたことでさらに気になる点が出てきてしまいました。

 

キーを押したままジャンプしたときは、実装した「速度が半減する処理」も効果が及ばないのです。このVelocity半減処理はキー押した瞬間に実行されるからですね。

 

これをふせぐために「キャラクターが空中にいるとき」だけ処理が実行されるようにしてみます。

 

「CharacterMovement」コンポーネントから出力ピンを伸ばします。
「Is Falling」ノードを検索して配置しましょう。

 

f:id:miccak:20210427132154p:plain

 

このノードのReturnValueピンは赤色ですね。
これは「True(真)」か「False(偽)」のどちらかの値を返すBoolean型といわれる型で、その性質から条件分岐などで多用されます。

 

「Is Falling」ノードの場合、ターゲットに接続されたコンポーネント(アクタ)が空中にいれば「True」、そうでなければ「False」を返す仕組みになっています。

 

「Is Falling」を使って処理を分岐させます。

 

ノードの近くを右クリックして「Branch」と検索してブランチノードを配置しましょう。

「if」と検索しても検索にヒットします。
また、「B」キーを押しながらグラフを左クリックしてもノードを配置できます。 

 

「Is Falling」の出力ピンをブランチノードの「Condition」に接続します。

f:id:miccak:20210427132247p:plain

 

これで、流れる処理を分岐できるようになりました。
一連の処理ノードに組み込んでいきましょう。

 

「Is Falling」から返される値が「True」なら、ブランチの「True」処理以降が流れます。
返される値が「False」ならブランチ「False」の処理が流れます。

 

空中にいたら処理を流したいので「True」以降に処理を接続してください。

 

f:id:miccak:20210427132307p:plain

 

プレイして確認してみましょう。
空中にいるときだけ処理が流れたら正しく設定できています。

 

 

【補足】Velocityノードを参考に分岐する

「Velocity」ノードは速度を取得できるのだから、それで確認できるのでは?
と気づくかもしれません。事実そのとおりです。

 

キャラクターはVelocityの「Z」方向への値が0よりも大きければ上昇、0より小さければ下降しています。興味のある方は図のような実装も確認してみてください。

f:id:miccak:20210427132415p:plain


図には、新しく上記ノードが出現しています。
それぞれのノードは検索すれば出てきます。

  • Vectorの分解ノード
  • Absolute(絶対値)にするノード
  • 比較演算するノード

 

Characterクラスを使っている場合は「CharacterMovement」コンポーネントの機能「Is Falling」ノードで判断できるので基本的に使わなくても問題ありませんが、このコンポーネントが無いブループリントで同様の検知をしたい場合などに活用できるときもあります。余裕があれば覚えておいてください。

 

 

制限時間を加える(タイマー・カスタムイベント)

空中に浮き続けるのも楽しいですが飛びすぎ防止のため、ためしに「制限時間」の実装を検討してみることにします。

「サクッと作ってためしてみよう」というUE4的な思想は大事ですね。

 

「Pressed」処理がすべて実行されたあと、以前学習した「Delay」で待機させれば元の値になると考えるかもしれません。しかし作ってみると少々問題があることがわかります(余裕があればためしてみてください)。

f:id:miccak:20210427132823p:plain

かわりに、処理をタイマーで実行する「タイマー機能」を使って、制限時間を作ってみましょう。

 

「Pressed」処理にある最後のノードからワイヤーを伸ばします。
「Set Timer by Function Name」と検索して配置します。

 

f:id:miccak:20210427132834g:plain

 

タイマーのノードはいくつか種類があるので注意してください。 

 

このタイマーは、ノードまで処理が流れるとまず「Time」に入力された値だけ待機が実行され、ウェイトが完了すると「Function Name」で指定されたイベントが実行されるという機能があります。

 

なお、Loopingピンにチェックを入れると、タイマー指定されたイベントがTime間隔ごとにループ実行されるようになります。

 

タイマーでパラメータの初期化を実行してみましょう。

 

タイミング(タイマー処理が実行されるまでの時間)を指定します。Timeに2.0を指定しました。

 

f:id:miccak:20210427132939p:plain

 

タイマーで実行させるカスタムイベントをつくります。


イベントグラフの空いている場所を右クリックします。
「Custom Event」と検索し「カスタムイベントを作成」を選んで配置しましょう

 

カスタムイベント(Custom Event)とは、その名の通り独自に作成したイベントです。

 

そのブループリント内ならどこからでも呼び出すことができるもので、呼び出しは何度でも行えます。

ただしそれは意図的に呼び出す必要があり「Begin Play」のようになんらかのトリガーによって呼び出されないことに注意してください。

f:id:miccak:20210427133031g:plain

 

カスタムイベントが配置されたら、好きな名前をつけましょう。
こちらも英語の名前をつけることをオススメします。

 

f:id:miccak:20210427133059p:plain

 

図では「Init Fly Param」としました。

パラメータ初期化の内容、つまり「Released」時に実行される処理をこちらに移しましょう。

 

f:id:miccak:20210427133112p:plain

 

わかりやすくするため「Print String」を挟んでいます

 

処理をカスタムイベント化できましたね。

必要な場所でこのイベントを呼び出しましょう。右クリックからイベント名で検索をかけるか、マイブループリントパネルからドラッグ&ドロップで設置しましょう。

f:id:miccak:20210427133204g:plain

 

タイマーにもこのイベントを登録します。


「Set Timer by Function Name」ノードの「Function Name」入力ピンに作成したカスタムイベント名を入力しましょう。

f:id:miccak:20210427133239p:plain

 

これでタイマー実行時に初期化イベントが実行されるようになりました。
 
プレイして確認してみましょう。
 
ひとまずは問題ありませんが、いくつか気になる点もあります。
ひとつは飛行する機能を使用後すぐに着地しても、タイマーイベントが発生しているということ。もうひとつは、連打するたびにタイマーがリセットするという点です。
 
次項では、これらを改善するために以下の処理を作っていきます。
  • 着地したらタイマーをリセット&初期化する。
  • タイマー実行中は、再度タイマーを実行させない。

 

 

着地したらタイマーをリセット&初期化する

まずは、リセットするためこのタイマーの参照となる変数を作成します。

「Set Timer by Function Name」ノードのReturn Value出力ピンからワイヤーを伸ばします。

「変数へ昇格」を選択します。

 

f:id:miccak:20210427133327p:plain

 

ワイヤーがつながった状態で変数が作成されましたね。

以前作成した「Float」型の変数では浮動小数点の数値を入れることができましたが、この「Timer Handle」ではタイマー情報(参照)を入れる事ができます。

 

現時点でセットノードにより、このタイマー情報(参照)が入っています。

任意の名前に変更します。

f:id:miccak:20210427133345p:plain

 

この変数をターゲットにした機能を作成しましょう。

着地したときの処理を作成しましょう。
Characterには「着地したとき」に発行される「On Landed」というイベントがあります。


そちらを活用すればうまくいきそうですね。

 

カスタムイベントとは異なり、動作がトリガーになって発生するイベントです。

 

検索ウィンドウで「On Landed」と検索して配置します。

f:id:miccak:20210427133439p:plain

 

この「On Landed」の出力ピン「Hit」からは着地したキャラクターの衝突情報を取得できますが、今のところは使わないので気にしないで進みましょう。

 

この実行ピンからタイマーを解除する処理を構築します。

まずはタイマーの参照にアクセスするため、ゲット(GET)ノードを配置します。

 

f:id:miccak:20210427133518g:plain

  

 変更したいという目的があるため、セットノードではなくゲットを使います。参照の変数を対象にして命令すると考えましょう。タイマーの中身を入れ替えたいときなどにセットを使います。

 

 「Fly Timer」(タイマーの変数)の出力ピンからワイヤーを伸ばします。

「Clear and Invalidate Timer by Handle」を検索して配置してください。
そして実行ピンを「On Landed」の実行ピンに接続します。 

f:id:miccak:20210427133724g:plain

 

 このノードは、Handleピンに接続されたタイマーの実行をすべて中止して、残り時間の計算も消去するという機能を持ちます。タイマー自体を削除すると考えてください。

 

これでキャラクターが着地(On Landed)するとタイマーが削除する処理が完成しました。

また、着地したあとはパラメータを初期化して良いはずです。

ついでに「On Landed」で初期化処理も走らせてしまいましょう。

 

f:id:miccak:20210427133811p:plain

 

これでひとつめの問題は解決できました。
プレイして確認しましょう!


 

タイマー実行中は再度タイマーを実行させない

つづけて、もうひとつの問題を解決しておきます。

ひとまずのところ分岐ノードを使い、タイマーノードが実行される直前で、タイマーが実行中なければタイマーを処理するようにして解決をこころみます。

タイマー変数のゲットを配置しましょう。

 

f:id:miccak:20210427133845p:plain

 

出力ピンからワイヤーを伸ばし「Is Timer Active by Handle」を検索して配置します。
これは接続されたタイマーがアクティブ状態かを返すノードです。
 

f:id:miccak:20210427133902p:plain

「Is Valid」ノードを使っても同じような結果を得ることができます。参考までに。

 

このノードを使って条件分岐させましょう。

f:id:miccak:20210427133927p:plain

 

タイマー変数が有効なときはTrue、有効でないときはFalseに処理が流れるはずです。


有効でないときにタイマーを実行させればいいので、図のように組みましょう。

プレイして確認してみましょう。

 

 

 

改善&アップデート!

少しずつ機能ができてきましたね!

UE4では即座にプレイして確認できるので、手探りででも少しずつトライ・アンド・エラーを繰り返しながら仕組みを作り上げていくことができます。

 

たとえば制限時間などの機能は実装が簡単なぶん、さまざまな実装方法があります。
余裕があれば、この学習に並行して色々なやりかたをためしてみてください。

今回は学習済みの範囲からはなるべく飛び出さずに、簡易的な方法で実装するように作成しましたが、今後の連載でアップデートしていく予定です。

 

作成していくうちに良いアイデアが湧くかもしれません。
ブループリントに慣れてきたときのためにメモしておきましょう。もちろん、ためしに作ってみるのも、UE4的な思想で素晴らしいと思います。

余裕があれば(思いつけば)、ここから他の機能の作成にチャレンジしてみてください。たとえば今回の内容だけで、走っているときだけジャンプ力を変えたり、ボタンの長押しや連打によって発生する処理などを作れそうです。

 

今回は以上です。
お疲れさまでした!