【UE4】BGMのテンポに合わせた命令実行機能を作る
この記事は、裏Unreal Engine 4 (UE4) Advent Calendar 2016への参加14日目の記事です。
qiita.com
昨日はyoshikataさんによる
UE4.14・AndroidでVRの実機プレイまでの解説 - Qiitaでした。
曲に合わせて演出したい
例えばゲームで敵が出現するタイミングだったり、演出が起きるタイミングだったり、「ステージ開始から何秒でなにをどうしてこうなって……」と、
画面内でシーケンシャルな動作・演出を行いたいときというのは往々にしてあると思います。
そういう時、単にゲーム内でのことを考えるなら時間で考えれば良いのですが、
これが楽曲のPVを作ったり、音楽に合わせたゲーム内での演出だったらどうでしょう。
何秒で、というよりは「何小節目の何拍目に何をどうしてこうなって」という考え方のほうが、捗りやすいと思います。
ですので、今回の記事ではその仕組みを実装していきます。
(注意:この記事で実装する方法では、楽曲のテンポが一定であることが必要となります。)
また、今回は文字列をいじることも多いのですが、alweiさんの
unrealengine.hatenablog.com
こちらの記事がとても参考になりました。この場を借りて御礼申し上げます。
必要な機能を考える
さて、楽曲にそって処理を行う仕組みを実装するにあたって、必要な機能を考えてみます。
・スクリプトを書き込んだCSVをインポートして、データテーブルとして読み出す
・テンポと小節数、拍数を時間に変換する
・構造体を作り、時間と処理を紐付けして配列にして格納して時間に応じて実行
という感じになると思うのですが、ここで一つ大事になるのは
楽曲に合わせた演出を行う場合、どれだけ細かい音符や珍しい分解能の音符でも対応できる必要がある、ということです。
たとえば楽曲の”キメ”の部分は6分音符になるような楽曲だと、一番細かくても16分音符でしか表現できない仕組みだった場合、
どうしても近似値となる音符で代用する必要が出てきます。その場合、音楽に演出や動きが微妙に合っていないという状況が発生し、
せっかくのテンポに合わせた命令実行という気持ちよさを生み出すための機能が台無しになってしまうことになります。
そのため、今回参考にしたのはBMS(Be-Music Script)の譜面スクリプトの考え方です。
hitkey.nekokan.dyndns.info
これは、PC上で動作する音楽ゲーム「BMS」における譜面スクリプトのフォーマットで、説明するととても長くなるのでお時間のあるときにでもリンク先を読んでみると良いと思います。
このフォーマットの譜面部分は1行で1小節を表記し、
「#WAV01:01」
とある場合、1小節目の頭に音符01が一つ置かれる形となります。(全音符)
「#WAV01:01010101」
とある場合、1小節目の頭から4分音符の間隔ごとに音符01が4つ置かれる形となります。
また、休符は00で表現します。
このように、記述した音符・休符の数に応じてプログラム側で自動的に小節の分割数を決めることが出来るため、
とても自由度の高いスクリプト記述が行えます。
今回の記事で作るスクリプトのシーケンス部分は、このシステムを取り入れています。
スクリプトを作る
まず最初に、構造体を作りましょう。
中身はこれだけです。
そして、これに対応したCSVファイルを作り、ちょっとだけ記述してみました。
私は今回、楽曲ではなくムービーそのものを流すために作ってみたので、最初が「Movie」と書かれていますが、
音楽のために作る場合はSoundとかBGMとかで良いと思います。
今回のスクリプトの仕様としては、
"MovieX":X番目のムービーを指定する(今回は1つしか使わないので0番目だけしか指定してませんが、複数使いたい時は増やせます。)
"BPM":テンポを指定する。
"seqX:Y":X小節目の意味。右側には処理のIDをBMS譜面のような形式で書き込む。Yは同時に2つ以上の処理を行いたい場合などに、seq1:0とseq1:1のように、Yの数字を増やして使う。
となっています。
3小節目は02と03が8分音符のタイミングで交互に、4小節目は16分音符のタイミングで交互に繰り返されているのがわかりますね。
1曲まるまる作るならもっと長いスクリプトファイルになると思います。
スクリプトのために下準備をする
次に、UE4側で下準備をします。
まずはムービー(orサウンド)をスクリプトから指定できるように、文字列に紐付けしたデータテーブルを作っておきます。
そして、スクリプトを読み込んだときに、どの時間にどの処理を行うのかを受け取らなければいけないので、処理用の構造体を作ります。
Time:スクリプトを読み込んで計算された、メディア再生からの経過時間
OperationNumber:処理内容ID
スクリプトを読み込む部分を作る
さて、ここから本番です。まずは読み込むためのBPを作ります。
ちなみに私の作ったBPでは、こんな感じの変数リストになってました。
Media:メディアプレイヤーを予め入れておく配列。たぶんサウンドでやるならAudioComponentか何かになってる
BPM:テンポを入れておく変数
Operation:さっき作った処理用の構造体
forSort:ソートに使ってました
MediaStartTime:再生開始時間を入れておく変数
SequenceStarted:処理が始まっているかどうか
文字列からムービーやらテンポやら処理やら、どれについて書かれた行なのかを判断する必要があります。
うーん、かなり長いですね。ということなので、1つずつ見ていきましょう。
まずはムービーのロード部分です。
データテーブル内のMovieXを検索し、そこに書かれている文字列に対応したメディアをメディアプレイヤーへセットします。
これを楽曲で行う場合は、おそらくSoundXを検索した後、そこに書かれている文字列に対応したサウンドをAudioComponentへセットする形になると思います。
次にBPM設定の部分です。
これは普通ですね。
ではシーケンス部分の読み込み処理を見ていきます。
全体図はこんな感じです。
1行ずつ読み込んで文字列の分解を行い、その結果をOperation配列へ登録。
それが全部終わったら、Timeが短い順にソートする。
という流れになっています。
行ごとの読み込み
99999まで続くForLoopWithBreakが2つありますが、"seqX:Y"におけるXが左側、Yが右側と対応しています。
とはいえいくらなんでも繰り返し回数が多いと思いますので、適当に削ってやっていただいて大丈夫です。
両方から持ってきた数字と"seq"と":"をAppendノードで結合して、データテーブルから値を引っ張ってきます。
文字列の分解
値を引っ張ってきたら、それを分解します。
まずは「IsNumeric」ノードを使って、値となっている文字列が全部数字であるかどうかを確かめます。
次に、文字列の全長を2で割った回数だけForLoopを行い、
2文字ずつ「GetSubstring」ノードで引っ張ってきます。引っ張ってきた2文字をIntにした時、0(="00"、つまり休符)ではない場合は、そのままオペレーション登録へ進みます。
オペレーション登録
処理IDとタイミングが取り出せる状況になったので、タイミングを時間に変換して配列へ登録します。
(60÷BPM)*4=1小節分の長さ
文字列の長さ÷2=小節内の分割単位
1小節の長さ÷小節内の分割単位=1分割単位分の長さ
行ごとの読み込みの左側から取り出せるIndex=何小節目か(A)
文字列分解のForLoopから取り出せるIndex=小節の頭から分割単位いくつ分ズレているか(B)
つまり、
1小節分の長さ×A+1分割単位分の長さ×B=その処理を行うべき経過時間
ということになります。
配列のソート
BPを使った配列のソートについては、こちらのサイト記事を参考にさせていただきました。
ゲームプログラムメモ — Blueprintで配列ソート #UE4Study
処理用の配列を、経過時間が短い順でソートします。
ここまででようやく準備が完了しました。
あとはこれを実行するときの部分になります。
実行部分
まずはBeginPlayに先程作ったスクリプトロードの関数を入れます。
処理を開始する時は、
このようにして、開始時間を記録すると同時にシーケンス処理開始のフラグを立てます。
そして、Tick処理がこちらです。
配列の最初の要素のTimeを確認し、経過時間がそれに到達していたらオペレーション実行のイベントを呼びます。
その後、不要になったそのインデックスを配列から削除します。
オペレーション実行のイベントでは、
このようにSwitch分岐でIDごとに処理を分けます。好きな処理を(99種類までなら)好きなだけ定義することが出来ます。
もちろん、処理IDが多くなればなるほど見た目が複雑になりがちなので、その時は以前の記事のようにマクロを使うなどして可読性を高める必要があると思います。
まとめ
・スクリプトの分解は面倒だけど、理屈がわかれば面白い
・BMSのスクリプトを考えた人は偉大
・これを上手く使えば楽曲のPVを作ったりすることも出来るのでは……?
というわけで、これでこの記事はおしまいです。
ここまで読んで頂き、ありがとうございました。
明日はmonsho1977さんの「ComputeShaderを使ったポストプロセス追加手法」です。
【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:まとめ
ぐらいの長さで公開していけたらと思っております。
何卒よろしくお願いいたします。