【UE4】リズムに合わせて自動的にActorへイベントが発行される仕組みを作る
今回の記事では、以前ブログに公開したこちらの記事
dfkfuturelab.hatenablog.com
で作った、サウンド管理アクタを利用します。
リズムに合わせて何かをさせたい事、ありませんか
ゲーム内BGMのリズムに合わせて敵を動かしたいとか、パーティクルを出したいとか、その他もろもろ、
リズムに合わせてアクタに何かをさせたい、と思うことは結構多いと思います。
しかし、アクタ毎にテンポを測ってどうのこうの……というのは非効率ですし、タイミングが統一できません。
実は前回の記事を書いた後にらりほまさん(@rarihoma)が
やりたいことによると思うけど、UE4 + FMOD だと小節・拍のタイミングでイベント発行してくれるノードがあったりするので楽。規約をしっかり確認する必要があるというのはその通りです。
— らりほま (@rarihoma) 2016年12月9日
こんなことをツイートされてたので、
「FMODいいなあ、ミドルウェアいいなあ……でも今は使えないし……」
「じゃあ似たような仕組みを作れるかやってみよう!」
と思い立ったら実装できてしまったので、この記事を書きました。
きっかけをもらえて良かったです。
機能を作るにあたって必要なこと
さて、今回はサウンド管理アクタから様々なアクタへとイベントを発行する必要があるのですが、
ゲーム内の全てのアクタにわざわざイベントを送るとなると、あまりに非効率です。そのため、
・イベントを発行してほしいアクタがその旨をサウンド管理アクタへ自己申告する
・そのアクタたちへのリファレンスを発行して欲しい間隔(小節ごととか、4分音符ごととか)ごとに配列に登録しておく
・タイミングが来たら、配列に登録したアクタ達に該当のイベントを発行する
という処理を行えるようにする必要があります。
この時、イベントを発行して欲しいアクタはどんなアクタでも登録できるようにしてやる必要がありますので、
まずはブループリントインターフェースを用意することから初めましょう。
ブループリントインターフェースについては、alweiさんの
unrealengine.hatenablog.com
こちらの記事や、もんしょさんの
もんしょの巣穴blog [UE4] Blueprint Interfaceを使って依存度を下げる
こちらの記事を読むと分かりやすいかと思います。
アクタに持たせるブループリントインターフェースを作る
これだけです。
・MeasureUpdate(小節が変わったタイミングで発行)
・NoteUpdateHalf(2分音符のタイミングで発行)
・NoteUpdate4th(4分音符の以下略)
・NoteUpdate8th(8分音符の以下略)
・NoteUpdate16th(16分音符の以下略)
このイベント発行機能を使いたいアクタには、実装インターフェースにこのインターフェースを指定すればOKです。
サウンド管理アクタ側で準備をする
サウンド管理アクタには、このような配列の変数を作っておきます。
それぞれ、そのタイミングごとにイベントを発行して欲しいアクタをしまっておく配列です。
この中にはアクタを登録するイベントと、アクタを削除するイベントを用意する必要があります。
このように、それぞれの配列に対してイベントを作っていきます。
そして、イベントを発行するための仕組みを作ります。
このサウンド管理アクタの一番最後のマクロの中身はこうなっています。
16分音符が更新されたか→8分音符が更新されたか→4分音符が……
というように、細かい音符から更新チェックを行います。
なぜこうしているかというと、
16分音符が更新されていないときはそれより長い音符は絶対に更新されない、
というように、細かい側の音符が更新されていないことが分かったら、それより分解能が粗い音符については確認する必要がなくなるからです。
(これでほんのちょっとでも負荷が減ると思いたいのです)
そして、それぞれ音符が更新されていたらBoolean変数をtrueにして
このように、イベント発行用の関数の引数に使います。
実は今回スクリーンショットに使っているCleanUp関数は、
もともと効果音のなるタイミングを8分音符や16分音符のタイミングに補正するために用意した関数で、
「更新タイミングのフラグが立っていたら該当する音の配列に登録された効果音を鳴らして、配列をクリアする」
という機能のためにこのような名前になっています。
今回のイベント発行の仕組みは、この関数の中に、
このようにして、
4分音符更新のフラグ(引数から持ってくる)が立っていたら、
ForEachLoopで4分音符のタイミング用の配列に登録された全てのアクタへイベントを発行する
という処理を入れます。
あと、
全体図を見れば分かるのですが、関数の最後にフラグのリセットを行います。
ここまでで、サウンド管理アクタ側の準備は終わりです。
適当なアクタを作って試してみる
では、早速どうでもよさそうなアクタを一つ作ってみましょう。
サウンド管理アクタに「SoundSystem」タグをつけておけば、こうして他のアクタから検索してイベントを発行できます。
4分音符毎にイベントを送ってほしいので、AddActor4thイベントを実行し、自分自身のリファレンスを登録します。
すると、BGMの4分音符のタイミング毎に、下にあるNoteUpdate4thイベントが呼び出され、そこに書いた処理が実行されます。
このアクタは、4分音符毎に「4分音符の更新やで」って画面に表示するようになっています。
次に、別のアクタをもう一つ作ってみます。
クラス名が異なるアクタですが、同じブループリントインターフェースを実装インターフェースに指定しているので、
サウンド管理アクタは同じ処理を行うだけで、そのイベントを発行されたアクタごとが、それぞれ組み込まれた処理を行ってくれるのです。
この2つ目のアクタは、先ほどとは違って4分音符毎に「( ^ω^)おっ」って画面に表示するようになっています。
そして、この2つのアクタをレベル内の適当な場所へ配置します。
では動かしてみる
こちらで動かしてみた様子の動画を見ることが出来ます。
ちゃんと、BGMの4分音符のタイミング毎に、「4分音符の更新やで」「( ^ω^)おっ」の2つのテキストが画面に表示されました。
サウンド管理アクタに用意した4分音符毎のイベント発行処理は無事に行われ、
アクタ毎に設定されているイベントの中身を、それぞれ正しく実行している様子がわかります。
これで確認はできました。
最後に
この機能を使うことで、BGMのテンポに同期した演出を行いやすくすることが出来ると思います。
今回は勢いで作って記事を書いたこともあり、難しい部分も多かったと思いますが、
もし難解すぎるという感想が多かった場合は何らかの機会でまた、詳しく説明できたらと思います。
それでは、読んでいただきましてありがとうございました。
【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つを以前の記事でも紹介した構造体
これを使ったデータテーブルへ入れ込みます。
この時に、
・BGMのテンポ(BPM)
・そのパートの小節数(Measure)
をそれぞれ設定します。
それでは、ここまでの下準備が終わったので、「イントロ・アウトロ付きBGMループ」を実装するための処理を、
1:BGMをコンポーネントにセットする
2:パートAを再生する
3:パートAのMeasureに設定した小節数に到達したらパートAの再生を停止すると同時にパートBを再生する
4:パートBのループ再生中にループ終了の条件を満たしたらキリのいい小節数で再生を終了するように処理する
5:パートBの再生を停止すると同時にアウトロを再生する
という形で実装します。
BGMをコンポーネントにセットする
こんな感じで、それぞれのAudioComponentへ該当するサウンドをSetSoundノードでセットします。
これで、再生や停止の命令を出したり、現在の状況を監視したりすることが可能になります。
パートAの再生
このように、パートAをセットしたAudioComponentをPlayノードで再生させます。
同時に、サウンド管理アクタ内のBPMを格納する変数へパートAのBPMをセットして、
BGMを再生した時間を記録する変数にGetAudioTimeSecondsで取ってきた時間をセットします。
その後、「この小節数に到達したらBGMを切り替える」という判定を行うために変数ChangeMeasureにパートAの小節数をセットします。
さて、現在開発中のゲームで使っているサウンド管理アクタのTickでは、このような処理が回っています。
ChangeMeasure変数の出番がどこか、分かってきた方も多いのではないかと思います。
パートAからパートBへの切り替え
Tick毎にGetMeasure関数で小節数を確認しているので、
その戻り値がChangeMeasure変数へ到達したタイミングのTickで切り替え処理をする必要があります。
ChangeState関数では様々なステージ状況に合わせて再生や停止などの処理が分岐しているので、全てをお見せすると逆にわけがわからなくなる危険性が有ります。
ですので、パート切り替えの部分だけをお見せします。
パートAをStopさせて、そのままパートBをPlayします。パートBのBPMをサウンド管理アクタの変数BPMへセットします。
パートBは条件を満たすまで無限ループ(ここではボスを倒すまで、が条件になってます)なので、
無限ループ状態であるフラグをBoolean変数か何かで立ててやる必要があります。
ここではボス戦であることを示すBoss変数をtrueにしています。
もちろん、BGM再生時間のリセットも行います。
ループからの脱出
さてこのボス戦モードの最中は、
この「ボス戦か?」のブランチによってBGM切り替え処理の判定がスキップされます。
なので、いつまでたってもパートBのループ再生が続きますが……
ボスを倒したら、もちろんループから脱出する必要があります。
そしてもちろん、ループからの脱出も綺麗に出来なければいみがありません。
今回はボスが倒された時に、BossKill関数を呼び出すことにしました。その内部の実装はこのようになっています。
4分の4拍子で作られたBGMであれば、だいたいは4小節ごと、8小節ごと、16小節ごと、辺りが楽曲構成の1パターンとなっています。
そのため、そのタイミングでアウトロへ切り替えてやれば、スムーズにBGMが切り替わったように聞こえてくれるわけです。
今回のスクリーンショットでは、8小節ごとをパターンの切れ目として捉え、
現在の小節数を取り出す→8小節ごとの区切りになるであろう次の小節数をChangeMeasure変数へセットする→ボスモードのフラグを折る
という処理を行っています。もし8小節ごとが長いのであれば、8を4にするなど、工夫してみるといいと思います。
こうして、ボスモードのフラグが折れたため、ChangeMeasure変数にセットされた小節数に到達したタイミングでBGMの切り替えイベントへ飛びます。
Bパートからアウトロへ
アウトロは流しきりなので、アウトロの小節数をChangeMeasureにセットすることと、ボスモードのフラグをいじらないこと以外は、だいたいAパートからBパートへの切り替えと一緒です。
まとめ
このように、ループしないパートはその長さを設定してやって、ループするパートからはキリのいいタイミングで切り替わるように処理を組むことで、
イントロとアウトロを持ったループBGMを実装することが出来ました。
もっと効率のいい便利な実装方法も多分あるんだと思うんですが、現時点で自分が実装しているのはこのような処理であります。
最後まで読んで頂き、ありがとうございました。
来週のUE4アドカレで載せる予定の記事もまたテンポに合わせたナンタラカンタラ的な話なので、どうぞ宜しくお願いします。
【UE4】スパゲッティを綺麗なBPにする
手当たり次第にブループリントを作っていたら、いつのまにか見た目がスパゲッティになってしまっている事、ありませんか?
私はよく、ひらめきが消えないうちに……と急いでどんどこノードを追加していく癖があったので、
このような美味しそうな麺類が出来上がることが多かったのですが……
こうして技術ブログをちまちま書いたり、スライドを作ったりして説明する機会を頂いたりする時に、
「このままスクリーンショットを見せてもなんにも伝わらないし、そもそも恥ずかしい!!」
となってしまうので、ブループリントを綺麗に整理してみることにしました。
まず参考にするべきスライド
ブループリントを綺麗にするにあたって、まず参考にすると良いのがこちらの、
ヒストリアさんのスライドです。
ここにある通り、
Q:ブループリントを書くにあたって唯一にして絶対の正義とは? A:見やすさ
であることを自分も実感しました……
まず、反省点
修正を始める前に、まずこのブループリントには大問題が有りました。
一時的に実装していたデバッグ用の機能などが、
「結構頑張って作ったしまた使うかもしれないから消すのはもったいない」
という理由でノードをつながれないまま残ってしまっていたのです。
なのでまず最初に、該当する部分のデバッグ用ノードなどは、
それぞれマクロに折りたたみました。
これで将来的に必要になったとき、マクロを置いて、そこにノードを繋ぐだけで確認ができるようになりますね。
左から右、上から下
次に配置の変更です。
先ほどのスライドを2つのルールに従う形で変更します。
・処理の流れは左から右へ
・分岐が発生したら上から下へ
これにしたがってノードを動かしていると、途中で混線してしまうこともあるので
「Rerouteノード」を使って形を綺麗にします。
その結果がこちらです。
こ……これはっ!!!
かなり見やすくなっているっ!!!!
処理するブロックごとにコメントを書く
ここまで修正できるとかなりテンションが上ってくるので、
ここからはさらにブロックごとにコメントを書いてわかりやすくします。
「ここではこんなことをしていますよ」という一連の流れをドラッグで囲んで、Cキーを押してコメントを設置します。
右クリックメニューなら「選択対象からコメントを作成する」をクリックします。
こうすることで、どこで何をしているかが非常にわかりやすくなります。
それと同時に、コメントを付けるともう一つ素敵な効果が生まれます。
それは、「ひとつの処理ブロックの長さ・複雑さがわかる」というところです。
外伝:長い処理をマクロにまとめる
たとえコメントをちゃんとつけたとしても、
(これは別のブループリントを使った例です)
これがどーんとイベントグラフの中に鎮座していたら、それはそれで見にくくなってしまいます。
なので、このような処理が複雑になっているブロックもマクロにします。
(先ほどのブループリントです)
シンプルな見た目になるし、マクロの中身をいじる=その機能に手を加える事になるので、自分がどこをいじっているのかもはっきりします。
完成
ということで、修正とコメント作成の結果
こうなりました。
なんということでしょう……
Before
After
まとめ
・ブループリントを綺麗にして可読性を上げると、開発が捗る
・綺麗なブループリントは他人に見せる時に恥ずかしくない
・ブループリントを綺麗にするとテンションがすごく上がる
というわけで、ブループリントの整形はいい事ずくめなのでおすすめです。
ちなみに今回修正したブループリントは、次回の記事
「UE4でイントロ・アウトロ付きBGMループを実装する」(仮題)
で登場する予定です。よろしくお願いします。
【UE4】BGMの現在の小節数・拍数をチェックできるようにする
先月、「UE4Meetup名古屋」「どテックNAGOYA」と2つのイベントに立て続けに登壇させて頂き、
技術ブログを更新せねばと思い立った大福です、お久しぶりです。
ちょっとした宣伝でございますが、このようなゲームを冬コミに向けて開発中です。
dfkfuturelab.wixsite.com
今回のブログ記事は、このゲームでも使った技術について書いています。
注意:この記事に書かれているやり方は、テンポが途中で変わらない&最初に無音などが入っていないBGM用ですのでご了承ください。
BGMに合わせた演出をするために
音楽ゲームなどでよくある「BGMのテンポに合わせた演出」や、
Rezなどに代表される「一定の拍数のタイミングで音が鳴るように補正する」などの技術は、
ゲーム内でテンポの管理が出来ないと難しいです。
ですから、まずはゲーム内でBGMにテンポの情報を付随させておく必要があります。
例として、このような構造体を作ります。
Audio:BGMファイル
BPM:BGMのテンポ
↑この2つがあればだいたい大丈夫です
Measure:BGMが何小節あるか(今回は使わないので忘れてください)
IsUseSmoothFade:フェード処理を使うか(今回は使わないので忘れてください)
で、サウンドを鳴らすアクタにはこのように
AudioComponentをつけておきます。(画像では5つ用意してありますが、普通は1つでいいです)
これがないとサウンドを鳴らした後に停止やらなんやらがめんどくさいことになります……
あと、float型の変数「BPM」と「BGMStartTime」を用意しておきます。
AudioComponentからサウンドを鳴らす
サウンドを鳴らすには、まずAudioComponentにSetSoundノードで構造体から持ってきたサウンドを突っ込んでやって
そのタイミングで、構造体からBPMも引っ張ってきましょう。
そしてこのサウンドをPlayノードで再生すると同時に、
GetAudioTimeSecondsノードで時間を取ってきて、BGMStartTimeに突っ込みます。
これで、
・BGMのテンポ
・BGMが再生された時間
が、アクタ内で確認できるようになりました。
これを使って、小節数や拍数をゲットできる関数「GetMeasure」を作ってみます。
GetMeasure関数を作る
ここでBGMのテンポ、BPMとはなんぞやという話になるのですが、
BPM(Beat Per Minute)……1分間に4分音符が何回鳴らされるか
というものなのです。つまり、
60.0秒÷BPM=4分音符の長さ ここから、
60.0秒÷{BPM×(n/4)}=n分音符の長さ となります。
これを利用して、GetMeasure関数では、
(現在の時間 - BGMStartTime)÷n分音符の長さ
の結果を返します。このnが1のとき、GetMeasure関数は小節数を返すのです。
私の場合はこのようなBPを組みました。
これでTickイベントにGetMeasureの引数に1,4,8,16をそれぞれ入れたものを繋ぎ、出力されたものと現在のBPMをPrintしてチェックしてみます。
毎フレームごとにプリントされているので見づらいですが、現在の小節数と拍数を計算できています。
これを使って、特定の拍数・小節数でイベントを起こしたりすることができるようになるはずです。
【Unityエディタ拡張】効果音のリストをエディタ拡張でわかりやすくしてみた
夏休みなのでやっと色々記事が書けそうです。
効果音のリストはなんか見づらいことが多い
よくオブジェクトにひっつける効果音のリストがAudioClipの配列になってたりすると、どこにどの音を入れとくんだっけ…みたいな感じになりませんか?
というわけで、エディタ拡張で作ってみることにしました。
今回は
Pop
Attack
Damage
Dead
の四種類の効果音を設定できるようになっていますが、AudioList.cs内のAudioName内を書き換えれば自由に変更できます。
こんな感じで、設定したい効果音の種類を選んで編集できます。
暫定的にこんな処理も入れてあります。
//__/__/__/__/__/__/__/__/__/__/__/__/__/__/__/__/__/__/__/__/__/__/__/__/__/__/ // // ▼ File AudioList.cs // // ▼ Brief オブジェクトで使用するAudioClipリストを自動的にAudioManagerに送る // // //__/__/__/__/__/__/__/__/__/__/__/__/__/__/__/__/__/__/__/__/__/__/__/__/__/__/ using UnityEngine; using System.Collections; public class AudioList : MonoBehaviour { /// <summary> /// AudioClipのenum /// </summary> public enum AudioName { Pop, Attack, Damage, Dead, End_of_Enum } /// <summary> /// エディタ拡張用 /// </summary> public AudioName audioName; public AudioClip[] _SoundEffectList = new AudioClip[(int)AudioName.End_of_Enum]; }
で、これのエディタを下記のように拡張してみます。
//__/__/__/__/__/__/__/__/__/__/__/__/__/__/__/__/__/__/__/__/__/__/__/__/__/__/ // // ▼ File AudioListEditor.cs // // ▼ Brief AudioList用エディタ拡張 // // //__/__/__/__/__/__/__/__/__/__/__/__/__/__/__/__/__/__/__/__/__/__/__/__/__/__/ using UnityEngine; using System.Collections; using UnityEditor; [CustomEditor(typeof(AudioList))] public class AudioListEditior : Editor { public override void OnInspectorGUI() { AudioList obj = target as AudioList; //入力された効果音の種類を設定する obj.audioName = (AudioList.AudioName)EditorGUILayout.EnumPopup("設定する効果音の種類", obj.audioName); //EOEの場合 if (obj.audioName == AudioList.AudioName.End_of_Enum) { EditorGUILayout.HelpBox("非設定項目です。", MessageType.Info, true); } else { obj._SoundEffectList[(int)obj.audioName] = EditorGUILayout.ObjectField(obj.audioName.ToString(), obj._SoundEffectList[(int)obj.audioName], typeof(AudioClip), true) as AudioClip; } } }
【C++/DirectX】リアルタイムで音声波形をテクスチャに書き込んで表示してみる:その1 必要な作業を割り出してみる
最初の記事がこれってどうなの?と関係各所から突っ込まれそうではありますが…
「音声波形をリアルタイムでテクスチャに書き込んで表示したい」
って書くと何言ってるのかよくわからない感じありますよね。
手っ取り早く書くと音楽プレイヤーとか最近のニコ動のボカロ曲の動画とかでよく表示されてるスペアナ(音域ごとの音量を示す棒グラフだったり折れ線グラフだったりがみょんみょん動いてるアレ)を表示させてみようというやつです。
実はこれ、冬コミで出した「KAMIKAZE BEAT TUBE」というゲームの背景で使っている機能なんです。
つまり、ある程度のスペックを持ったパソコン上で動かすことを考えるなら、リアルタイムで音声データを変換→テクスチャへ書き込むという流れは(私みたいなへっぽこ学生プログラマでも)可能なのです。
最初の記事では、この「音声波形をリアルタイムでテクスチャに書き込んで表示したい」という作業を細かく分けて、どの段階でどんな処理をするのかから書いていきたいと思います。
まず音声波形を表示するわけだから、音が必要ですね。
となると、音声ファイルを読み込まなければいけません。
次に、その音声ファイルを読み取ってから、鳴らすだけではなくて視覚化出来るような処理をしなければいけません。
そんでもって、視覚化出来るようなデータが出来上がったら、それをテクスチャに焼き付けなければいけませんから、まずテクスチャを確保して、それに書き込む線やら図形やらの事前処理をしなければいけません。
そこまでやってようやく、音声波形をテクスチャに書き込むことが出来るのです。
というわけで、
その2:音声ファイルの読み込み
その3:音声データを変換する
その4:テクスチャに書き込むとは
その5:いざ書き込み
その6:まとめ
ぐらいの長さで公開していけたらと思っております。
何卒よろしくお願いいたします。