【第14回】イベントをトリガーにする(イベントディスパッチャー)
- 概要
- 確認用のレベルを作成する
- 「収集アイテム」アクタを作成する
- (補足)アクタをクリックしやすくしておく
- 獲得処理を作る(オーバーラップイベント)
- イベントディスパッチャー
- アクタを作成してイベントディスパッチャーをバインドする
- タイムライン機能を使ってアクタを動かす
- (補足)動きの調整
- (補足)足場を元の位置に戻すには
- (補足)エラーが出たときは
- (補足)バインドのタイミング
- (補足)レベル上のアクタを検索する
- プロジェクトのダウンロード
概要
今回はクリアするのに必要な「収集アイテム」のアクタを作成し、そのアイテムを獲得することで動作(トリガー)するアクタを作成します。
以前学習した「収集アイテム」のブループリントで、「トリガー動作するアクタ」の参照を作成して、イベントを呼び出すという方法を使うのも良いですが、今回は「イベントディスパッチャー」という機能を用います。また、動作部分の動きは「タイムライン」という機能を使って実現します。
確認用のレベルを作成する
確認用のレベルを作成しておきます。
ゲームモードも設定しておきましょう。
「収集アイテム」アクタを作成する
このアクタはゲームでよくある「獲得で消滅する」仕様で、複数個配置します。
レベル上のアイテムをすべて獲得するとゴールまでの道が開かれる予定です。また、それぞれのアイテムには個別のID(通し番号)を設定できるようにして、特定のアイテムを獲得したら、それをトリガーにして別のアクタのイベントを発行できるようにします。
まずは「追加/インポート」>「ブループリントクラス」>「Actor」でアクタを作成してください。
名前は自由です。ダブルクリックして開きましょう。
コンポーネントを設定していきます。
当たり判定は、メッシュそれ自体としても良いですが(メッシュに触れたら獲得)、スタティックメッシュの形状に左右されてしまうことを防ぐため、別途コリジョンコンポーネントを追加しておきます。
「コンポーネントを追加」から「Sphere Collision」を選び追加してください。
そして、この「Sphere Collision」を基準とするためルート(Root)にします。
「Sphere Collision」を「Default Scene Root」にドラッグ&ドロップしましょう。
「Default Scene Root」が削除されて「Sphere Collision」がルートに置きかわりましたね。
このコリジョンを親にして、その下に「Static Mesh」などを追加していきます。
「コンポーネントを追加」から「Static Mesh」を追加しましょう。
詳細パネルで、以前作成したアイテム用のメッシュを設定します。
好きなメッシュを設定していただいても構いません。
マテリアルも任意のものを設定しておきます。
獲得判定にはメッシュのコリジョンは使わず「Sphere Collision」の当たり判定を使うので、邪魔にならないようメッシュ自体のコリジョンを削除します。
詳細パネルの「Collision」項目からコリジョンプリセットに「No Collision」を選んでください。
「Sphere Collision」のサイズをを設定しておきましょう。
コンポーネントパネルの「Sphere」を選択して、詳細パネルの「シェイプ」>「Sphere Radius」からコリジョンサイズを変更します。
これでコンポーネントの設定は完了です。
(補足)アクタをクリックしやすくしておく
マテリアル設定の関係上、アイテムのアクタがレベルでクリックしづらくなっています。
こういった選択しづらいアクタや、小さなアクタなどを見つけやすくするため、ときどき「見えないコンポーネント」を付与させておくことがあります。
コンポーネントパネルから「Billboard」というコンポーネントを追加します。
これは常にカメラの方を向いて描画される2Dテクスチャで、 プレイ時には表示されません。
ちなみに詳細パネルから描画テクスチャの見た目を変えることもできます。
無理に使う必要はありませんが、適宜目印として使ってみてください。
レベル上でメッシュの見た目を確認したいときに邪魔だなぁというときは、レベルのビューポート上で「G」キーを押してみましょう。実際のゲーム中と同じ見た目になります(ビューポート上のメニューからもONOFFできます)。
獲得処理を作る(オーバーラップイベント)
「Sphere Collision」にプレイヤーキャラクターが触れたときに発生するイベントを作ります。
コンポーネント「Sphere Collision」を選択。
詳細パネルの「イベント」から「On Component Begin Overlap」を選択します。
イベントグラフに「On Component Begin Overlap」イベントが追加されました。
アクタがプレイヤー以外が触れたときに動作しないよう、判定する処理を入れます。
「Cast」で判断する方法で実装しても良いのですが、他の処理にも慣れておきたいので今回は「Player Pawn」と比較する方法で判定してみます。
Castでの判定が間違っているわけではないので、そちらを採用してもかまいません。
「Other Actor」ピンからワイヤーを伸ばして「==」で検索して比較ノードを配置します。
さらに「Get Player Pawn」ノードを検索して配置し、ブランチノードで分岐させます。
これでオーバーラップしたのがプレイヤーであれば「True」の処理が流れるようになりました。
続けて、プレイヤーキャラクターに触れたことをブランチ確認したら、再度このコリジョンが反応しないようにします。また、見た目を消しておきましょう。
「Destroy Actor」ノードを使ってレベルから削除(消去)しても良いのですが、削除前に処理を入れる可能性もあるので、今のところは見た目と判定を消すだけにしておきます(現状レベルのアセットも多くなく負荷も心配なさそうです)。
グラフ上に「Sphere Collision」をゲットして「Set Collision Enabled」ノードを配置します。
タイプを「No Collision」にしてコリジョンの判定を消しましょう。
続けて表示「Set Actor Hidden In Game」を設置。
オプションのNew Hidden チェックをONにします。
これは文字通りアクタの表示を消すノードで、引数のチェックを入れる(True)と非表示になります。
逆に、表示させたいときはチェックをOFFにしたノードを呼び出します。
表示を消す「Set Visibility」というノードもあります。
このノードの場合は引数のチェックをFalseにすると表示が消えます。
プレイして、キャラクターが触れたら消えるかを確認しておきましょう。
合わせて音などを鳴らすノードを追加すると、よりわかりやすくなるかもしれませんね。
ためしてみてください。
イベントディスパッチャー
「オーバーラップした」ことを知らせるトリガーとしてイベントディスパッチャーを使います。
イベントディスパッチャーとは「イベントが呼び出されると、そのイベントにバインドされたイベントも同時に実行できる」という機能で、ひとつの呼び出しで複数のイベントを同時実行したいときなどに有効な機能です。
説明だけを聞いてもなかなか理解できない機能かもしれません。
何度も使って少しずつ理解していきましょう。
以前学習した「イベント元から別のブループリントを参照して呼び出す」のではなく、逆に「別のブループリント側からイベント元のイベントを受け取る」と考えたらわかりやすいかもしれません。
まず呼び出すイベントディスパッチャーを作成します。
マイブループリントパネルのイベントディスパッチャーを追加して(「+」を押す)、
イベントを作成します。名前はわかりやすいものにしておきましょう。
このイベントディスパッチャーを獲得時の処理の最後に呼び出します。
検索して配置しても、図のようにドラッグ&ドロップで「呼び出し」を選んでも構いません。
これでひとまず獲得アイテム側の処理が完成しました。
アクタを作成してイベントディスパッチャーをバインドする
イベントを受け取るアクタを作成していきましょう。
動作すると、図の場所にある床が落下するようにします。
仮素材をつくるため、床部分をメッシュ化しておきます(コリジョンもつけておきます)。
「追加/インポート」>「ブループリントクラス」>「Actor」で作成します。
Static Mesh コンポーネントを設定しましょう。
イベントディスパッチャーを受け取る処理を作りましょう。
そのためにまず、アイテム(BP_KeyStone)の参照用変数を作成します。
参照を得る方法は他にもいくつかあります(後述)。
今回は簡単な対象のアクタを型とした変数を作る方法をとりました。
マイブループリントパネルから、変数を作成します。
詳細パネルから変数の型をアイテムの型に変更します(BP_KeyStone型)
これで、アイテムのブループリントを格納・参照できるようになりました。
レベル上で格納するため、インスタンス編集可能のチェックを入れておきます。
コンパイルして保存します。
テスト用のレベルに配置して、指定しておきましょう。
ブループリントに戻り、「Begin Play」イベントの近くに変数のゲットを配置します。
変数の出力ピンからワイヤーを伸ばして、イベントディスパッチャー名で検索してください。
「(イベントディスパッチャー名)にイベントをバインド」を選んで配置します。
そしてBegin Playに接続します。
「Begin Play」実行時(ゲームプレイ開始時)に、「BP_KeyStone」の「Get the Key Item」イベントとバインドする(繋がりをつくる)ことができました。これで「Get the Key Item」の呼び出しが起きると、バインドに接続されたイベントが実行されるようになります。
実際にバインドにイベントを作成しましょう。
ノードの「イベント」という部分からワイヤーを伸ばして Custom Event と検索してカスタムイベントを配置してください。任意の名前をつけておきます。
これでバインド元のイベントが呼ばれると、このバインド接続されたイベントが実行されます。
バインドが実行されていなければ、赤いワイヤーで接続されたイベントは実行されないことに注意しましょう(そのために最初に実行される「Begin Play」でバインドを行っています)
ためしに、「Print String」を接続してためしてみましょう。
獲得した瞬間にバインドされたイベントが実行されましたね。
あとは動作内容を作るだけです。
タイムライン機能を使ってアクタを動かす
タイムラインとは、「Tick」イベントに依存せずに時間ベースで区切って指定された処理を実行するノードで、なめらかに動くアニメーションのような演出を簡単に作ることができる機能です。
たとえば「2秒毎に動くフロア」や「5秒間だけ光らせる」といった機能をつくるときには重宝します。
グラフを右クリックして「Timeline」と検索して配置してみましょう。
配置したら名前をつけておきます。
実行ピンが多くて混乱するかもしれませんが、機能はいたって単純です。
まずは3つ覚えておきましょう。
「Play from Start」実行ピンに処理が流れると、タイムラインが開始、実行されます。
実行内容は「Update」以下の処理です。設定された時間だけ処理は流れます。
そしてタイムラインの設定時間が終了したら「Finished」ピン以降の処理が流れます。
設定してみましょう。
タイムラインの実行される時間を設定しましょう。
タイムラインをダブルクリックしてください。タイムラインエディタが別タブで開きます。
「長さ」がタイムラインの実行時間(秒)です。初期値は「5.0」になっていますね。
任意の時間を設定しましょう。「1.0」を設定しておきました。
次にタイムライン実行中に出力する値(グラフ)を設定します。
どんな情報を出力するか決めましょう。タイムラインでは「f+(float値)」、「V+(Vector値)」などさまざまな情報を出力できますが、今回は基本となる「Float値」を出力させましょう。
「F+」(floatトラックを追加
イベントグラフに戻って確認してみましょう。
設定した名前でFloat型の出力ピンができているのがわかるでしょうか。
タイムラインのグラフに戻ってください。
出力ピンからどんな値を出力するか設定していきます。
ためしに回転させてみましょう。
キーを打ちます。「0」秒地点に、キーを打ちましょう。
「Shiftキー」+「左クリック」でキーを打つことができます。
打ったキーは左クリックなどで選択できます(複数選択も可能)。
ひとつ目のキーは時間を「0」、値を「0」に合わせておきましょう。
これで、タイムライン開始から(タイムライン終了まで)0の値が出力されることになります。
つぎにもうひとつのキーをグラフ時間の終わり際付近に打ちましょう。
正確にするため、キーを打ったあとは値を入力しておくことをオススメします。
ズーム機能を使って、見やすい形にしておきましょう。
なお、キーの曲線は手動で調整することが可能です。
さて、これで出力する時間と値が設定できました。
イベントグラフに戻り、「Update」実行ピン以降に処理を繋ぎましょう。
ためしに「Print String」を使って値を見てみます。
バインドされたイベントの最後部に「Play from Start」ピンを接続します。
「Update」に「Print String」を接続して、出力されるMove値を「In String」に繋ぎます。
プレイして確認してみましょう。
グラフの値がうまく出力されていますね。
つぎは「Set Actor Rotation」を使ってみます。
これは、このアクタ自身を回転させるノードです。検索して配置しましょう。
実行されるとターゲット(Self)の回転値を指定した(New Rotation)値に変更します。
「Set Actor Rotation」が受け取れるのは「Rotator」型のピンですが、この型は分解することができます。「Set Actor Rotation」の「New Rotation」ピンからワイヤーを伸ばして「Make Rotator」ノードを設置してみましょう。入力値が分解されました。
ピッチに接続してみます。
プレイして確認してみましょう。
うまく回転してくれたように見えますが、タイムライン実行時にRollとYawが0に上書きされるため、レベルに置かれたアクタが回転していると意図しない動きになってしまうことがわかります。
「Set Actor Location」と同じように、このノードではアクタをワールド座標で上書きします。
それを回避するために、「Set Actor Rotation」が実行されるとき、現時点の角度を参照して指定するようにしましょう。
「Get Actor Rotation」を検索して配置します。
出力ピンから現在の角度を取得できるので、ワイヤーを伸ばして「Breake Rotator」を配置して角度の値を分解します。
Roll と Yaw を接続します。
動作部分が完成しましたので確認してみます。
RollやYawを変えたければタイムライン出力ピンの接続先を変えるだけです。
別の値を出力したければ、タイムラインでグラフを増やしたり、「Vector」グラフを作成して出力するという方法もあるので、いろいろ試してみてください。
とりあえずタイムラインの処理を確認したいというときは、タイムラインエディタにある「オートプレイ」のチェックを入れる方法が簡単です。このチェックを入れると自動でタイムラインが実行されます。
基本的にはこれで完成です。
レベル上に配置して調整しましょう。
タイムラインエディタの補足として、「最終キーフレームを使用しますか?」オプションをOnにすると設定した長さに関係なく最後のキーまでUpdateを実行してから終了するようになります。
また、ループにチェックをいれるとタイムラインの実行を繰り返すようになります。
以前作成した行ったりきたりする足場などもタイムラインで作れそうですね!
(補足)動きの調整
作成したものの、イメージ通りの動きではなかったので調整してみました。
驚かせるような傾きは抑えて、エレベータのようにゆっくり下がっていく演出に変えています。
図のようにタイムラインには「Set Actor Rotation」だけでなく、さまざまなノードを組み込むことが可能です。余裕があれば調整してみましょう。
オートプレイで確認した際は、チェックを外すのを忘れずに。
レベルでプレイしながらチェックする場合は、レベル上の開始したい場所にあるメッシュなどを右クリックした際のメニュー「ここからプレイ開始」を選択してスピードアップを図りましょう。(たまにズレますが、大体の場合狙った場所からプレイを開始できます)。
さて、いかがだったでしょうか。今回は以上です。
アクタ同士の連携は今後も頻繁に使うはずですので、この段階でなんとなくでも理解を深めておくと後々ラクになるかもしれません。
余裕があればイベントディスパッチャーやタイムラインに要素を加えて、演出を足してみましょう。
たとえば、いずれキャラクターを戻すシナリオを入れて自由度を持たせたいと思うかもしれません。しかし現状では足場を戻すことができず、戻ったとしても再度傾ける処理がありません。そんな事態を考慮して柔軟性をもたせるため、アイテムの獲得をディスパッチャーにするのではなく、同じくらいの大きさのコリジョンを別途配置して、トリガーにすることも検討すべきかもしれませんね。
まあ、実装や(レベル)デザインの問題ですし、足場を動かすギミックを別途追加すれば良い、という考えでも構わないと思います(たぶん)。
さまざまな実装にもサッと対応できるようなつくりを心がけたいですね!
ちなみに、以下補足にて、タイムラインを巻き戻す方法で足場を戻す方法を記載しておきます。ご参考までに。お疲れさまでした!
(補足)足場を元の位置に戻すには
「Reverse(Reverse from End)」入力ピンによる「巻き戻し」機能を使うのが便利です。
この実行ピンは文字通りタイムラインの内容を巻き戻す機能があります。
巻き戻しのためにカスタムイベントを作成して「Reverse from End」ピンに接続します。
タイムラインが進行中か巻き戻り中かはフラグ変数で管理するか、「Direction」ピンから進行状況を取得するのが簡単です。
図では「Finished」ピンに、現在タイムラインが進行中かを判断するブランチを設定して「Delay」待機をしたあとに巻き戻り処理を実行するものです。
「Finished」ピンはタイムラインが終了したら実行される処理です。
タイムラインの「Direction」出力ピンからワイヤーを伸ばして「=(イコール)」を検索して配置。
「Dicrection」からはタイムラインが通常再生なのか巻き戻し再生なのかを列挙型という型で受け取ることができます。受け取る値が「Forward」なら進行中、「Backward」なら巻き戻り中を示しています。それをつかって、再生中のときだけ(Delayを挟んで)、以降の巻き戻しイベントが呼ばれるようになっています。
この分岐がないと、巻き戻しタイムラインが実行されたあとも再度巻き戻しイベントが呼ばれてしまいます。
(補足)エラーが出たときは
プレイして確認するとうまく回転しない、プレイをやめると図のようなエラーメッセージが出ることがあります。
前回でも出たエラーですが、“ Accessed None trying to read property ~~.” というメッセージのときは、変数などの参照がないことを示しています。
レベル上にある参照の変数割当てがしっかりされているかを確認してくださいね。
(補足)バインドのタイミング
今回はイベントをバインドするタイミングを「Begin Play」にしましたが、「Get the Key Item」イベントが呼ばれる前であればどこでも構いません。
(補足)レベル上のアクタを検索する
参照用の変数からイベントディスパッチャーのバインドを作成する方法では、レベルが始まったときに変数にアクタが格納されている必要がありますが、レベル開始途中で検索したりアイテムが出現させたタイミングでバインドしたくなるかもしれません。
そういったとき、しばしばアクタを検索してバインドします。
検索には「All Actors~」から始まるイベントを使うのが一般的です。
2種類ご紹介します。
すぐに使いこなす必要はありませんが、こういったやり方もあるんだな程度に読んでください。
Get All Actors Of Class
クラスを指定して検索するノードです。
クラスとはブループリントなどを指します。
プルダウンからクラスを指定して使います。
図では「BP_KeyStone」を指定していますね。
実行されると、「Out Actors」ピンから該当するクラスのアクター参照が配列形式で出力されます。
「配列」とはいくつもの変数がひとまとまりになっていると考えてください。
詳しくは今後の章でご説明します。ここでは、レベル上の(指定したクラスの)アクタ参照をまとめて受け取っていると考えてください。
レベル上にひとつしかないアクタの場合は、出力ピンから「Get」という「指定した順番の配列情報を返す」ノードを配置します(引数が0なので配列1番目の情報が返る)。
しかし、今回のように複数個アクタが設置してある場合は、何番目のアクタなのかわかりません。
そこでアイテムのブループリントに「通し番号(目印)」をつけたうえで、配列すべてに対して「その目印を持っているか?」をチェックする処理を行って探し出します。
まずは獲得アイテム(BP_KeyStone)に通し番号を設定します。
獲得アイテムのブループリントを開いて変数を追加してください。
Boolean型でも良いですが、各アイテムの順番を設定するナンバーとしてInteger型を指定しました。
個別に変更できるよう、「インスタンス編集可能」のチェックも入れておきます。
そして、レベル上にあるアイテムに対して連番の番号を割り振ります。
そのうちギミックが動作するトリガーとなるアイテムのナンバーを「1」とします。
「Get All Actors of Class」のあるアクタのブループリントに戻ります。
「For Each Loop」というノードを配置して接続します。
このノードは受け取った配列すべてに「Loop Body」以降の処理を実行するノードで、配列のひとつひとつが「Array Element」ピンから出力されます。
この場合はレベル上にある「BP_KeyStone」アクタがすべて出力・参照できます。
そこから作成した変数をゲットして、指定した番号か確認すればどの要素が目的のアクタなのかがわかるという仕組みです。
そこから即座にバインドしてもいいですが、変数に格納してみましょう。
検索にヒットしたアクタから変数を取得して、指定番号と比較する処理ができました。
ゲットした変数の値が一致したらTrueとなり「そのとき確認している「Array Element(BP_KeyStone)」が「Key Stone」変数に代入される処理が実行されます。
「For Each Loop」はすべての配列にたいして処理が終わると、「Completed」ピンから処理が流れます。そこでバインドしてみましょう。
これで検索からバインドまで完成しました。
Get All Actors of Class with Tag
「目印」に変数ではなく「タグ」を使う方法もあります。これは文字通り識別に使える「タグ」のような仕組みでアクタごとに設定できます。
レベル上からアクタを選択して、詳細パネルのActor項目にあるTagsを選択しましょう。「+」を押してタグを追加します。
任意の名前をつけます。これで区別できるようになりました。
検索には「Get All Actors Of Class with Tag」か「Get All Actors with Tag」を使います。
前者はクラスの指定とタグの指定をして一致するものを検索、後者はすべてのアクタに対してタグ検索を実行します。図では「Get All Actors Of Class with Tag」を使って検索しています。
注意点として「Get All Actors Of Class」では不要だった「Cast」ノードを挟む必要があることです。それ以外は通常の検索同様に使うことができますので、うまく使い分けてください。個人的にはタグ管理が面倒なのであまり使わないデスが……。
プロジェクトのダウンロード
※ 第14回Appendixまでの内容もセットになっています。