改訂バージョン: Unreal Engine 4.21 |
今回は、スプライン移動中に障害物が横切った際、ぶつからないように一時停止する機能を作りたいと思います。
また、おまけとしてイベントディスパッチャーを用いてちょっとした機能を追加しますので、イベントディスパッチャーの使い方にいまいちピンとこない方も参考にしていただければ幸いです。
まず、スプライン移動については過去記事を参考にしてください。
それでは早速スプライン移動用コンポーネントに機能を追加していきたいと思います。
まず実装の概要としましては、スプライン移動するアクター(今回はゲストとしてネコちゃんに来てもらいました)に衝突検知用のコリジョンを追加し、スプライン移動側でそのコリジョンが何かにヒットしたら移動を一時停止し、ヒットが抜けたら移動を再開するといった感じです。
また、おまけの機能として、一時停止した際にネコちゃんがしゃべるようにします。
それでは早速実装方法のご紹介です。
まずはSplineMoveComponentに「PrimitiveComponent」型の変数を追加します。PrimitiveComponentはコリジョン用コンポーネントのボックスやスフィア、シリンダーの基底クラスになっているため、コリジョンの形状に関係なくそれらを設定できるようになります。

続いて外部から受け取ったPrimitiveComponentを先ほど追加した変数に保存しておき、Collision側の各イベントを受け取るカスタムイベント「SetCollisionEvent」を追加します。
まずはコリジョンのヒット検知時に呼ばれるBeginOverlapイベントを紐づけます。

こうすることで、バインドする際に追加したカスタムイベント「ObstacleBeginOverlap」が、外部から受け取ったCollisionのBeginOverlap時に全く同じタイミングで一緒に呼ばれるようになります。
ObstacleBeginOverlap内で設定しているCollisionStopという変数は、事前にBool型の変数としてSplineMoveComponentに追加しておいてください。このフラグが立っている時(すでに一度CollisionのBeginOverlapが呼ばれている)は、何もしないというのが後のポイントです。
続いてBeginOverlapで検知したアクターがコリジョンの外に出た時に呼ばれるEndOverlapのイベントをバインドします。
ここでも追加したカスタムイベントの「ObstacleEndOverlap」は、CollisionのEndOverlap時に同時に呼ばれるので、その際にGetOverlappingActorsでCollisionの中に他にアクターがいないかチェックして、誰もいなければCollisionStopをFalseに設定します。
これであとはCollisionStopのフラグをチェックすれば、障害物があるかどうかが判別できるようになりました。
※Bind関連の処理を書く際は、バインドするときのタイミングと、バインド先のカスタムイベントとで、イベントが呼ばれるタイミングが異なることに注意してください。
さて、これでバインドによるイベントの紐づけはとりあえず完了しましたが、途中でCollisionが切り替わった際に安全にイベントも切り替わるように処理を追加しておきましょう。イベント名はとりあえず「SetCollisionEventUnBind」とします。
現在のCollisionが設定されている場合に、バインドして紐づけていたイベントはもう必要なくなるため、Unbind処理を呼んで紐づけを切ります。

このイベントをSetCollisionEventの中に入れてしまえば、Collisionの設定時に、自動で以前のイベントがアンバインドされるはずです。
最後はTick内でCollisioStopフラグを監視すればCollision内に障害物がある場合にスプライン移動は停止し、障害物がすべて無くなれば移動を再開できるようになります。

ちなみにこのTickの処理は、スプライン移動がポーズ中の場合は以前の実装によりTickイベント自体が呼ばれなくなっているはずなので、障害物による停止とポーズによる停止がちぐはぐになることはありません。障害物で止まっている最中にもし別の要因で一時停止になった場合でも、移動の再開回りで問題は発生しないはずです。
最後にレベルブループリントでネコちゃんにSplineMoveComponetを追加してやり、そのあとに参照したいネコちゃんのコリジョンを設定してあげれば完成です。

おまけ
ゲームを作っていると、ネコちゃんが障害物に邪魔された瞬間と、障害物がなくなった瞬間のイベントをSplineMoveComponet以外が知りたい場合など出てくると思います。例えば障害物に邪魔された瞬間にネコちゃんや別のキャラがそれに連動して違うモーションを再生したり、何かしらのアクションを起こしたりなどです。
そんな時に便利なのがイベントディスパッチャーです。
先ほど紹介したPrimitiveComponentのBeginOverlap、EndOverlapのバインドもイベントディスパッチャーの機能の一つです。
それでは早速実装してみましょう。
まずは障害物によって移動が停止するタイミングに呼ばれるイベントと、障害物がなくなって移動が再開するタイミングに呼ばれるの二つのイベントディスパッチャーを追加します。名前はそれぞれ
- ObstacleOnEvent
- ObstacleOffEvent
とします。

イベントの引数に、障害物が何のアクターかわかるようにとりあえずアクター型の追加しておきましょう
つぎに追加したイベントディスパッチャーをそれぞれのタイミングで呼び出します。
ObstacleOnEventはCollisionStopがTrueになったときに、ObstacleOffEventはCollisionStopがFalseになったタイミングです。


これで準備は完了です。
この実装により他のブループリントから、ObstacleOnEvent、ObstacleOffEventをBeginOverlap同様にバインドしてあげれば、それぞれのタイミングで処理が呼ばれるようになります。
試しにレベルブループリントでネコちゃんに追加Widgetを表示したり消したりする処理を追加してみます。

バインドの仕方はBeginOverlapの時と同じです。イベントピンをドラッグ&ドロップしてカスタムイベントを追加すれば、自動的に引数が入っているイベントが追加されます。あとは障害物に邪魔されて移動が停止した瞬間と再開した瞬間で実行したい処理を書けばその通りに動くはずです。
実行結果を動画にしてみました。