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

大福未来研究所

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

【UE4】UE4でイントロ・アウトロ付きBGMループを実装する

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


まずは普通のBGMの話

UE4でミドルウェアを使わずにBGMを再生する場合、
UE4で普通にステージ用のBGMを鳴らそうとすると、大体の場合は

「BGM.wavをインポートしてloopingにチェック入れて(or入れないで)鳴らして終わり」

というあっさりした処理で終わるのですが、その場合に起きることとして、

・ループしない場合、BGMが鳴り終わってもゲームが終わらなかったら無音になってしまう
・ループする場合、曲の終わりからまた最初へ戻るので、起承転結がしっかりした楽曲だと繰り返してるのがモロバレになってしまう

などの事態が想定されます。そのため、大体の場合は「展開の少ない短いループ用BGM」をループ再生することで済ませている方も多いのではないかと思います。


しかし、それでは音楽が単調になりがちです。
ゲームを盛り上げる要素の一つとして、やはりステージが始まる時にBGMのイントロから始まり、ステージが終わるときにはアウトロが流れるという、展開を持ったBGMの存在は不可欠といえます。
ですので、それが実現できるように実装に挑戦してみます。

BGMを分割する

BGM「Stage1.wav」を例とします。
そのStage1.wavは「イントロ」と「メインでループしたい部分」と「アウトロ」を持つBGMですが、
そのまま流してしまったら「1回流しきり」か「最初から最後までをループし続ける」かのどちらかにしかなりません。
ですので、Stage1.wavを要素ごとに分割してみましょう。作曲ソフトなどで綺麗に切り分けてみてください。


Stage1-PartA.wav:イントロ(ループしない)
Stage1-PartB.wav:メイン(ループする)
Stage1-Outro.wav:アウトロ(ループしない)


この3つに切り分けたあとに、ループさせたい「Stage1-PartB.wav」のLoopingにチェックを入れ、残りの2つはLoopingのチェックを外します。
そして、この3つを以前の記事でも紹介した構造体
f:id:dfk_ohnuma:20161202110312p:plain
これを使ったデータテーブルへ入れ込みます。
f:id:dfk_ohnuma:20161209151221p:plain
この時に、
・BGMのテンポ(BPM
・そのパートの小節数(Measure)
をそれぞれ設定します。


それでは、ここまでの下準備が終わったので、「イントロ・アウトロ付きBGMループ」を実装するための処理を、

1:BGMをコンポーネントにセットする
2:パートAを再生する
3:パートAのMeasureに設定した小節数に到達したらパートAの再生を停止すると同時にパートBを再生する
4:パートBのループ再生中にループ終了の条件を満たしたらキリのいい小節数で再生を終了するように処理する
5:パートBの再生を停止すると同時にアウトロを再生する

という形で実装します。

BGMをコンポーネントにセットする

f:id:dfk_ohnuma:20161209153926p:plain
こんな感じで、それぞれのAudioComponentへ該当するサウンドをSetSoundノードでセットします。
これで、再生や停止の命令を出したり、現在の状況を監視したりすることが可能になります。

パートAの再生

f:id:dfk_ohnuma:20161209154447p:plain
このように、パートAをセットしたAudioComponentをPlayノードで再生させます。
同時に、サウンド管理アクタ内のBPMを格納する変数へパートAのBPMをセットして、
BGMを再生した時間を記録する変数にGetAudioTimeSecondsで取ってきた時間をセットします。
その後、「この小節数に到達したらBGMを切り替える」という判定を行うために変数ChangeMeasureにパートAの小節数をセットします。


さて、現在開発中のゲームで使っているサウンド管理アクタのTickでは、このような処理が回っています。
f:id:dfk_ohnuma:20161209160145p:plain
ChangeMeasure変数の出番がどこか、分かってきた方も多いのではないかと思います。

パートAからパートBへの切り替え

f:id:dfk_ohnuma:20161209160506p:plain
Tick毎にGetMeasure関数で小節数を確認しているので、
その戻り値がChangeMeasure変数へ到達したタイミングのTickで切り替え処理をする必要があります。
ChangeState関数では様々なステージ状況に合わせて再生や停止などの処理が分岐しているので、全てをお見せすると逆にわけがわからなくなる危険性が有ります。
ですので、パート切り替えの部分だけをお見せします。
f:id:dfk_ohnuma:20161209161229p:plain
パートAをStopさせて、そのままパートBをPlayします。パートBのBPMをサウンド管理アクタの変数BPMへセットします。
パートBは条件を満たすまで無限ループ(ここではボスを倒すまで、が条件になってます)なので、
無限ループ状態であるフラグをBoolean変数か何かで立ててやる必要があります。
ここではボス戦であることを示すBoss変数をtrueにしています。
f:id:dfk_ohnuma:20161209161241p:plain
もちろん、BGM再生時間のリセットも行います。

ループからの脱出

さてこのボス戦モードの最中は、
f:id:dfk_ohnuma:20161209160145p:plain
この「ボス戦か?」のブランチによってBGM切り替え処理の判定がスキップされます。
なので、いつまでたってもパートBのループ再生が続きますが……
ボスを倒したら、もちろんループから脱出する必要があります。
そしてもちろん、ループからの脱出も綺麗に出来なければいみがありません。


今回はボスが倒された時に、BossKill関数を呼び出すことにしました。その内部の実装はこのようになっています。
f:id:dfk_ohnuma:20161209162051p:plain
4分の4拍子で作られたBGMであれば、だいたいは4小節ごと、8小節ごと、16小節ごと、辺りが楽曲構成の1パターンとなっています。
そのため、そのタイミングでアウトロへ切り替えてやれば、スムーズにBGMが切り替わったように聞こえてくれるわけです。
今回のスクリーンショットでは、8小節ごとをパターンの切れ目として捉え、

現在の小節数を取り出す→8小節ごとの区切りになるであろう次の小節数をChangeMeasure変数へセットする→ボスモードのフラグを折る

という処理を行っています。もし8小節ごとが長いのであれば、8を4にするなど、工夫してみるといいと思います。

こうして、ボスモードのフラグが折れたため、ChangeMeasure変数にセットされた小節数に到達したタイミングでBGMの切り替えイベントへ飛びます。

Bパートからアウトロへ

アウトロは流しきりなので、アウトロの小節数をChangeMeasureにセットすることと、ボスモードのフラグをいじらないこと以外は、だいたいAパートからBパートへの切り替えと一緒です。


まとめ

このように、ループしないパートはその長さを設定してやって、ループするパートからはキリのいいタイミングで切り替わるように処理を組むことで、
イントロとアウトロを持ったループBGMを実装することが出来ました。
もっと効率のいい便利な実装方法も多分あるんだと思うんですが、現時点で自分が実装しているのはこのような処理であります。
最後まで読んで頂き、ありがとうございました。

来週のUE4アドカレで載せる予定の記事もまたテンポに合わせたナンタラカンタラ的な話なので、どうぞ宜しくお願いします。