読者です 読者をやめる 読者になる 読者になる

大福未来研究所

大福フューチャーラボのなんか色々なアレ。プログラミング(C++/DirectX/Unity/UE4あたりやりたい)とか音楽とかの。

【UE4】リズムに合わせて自動的にActorへイベントが発行される仕組みを作る

今回の記事では、以前ブログに公開したこちらの記事
dfkfuturelab.hatenablog.com
で作った、サウンド管理アクタを利用します。

リズムに合わせて何かをさせたい事、ありませんか

ゲーム内BGMのリズムに合わせて敵を動かしたいとか、パーティクルを出したいとか、その他もろもろ、
リズムに合わせてアクタに何かをさせたい、と思うことは結構多いと思います。
しかし、アクタ毎にテンポを測ってどうのこうの……というのは非効率ですし、タイミングが統一できません。

実は前回の記事を書いた後にらりほまさん(@rarihoma)が


こんなことをツイートされてたので、

「FMODいいなあ、ミドルウェアいいなあ……でも今は使えないし……」




「じゃあ似たような仕組みを作れるかやってみよう!」

と思い立ったら実装できてしまったので、この記事を書きました。
きっかけをもらえて良かったです。

機能を作るにあたって必要なこと

さて、今回はサウンド管理アクタから様々なアクタへとイベントを発行する必要があるのですが、
ゲーム内の全てのアクタにわざわざイベントを送るとなると、あまりに非効率です。そのため、


・イベントを発行してほしいアクタがその旨をサウンド管理アクタへ自己申告する
・そのアクタたちへのリファレンスを発行して欲しい間隔(小節ごととか、4分音符ごととか)ごとに配列に登録しておく
・タイミングが来たら、配列に登録したアクタ達に該当のイベントを発行する


という処理を行えるようにする必要があります。
この時、イベントを発行して欲しいアクタはどんなアクタでも登録できるようにしてやる必要がありますので、
まずはブループリントインターフェースを用意することから初めましょう。

ブループリントインターフェースについては、alweiさんの
unrealengine.hatenablog.com
こちらの記事や、もんしょさんの
もんしょの巣穴blog [UE4] Blueprint Interfaceを使って依存度を下げる
こちらの記事を読むと分かりやすいかと思います。


アクタに持たせるブループリントインターフェースを作る

f:id:dfk_ohnuma:20161209222044p:plain
これだけです。

・MeasureUpdate(小節が変わったタイミングで発行)
・NoteUpdateHalf(2分音符のタイミングで発行)
・NoteUpdate4th(4分音符の以下略)
・NoteUpdate8th(8分音符の以下略)
・NoteUpdate16th(16分音符の以下略)

このイベント発行機能を使いたいアクタには、実装インターフェースにこのインターフェースを指定すればOKです。

サウンド管理アクタ側で準備をする

サウンド管理アクタには、このような配列の変数を作っておきます。
f:id:dfk_ohnuma:20161209222609p:plain
それぞれ、そのタイミングごとにイベントを発行して欲しいアクタをしまっておく配列です。
f:id:dfk_ohnuma:20161209222655p:plain

この中にはアクタを登録するイベントと、アクタを削除するイベントを用意する必要があります。
f:id:dfk_ohnuma:20161209222759p:plain
このように、それぞれの配列に対してイベントを作っていきます。

そして、イベントを発行するための仕組みを作ります。
f:id:dfk_ohnuma:20161209160145p:plain
このサウンド管理アクタの一番最後のマクロの中身はこうなっています。

f:id:dfk_ohnuma:20161209223212p:plain
16分音符が更新されたか→8分音符が更新されたか→4分音符が……
というように、細かい音符から更新チェックを行います。
なぜこうしているかというと、
16分音符が更新されていないときはそれより長い音符は絶対に更新されない
というように、細かい側の音符が更新されていないことが分かったら、それより分解能が粗い音符については確認する必要がなくなるからです。
(これでほんのちょっとでも負荷が減ると思いたいのです)

そして、それぞれ音符が更新されていたらBoolean変数をtrueにして
f:id:dfk_ohnuma:20161209223741p:plain
このように、イベント発行用の関数の引数に使います。

実は今回スクリーンショットに使っているCleanUp関数は、
もともと効果音のなるタイミングを8分音符や16分音符のタイミングに補正するために用意した関数で、
「更新タイミングのフラグが立っていたら該当する音の配列に登録された効果音を鳴らして、配列をクリアする」
という機能のためにこのような名前になっています。
今回のイベント発行の仕組みは、この関数の中に、
f:id:dfk_ohnuma:20161209224134p:plain
このようにして、
4分音符更新のフラグ(引数から持ってくる)が立っていたら、
ForEachLoopで4分音符のタイミング用の配列に登録された全てのアクタへイベントを発行する

という処理を入れます。

あと、
f:id:dfk_ohnuma:20161209224408p:plain
全体図を見れば分かるのですが、関数の最後にフラグのリセットを行います。

ここまでで、サウンド管理アクタ側の準備は終わりです。

適当なアクタを作って試してみる

では、早速どうでもよさそうなアクタを一つ作ってみましょう。
f:id:dfk_ohnuma:20161209224515p:plain
サウンド管理アクタに「SoundSystem」タグをつけておけば、こうして他のアクタから検索してイベントを発行できます。
4分音符毎にイベントを送ってほしいので、AddActor4thイベントを実行し、自分自身のリファレンスを登録します。
すると、BGMの4分音符のタイミング毎に、下にあるNoteUpdate4thイベントが呼び出され、そこに書いた処理が実行されます。
このアクタは、4分音符毎に「4分音符の更新やで」って画面に表示するようになっています。

次に、別のアクタをもう一つ作ってみます。
f:id:dfk_ohnuma:20161209224807p:plain
クラス名が異なるアクタですが、同じブループリントインターフェースを実装インターフェースに指定しているので、
サウンド管理アクタは同じ処理を行うだけで、そのイベントを発行されたアクタごとが、それぞれ組み込まれた処理を行ってくれるのです。
この2つ目のアクタは、先ほどとは違って4分音符毎に「( ^ω^)おっ」って画面に表示するようになっています。

そして、この2つのアクタをレベル内の適当な場所へ配置します。

では動かしてみる


こちらで動かしてみた様子の動画を見ることが出来ます。
ちゃんと、BGMの4分音符のタイミング毎に、「4分音符の更新やで」「( ^ω^)おっ」の2つのテキストが画面に表示されました。
サウンド管理アクタに用意した4分音符毎のイベント発行処理は無事に行われ、
アクタ毎に設定されているイベントの中身を、それぞれ正しく実行している様子がわかります。

これで確認はできました。

最後に

この機能を使うことで、BGMのテンポに同期した演出を行いやすくすることが出来ると思います。
今回は勢いで作って記事を書いたこともあり、難しい部分も多かったと思いますが、
もし難解すぎるという感想が多かった場合は何らかの機会でまた、詳しく説明できたらと思います。
それでは、読んでいただきましてありがとうございました。