大福未来研究所

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

【UE4】Sound Visualizations Pluginと描画ターゲットを使ってサウンドの波形を表示する

はじめに

年度が明けました。今年度からは自分を取り巻く環境も大きく変わるため、ブログの更新頻度も落ちるかもしれませんがご了承ください。

さて、いろいろな音楽系のサービスや、UE4でサウンドを読み込んだときなど、音声ファイルの「波形」が表示されるのを見たことがある方も多いかと思います。
f:id:dfk_ohnuma:20170408181712p:plain
↑こんなやつのことです。


今回の記事では、読み込んだサウンドの波形をSound Visualizations Plugin経由で取得し、描画ターゲットへ書き込む仕組みを作ります。




今回のプロジェクトについて

今回のプロジェクトの動作の流れは、次のようになります。


1.レベル上でGキーを押す
2.ブループリントAサウンドBから波形データを読み込み、描画ターゲットCへ描画する
3.レベル上にはウィジェットDが描画ターゲットCを表示するようになっており、処理が成功すれば画面上にはサウンドBの波形が可視化される
4.描画ターゲットCの描画内容はCキーを押すとクリアされる


というわけなので、
・ブループリントA
・サウンドB
・描画ターゲットC
ウィジェットD

の4つを用意する必要があります。
その中のサウンドについては、適当にwavファイルを突っ込めばいいだけなので省略します。

それでは、新規プロジェクトを立ち上げましょう。
立ち上がったらすぐに「編集」→「Plugins」へ入り、
「Sound Visualizations Plugin」にチェックを入れてエディタを再起動しましょう。
f:id:dfk_ohnuma:20170408183441p:plain

描画ターゲットを用意しよう

コンテンツブラウザ上で右クリック→「マテリアル・テクスチャ」→「描画ターゲット」で描画ターゲットを作成します。
f:id:dfk_ohnuma:20170408183126p:plain
パラメータは適当にいじっておきます。これは一例です。
f:id:dfk_ohnuma:20170408183143p:plain
これはブループリント等から書き込むことが出来、それをテクスチャとしてウィジェットなどに利用することも出来るオブジェクトです。

ウィジェットを用意しよう

先程の描画ターゲットを表示するだけのウィジェットを作ります。
f:id:dfk_ohnuma:20170408183712p:plain
imageをこのように配置し、テクスチャとして先程の描画ターゲットを指定します。

いざブループリント

それではここからが本丸です。
新しいActor Blueprintを作成し、開きます。
用意する変数は2つです。
f:id:dfk_ohnuma:20170408184349p:plain
Divide…サウンドの波形データをいくつ分に分割するか。描画ターゲットのXサイズと同じにしておくことを推奨します。int型。
SoundWave…読み込みたいサウンドをデフォルト値のところに入れておきます。SoundWave型へのリファレンス。


まずはウィジェット表示の部分を作りましょう。


f:id:dfk_ohnuma:20170408183949p:plain


次に、Cキーを押したときのクリア機能を付けます。


f:id:dfk_ohnuma:20170408184614p:plain


では、Gキーを押したときの処理を作っていきましょう。
全体図はこのようになります。


f:id:dfk_ohnuma:20170408185602p:plain


それでは細かい部分を見ていきましょう。


f:id:dfk_ohnuma:20170408190115p:plain


「描画ターゲットクリア」は先ほどもありましたね。
次にBegin Draw Canvas To Render Targetノードで描画開始の手続きをします。
「Size」ピンには指定した描画ターゲットの縦横サイズが入るので、右クリックで「ピンを分割」することで画像のようにfloatピンが2つ用意されるようになります。
Get AmplitudeノードはSound Visualizations Plugin付属のノードです。


Sound Wave…取得対象とするサウンドファイル。
Channel…どのチャンネルから取得するか。ステレオwavなら0と1チャンネルに左右それぞれの音が入っています。
Start Time…サウンドファイルの何秒のところからを対象にするか。
Time Length…Start Timeから何秒分の長さを対象にするか
Amplitude Buckets…取得したデータを入れる配列の長さ。多いほど出力されるデータが細かくなる


SoundwaveからGet Durationで音の長さを取得し、Time Lengthへ代入しましょう。
Divideの値をAmplitude Bucketsへ代入します。これでOut Amplitudeから取得したデータが配列となって出てきます。


ここからはそのデータを使って描画ターゲットに線を引きます。
f:id:dfk_ohnuma:20170408190551p:plain
描画ターゲットのXサイズ=Divide=配列の長さ、となっているので、描画ターゲットのX座標を1ずつずらしながらデータの数値の長さだけY方向へ線を引きます。
この時、全配列の中で一番大きな値(=一番音量が大きいところ)を1として、それと比べた比率で描画するため、小さな音のファイルでも縦幅が1になる部分が必ず生ずる事になります。
これが納得いかん!という場合は基準値を何らかの形で取って、処理するほうが良いと思います(なお私も納得行かないかもしれないw)
そして、配列を回し終わったら「描画終了手続き」を経て一連の流れは終了です。

実際に動かすとこうなる

それでは実際に動かし、Gキーを押してみますと……
f:id:dfk_ohnuma:20170408190950p:plain
このように波形が書き込まれれば成功です。


最後に

Sound Visualizations Pluginはまだまだ色々使えそうなのですが、
私自身も勉強中なので、また新しい記事が書けるよう精進します。
また、Sound Visualizations Pluginについて詳しい方がいらっしゃったら是非ブログ記事を書いていただけると大福さんが喜びますのでお願い致します。
というか色々と教えてください(土下座)

以上でこの記事は終わりです。
お疲れ様でした。

【UE4】ウィジェットでメニュー画面などを作った際にマウス入力とキーボード/ジョイパッド入力を両立させる

ウィジェットブループリントあるある

UE4のウィジェットブループリント、とても便利に使ってます。
拙作「L.F.O.」のタイトル画面もポーズ画面も死んだときの画面もスコア表示等のUIも全部ウィジェットです。

しかしひとつだけ困ったことがありました……

「キーボード入力やジョイパッド入力のイベントがないやん!!!!」

そう、キーボードやジョイパッドでのインプットイベントに対応していなかったのです。



なので作ってみました。


間接的にインプットを取る

ウィジェットブループリントではキーインプットをイベントとしてグラフに作ることは出来ません。
しかし、アクタブループリントのキーインプットイベントからウィジェットブループリント内のイベントや関数を呼び出すことは出来ます。
例えるとこんな感じになります。

f:id:dfk_ohnuma:20170315221325p:plain

その手順を説明していきます。


インターフェースを作ろう

この方法で入力を取れるようにする場合、ウィジェット側に持たせておくブループリントインターフェースがあると便利になります。
というのも、ブループリントインターフェースは対象のオブジェクトを指定して呼び出す時、
そのオブジェクトのクラス名や実際にそのオブジェクトクラス内に該当するイベント及び関数が存在するかどうかに関係なく、
メッセージとして投げっぱなしジャーマンをしてくれるからです。

というわけで、さっそくブループリントインターフェースを作ってみましょう。今回は、

・カーソルを上移動させるCurUp
・同じく下移動させるCurDown
・決定ボタンを押すPushDecide

この3つのイベントをブループリントインターフェースで定義します。

f:id:dfk_ohnuma:20170316001315p:plain

あっという間に出来ました。
このブループリントインターフェースを、ウィジェットブループリントに実装します。
f:id:dfk_ohnuma:20170316001512p:plain
これで、このウィジェットブループリントの中に先ほどの3つのイベントが増えました。


使い捨てアクタくん

次に、入力用のアクタブループリントを作成します。今回の場合、中身はこのようになっています。

f:id:dfk_ohnuma:20170316005814p:plain

複数の種類のウィジェットに対応できるようにするため、変数のWidgetはObject型のリファレンスにしてあります。
また、この時、
f:id:dfk_ohnuma:20170316011004p:plain
「ポーズ中でもティックする」にはチェックを入れておかないと、ポーズ中に出て来るウィジェットなどに対して動作してくれなくなります。


この手段を使う場合、ウィジェット自身が入力用のアクタをスポーンし、そこに一対一の関係で紐付けをする必要があります。
そして、ウィジェットがビューポートからリムーブされる際、入力用のアクタも同時に破壊されるようにしなければなりません。

ウィジェット側のコンストラクタには、
f:id:dfk_ohnuma:20170316010438p:plain
このように入力用アクタをスポーンして、そこに自分自身のリファレンスを登録させるようにしています。
そして、スポーンした入力用アクタのリファレンスを変数にもっておきます。


あとは、(適当な作りではありますが)それぞれの入力に対応したイベントと、それに応じた動作をウィジェット側に組み込んでみましょう。
f:id:dfk_ohnuma:20170316010742p:plain
f:id:dfk_ohnuma:20170316010746p:plain
決定ボタンを押したときにはウィジェットが消えるので、入力用のアクタにも消えてもらいます。

これで、ウィジェットに対するマウスの入力の可否に関係なく、ウィジェットへのキーボード/ジョイパッドの入力が可能になります。

まとめ

ブループリントインターフェースと入力用アクタを使うことで、ウィジェット自身だけではやりづらい動作や実装も、
色々と組み込みやすくなると思います。
もし面白そうだと思ったら、是非試してみてください。
また、もっと効率のいい方法をご存知の方いらっしゃいましたら、是非教えてください。
ここまでお読み頂き、ありがとうございました。

【UE4】Procedural MIDIでMIDIファイルを元にオブジェクトを生成する

最初に

この記事はMozPacaさんの
mozpaca.hatenablog.com
こちらの記事を読み、触発されて書いたものです。
Procedural MIDIを掘り下げていく過程がわかりやすく書かれているので、
まずはその記事を読んでみてください。
ただし、現在のProcedural MIDIプラグインとはBPノードの見た目や項目数等が異なっていますので、それだけは注意してください。

Procedural MIDIとは

UE4でMIDI関連を取り扱えるようになるプラグインになります。
www.unrealengine.com


こちらからプラグインはダウンロードできますが、
必ず上記のサイトから飛べる「サンプルプロジェクト」もダウンロードするようにしてください!
サンプルプロジェクトは、機能を再確認したりする時にもとても役に立ちます。


まずはサンプルプロジェクトを見てみよう

まずは上記のサンプルプロジェクトを開いてみましょう。
f:id:dfk_ohnuma:20170206212148p:plain
「BP_Readme」の中に書かれているコメントを読んでおくと、今後の応用もやりやすくなると思います。

このサンプルプロジェクト内には、MIDIの初期化などを担当する「BP_Asset_Player」、それを継承し、MIDIメッセージから音を鳴らすのを担当する「BP_MIDI_Player」、
更にそれを継承し、MIDIメッセージを元に視覚オブジェクトを操作する「BP_Visual_MIDI_Player」という3つのオブジェクトがあります。
この構成を元にしてプロジェクトを作ってみましょう。
あと、できればサンプルプロジェクトを実行してみてください。MIDIの音が鳴ると同時に画面内のオブジェクトの色が変わるのがわかると思います。
一体何が起きているのか、自分で調べてみるのも良いと思います。

新しいプロジェクトでブループリントを作ってみる

新しいプロジェクトを立ち上げて、「編集」→「Plugin」を開き、
f:id:dfk_ohnuma:20170206214648p:plain
の「MIDI Plugin」の「Enabled」がチェックされているかを確認しましょう。
チェックされていなかったら、チェックして再起動します。


確認が終わったら、サンプルプロジェクトを参考にして3つのブループリントクラスを作ります。
f:id:dfk_ohnuma:20170210112859p:plain
「BP_Base」と、

f:id:dfk_ohnuma:20170210112922p:plain
それを継承してMIDIから音を鳴らしたりうんぬんする「BP_ObjBase」、

f:id:dfk_ohnuma:20170210113008p:plain
更にそれを継承して、オブジェ生成を担当する「BP_ObjFactory」です。
この上の「MIDIメッセージの中身表示」の部分は検証用に作ったものなので、ご自由にどうぞ。


MIDIメッセージとMIDIファイルの関係

MIDIファイル内にある譜面を再生している時の
「音符」とか「テンポチェンジ」とか「ピッチ変更」とかの指示がMIDIメッセージとして流されます。
つまり、MIDIファイル=譜面とすると、演奏者の「ここでこの音符を鳴らすぞ!」という脳からの命令がMIDIメッセージで、
それが身体に伝わって楽器(MIDI音源)を鳴らす、ということになります。

UE4でMIDIメッセージをゲームなどに利用する際、どんな種類のメッセージに対応して動作させるのかを決めるのは大事です。
どんな種類のメッセージでも受け付けてしまうと、「音符に合わせてオブジェクトを作りたい/動かしたい」と思って実装したものが、
ピッチの変更や楽器の変更などのメッセージで誤動作してしまうからです。


f:id:dfk_ohnuma:20170210114147p:plain
MIDIメッセージの種類はこのように列挙型でまとめられているので、
この中の「Note」でだけ動作するようにブランチを用意するといいと思います。


それではBP_ObjFactoryの「OnMidiEvent」イベントを見てみましょう。
f:id:dfk_ohnuma:20170210114431p:plain
この引数の意味ですが、


・Event Type:このメッセージはどの種類のメッセージか(前述の列挙型)
・Event Channel:何チャンネル目のメッセージか(MIDIはチャンネル数が16あります。つまり同時に16種類の楽器の音が出せるようになっています)0~15の間の値になります。
・Event Data 1:0~127の値になります。Noteメッセージの場合は音の高さになります。それぞれの数値が低い順に鍵盤に割り当てられていると考えてください。
・Event Data 2:0~127の値になります。Noteメッセージの場合は音の強さ(ベロシティ)になります。0の場合、無音になります。


となっています。
ですので今回、自分の場合では
f:id:dfk_ohnuma:20170210115015p:plain
このようにして、音の高さをY軸、音の強さをZ軸にして、さらにチャンネルごとに高さと生成するオブジェの種類を分けることにしました。
ここで大事なのは、「Noteメッセージは、MIDIの音符開始時と終了時の、合計2回流れる」ということです。
例えばベロシティ64の音が鳴る時、鳴り始めにベロシティ64のNoteメッセージが流れ、鳴り終わりにベロシティ0のNoteメッセージが流れます。
ですので、単にNoteメッセージを受け取るだけの実装にすると、予想だにしないタイミングでイベントが発生してしまうことがあります。
先ほどの画像の左上で「!=0」の判定を用意してブランチを挟んでいるのは、このためです。

MIDIファイルを用意して再生してみる

あとはMIDIファイルを用意して再生してみましょう。


こちらの動作デモではわかりやすいように、弦楽四重奏のカノンのMIDIファイルを使っています。

音を鳴らさずにMIDIファイルを再生する

さて、ここからがある意味本番です。

f:id:dfk_ohnuma:20170210120048p:plain
BP_ObjBaseの、赤く囲った部分の接続を外して、もう一度実行してみましょう。
すると、音は鳴らないのにMIDIファイルの通りにオブジェが生成されているのがわかると思います。


( ^ω^)これがとても美味しいのです。


つまりBGMを別に用意して、BGMと同じテンポでMIDIファイルを作り、同時に再生すれば、
BGMの音楽に合わせた演出やオブジェ生成のためだけのスクリプトファイルとして使うことが出来ます。
MIDIファイルはチャンネル数が16あるので、16チャンネル分の命令を1つのファイルで作ることが出来ます。
どのチャンネルのメッセージなのかをSwitchノードなどで分岐させて実装すれば、
1つのステージ内の敵出現、演出、その他もろもろを1つのMIDIファイルに集約することも可能になると思います。

まとめ

Procedural MIDIプラグインを使うことで、MIDIを「音を鳴らす」以外の用途にも使える可能性はたくさんあります。ここで紹介したのは一つの可能性に過ぎないので、興味を持たれた方は是非色々掘り下げてみてください。
そしてブログでシェアしてください(切実)
UE4のProcedural MIDIプラグインは、今も更新が続いているので、
この記事で書いた内容が、古いものになってしまうときも来るかもしれません。
その時はどうかご容赦くださいませ。

ということで、この記事はここで終わりです。
お疲れ様でした。

【UE4】コントローラを振動させる

振動が生む感動

ダメージを受けた時、必殺技を出した時、ボスが倒れた時。
色んなタイミングでコントローラが振動するゲームがコンシューマゲームには多いと思います。

適切なタイミングに用意された振動は、ゲームのもたらす情報を増幅し、
場合によっては感動や陶酔感を生むこともあります。

UE4ではコントローラを振動させる事ができ、しかもそれが案外簡単な方法で出来たので、
このブログ記事で共有させて頂けたらと思います。

フォースフィードバックエフェクトを用意する

実際のところ、コントローラを振動させるには、
f:id:dfk_ohnuma:20170107214936p:plain

このようなノードを用意するだけでOKでした。
各パラメータは以下の通りです。

・ForceFeedbackEffect……振動のパターンプリセット
・Looping……振動パターンがループするか否か
・Tag……タグを持たせたい時に使う

ちなみに、Client Play Force Feedbackで振動を開始した場合、
任意のタイミングでClient Stop Force Feedbackノードを使って振動を停止させることが出来ます。


さて、ノードを用意したばかりではForceFeedbackEffectには何も割り当てられていない場合がほとんどだと思います。
このノードは指定したパターンを再生するわけで、振動パターンが書き込まれたフォースフィードバックエフェクトを指定しなければなにも起きません。

f:id:dfk_ohnuma:20170107220803p:plain

これがフォースフィードバックエフェクトです。
「その他」→「フォースフィードバックエフェクト」で新規作成できます。

f:id:dfk_ohnuma:20170107221830p:plain

f:id:dfk_ohnuma:20170107222232p:plain

コントローラのどの部分を振動操作の対象とするかをチェックボックスで選び、
グラフで横・時間にたいする縦・振動の強さを描きます。

そして出来上がったフォースフィードバックエフェクトを先ほどのノードから指定して、完了です。
これで、実行パルスが通過するタイミングでコントローラが振動します。

フォースフィードバックエフェクトについては、
imoue.hatenablog.com
こちらの記事も参考にするとよいかと思います。
お疲れ様でした。

【コミケ】C91出展します

このたび、個人サークル「大福フューチャーラボ」として、
12月29日(明日ですね)のC91初日に出店させていただきます。

サークル配置は 1日目・西ほ-14b になります。

このブログで書いていた記事の内容をそっくりそのまま応用して作った、
UE4製のゲームとそのサントラが主な頒布物になります。

f:id:dfk_ohnuma:20161228173436p:plain

L.F.O. DLカード 1200円
L.F.O.サントラ DLカード 800円

2つセットで買えば2000円ぴったりなので、是非セットでどうぞ(宣伝)

dfkfuturelab.wixsite.com
↑この特設ページで体験版等もDL出来ますのでよかったらどうぞ

このL.F.O.というゲームなのですが、
音にシンクロする3DSTG、というところでRezを思い浮かべる方も多いと思いますが、
むしろ自分の中では、このゲームの源流は

ナイトストライカー
アフターバーナー
スペースハリアー
ダライアス
アウトラン

このあたりのオールドゲームにあります。
そして研究所らしくゲームを作ったときのテーマを言いますと、

・3DSTGに「音とシンクロするゲーム」という要素を加えたらどれぐらい気持ちいいゲームが作れるのか?
・UE4の個人制作でどこまで頑張れるのか?
・学生生活の最後に、自分の作りたいものと、自分のやってきたことの集大成を作りたい!

この辺りが主要なテーマだったような気がします。

ギリギリまで開発を続けましたが、自分の中で納得できるゲームになることが出来ました。
おしゃべりするだけでもいいので、当日はぜひ遊びに来てやってください。


今年はUE4でミートアップに登壇させていただいたり、色々自分の身の回りで変化が大きい年だった気がします。
年明けにもL.F.O.の開発で得た経験を記事にしていきたいと思いますので、是非よろしくお願いいたします。

【UE4】高解像度なスクリーンショットを撮る

はじめに

この記事はちょっとしたTips的なものです。

進捗の必需品・スクリーンショット

ゲームの開発中に、「今こんな感じです」と進捗をネットに上げたりする時、
だいたいはスクリーンショットか動画を見せると思うのですが、
今回はその中でスクリーンショットを用意するときに、
より高画質なものを撮影できる方法を書きたいと思います。

さっそく撮ってみる

というわけでエディタを開きます。
f:id:dfk_ohnuma:20161218192846p:plain


そして、この中にある
f:id:dfk_ohnuma:20161218193323p:plain
選択ビューポート」でプレイしてみましょう。


プレイ中に好きなタイミングで一時停止をします。
Shift+F1でマウスをビューポートの外へ出して、一時停止のところをクリックすればOKです。
するとこのような画面になるはずです。
f:id:dfk_ohnuma:20161218193639p:plain


では、この状態で「イジェクト」をクリックしますと、
f:id:dfk_ohnuma:20161218193731p:plain

f:id:dfk_ohnuma:20161218193811p:plain
このように、ビューポート内が普段の編集時と同じような見た目になります。


この状態だと、
f:id:dfk_ohnuma:20161218195806p:plain


こうしてビューポートの視点を自由に移動させたり、


f:id:dfk_ohnuma:20161218195905p:plain


こうしてレンダリングされる内容を変更したり、
他にも邪魔なアクタを非表示にしたりできるので、
撮りたいアングルやらなんやらを自由に決めます。
その後、左上のメニューを開いて、


f:id:dfk_ohnuma:20161218200115p:plain


この「高解像度スクリーンショット」を選択しますと、


f:id:dfk_ohnuma:20161218200225p:plain


警告とともにこんな画面が出てきます。
スクリーンショットの解像度をn乗にして撮影できます。
それでは試しに、乗数を1.0、2.0、4.0にしてそれぞれ撮影してみます。
撮影された画像は/Saved/Screenshots/Windowsフォルダへ保存され、
"HighresScreenshot+通し番号.png"という名前で保存されます。

これが1.0
f:id:dfk_ohnuma:20161218200629p:plain


2.0
f:id:dfk_ohnuma:20161218200652p:plain


4.0
f:id:dfk_ohnuma:20161218200836p:plain

まとめ

もちろん乗数を上げれば上げるほど、画像のサイズも大きくなるので容量にはお気をつけください。
こうして撮影した高解像度のスクリーンショットは、フライヤーやポスターに印刷するときに役に立ちそうですね。
技術的な話ではありませんでしたが、ここまでお読み頂きありがとうございました。

【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で表現します。

このように、記述した音符・休符の数に応じてプログラム側で自動的に小節の分割数を決めることが出来るため、
とても自由度の高いスクリプト記述が行えます。
今回の記事で作るスクリプトのシーケンス部分は、このシステムを取り入れています。


スクリプトを作る

まず最初に、構造体を作りましょう。
f:id:dfk_ohnuma:20161214013924p:plain
中身はこれだけです。
そして、これに対応したCSVファイルを作り、ちょっとだけ記述してみました。

f:id:dfk_ohnuma:20161214022435p:plain
私は今回、楽曲ではなくムービーそのものを流すために作ってみたので、最初が「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サウンド)をスクリプトから指定できるように、文字列に紐付けしたデータテーブルを作っておきます。
f:id:dfk_ohnuma:20161214021818p:plain

そして、スクリプトを読み込んだときに、どの時間にどの処理を行うのかを受け取らなければいけないので、処理用の構造体を作ります。
f:id:dfk_ohnuma:20161214021951p:plain
Time:スクリプトを読み込んで計算された、メディア再生からの経過時間
OperationNumber:処理内容ID

スクリプトを読み込む部分を作る

さて、ここから本番です。まずは読み込むためのBPを作ります。

ちなみに私の作ったBPでは、こんな感じの変数リストになってました。
f:id:dfk_ohnuma:20161214024141p:plain

Media:メディアプレイヤーを予め入れておく配列。たぶんサウンドでやるならAudioComponentか何かになってる
BPM:テンポを入れておく変数
Operation:さっき作った処理用の構造体
forSort:ソートに使ってました
MediaStartTime:再生開始時間を入れておく変数
SequenceStarted:処理が始まっているかどうか

文字列からムービーやらテンポやら処理やら、どれについて書かれた行なのかを判断する必要があります。
f:id:dfk_ohnuma:20161214021552p:plain
うーん、かなり長いですね。ということなので、1つずつ見ていきましょう。


まずはムービーのロード部分です。
f:id:dfk_ohnuma:20161214022932p:plain
f:id:dfk_ohnuma:20161214022848p:plain
データテーブル内のMovieXを検索し、そこに書かれている文字列に対応したメディアをメディアプレイヤーへセットします。
これを楽曲で行う場合は、おそらくSoundXを検索した後、そこに書かれている文字列に対応したサウンドをAudioComponentへセットする形になると思います。


次にBPM設定の部分です。
f:id:dfk_ohnuma:20161214023232p:plain
これは普通ですね。


ではシーケンス部分の読み込み処理を見ていきます。
f:id:dfk_ohnuma:20161214024326p:plain
全体図はこんな感じです。
1行ずつ読み込んで文字列の分解を行い、その結果をOperation配列へ登録。
それが全部終わったら、Timeが短い順にソートする。
という流れになっています。

行ごとの読み込み

f:id:dfk_ohnuma:20161214025509p:plain
99999まで続くForLoopWithBreakが2つありますが、"seqX:Y"におけるXが左側、Yが右側と対応しています。
とはいえいくらなんでも繰り返し回数が多いと思いますので、適当に削ってやっていただいて大丈夫です。
両方から持ってきた数字と"seq"と":"をAppendノードで結合して、データテーブルから値を引っ張ってきます。

文字列の分解

値を引っ張ってきたら、それを分解します。
f:id:dfk_ohnuma:20161214030130p:plain
まずは「IsNumeric」ノードを使って、値となっている文字列が全部数字であるかどうかを確かめます。
次に、文字列の全長を2で割った回数だけForLoopを行い、
2文字ずつ「GetSubstring」ノードで引っ張ってきます。引っ張ってきた2文字をIntにした時、0(="00"、つまり休符)ではない場合は、そのままオペレーション登録へ進みます。

オペレーション登録

処理IDとタイミングが取り出せる状況になったので、タイミングを時間に変換して配列へ登録します。
f:id:dfk_ohnuma:20161214030704p:plain


(60÷BPM)*4=1小節分の長さ
文字列の長さ÷2=小節内の分割単位
1小節の長さ÷小節内の分割単位=1分割単位分の長さ
行ごとの読み込みの左側から取り出せるIndex=何小節目か(A)
文字列分解のForLoopから取り出せるIndex=小節の頭から分割単位いくつ分ズレているか(B)


つまり、

1小節分の長さ×A+1分割単位分の長さ×B=その処理を行うべき経過時間
ということになります。

配列のソート

BPを使った配列のソートについては、こちらのサイト記事を参考にさせていただきました。
ゲームプログラムメモ — Blueprintで配列ソート #UE4Study

処理用の配列を、経過時間が短い順でソートします。
f:id:dfk_ohnuma:20161214031838p:plain
ここまででようやく準備が完了しました。
あとはこれを実行するときの部分になります。

実行部分

まずはBeginPlayに先程作ったスクリプトロードの関数を入れます。
f:id:dfk_ohnuma:20161214032100p:plain


処理を開始する時は、
f:id:dfk_ohnuma:20161214032248p:plain
このようにして、開始時間を記録すると同時にシーケンス処理開始のフラグを立てます。

そして、Tick処理がこちらです。
f:id:dfk_ohnuma:20161214032637p:plain
配列の最初の要素のTimeを確認し、経過時間がそれに到達していたらオペレーション実行のイベントを呼びます。
その後、不要になったそのインデックスを配列から削除します。


オペレーション実行のイベントでは、
f:id:dfk_ohnuma:20161214032747p:plain
このようにSwitch分岐でIDごとに処理を分けます。好きな処理を(99種類までなら)好きなだけ定義することが出来ます。
もちろん、処理IDが多くなればなるほど見た目が複雑になりがちなので、その時は以前の記事のようにマクロを使うなどして可読性を高める必要があると思います。

まとめ

スクリプトの分解は面倒だけど、理屈がわかれば面白い
BMSスクリプトを考えた人は偉大
・これを上手く使えば楽曲のPVを作ったりすることも出来るのでは……?

というわけで、これでこの記事はおしまいです。
ここまで読んで頂き、ありがとうございました。



明日はmonsho1977さんの「ComputeShaderを使ったポストプロセス追加手法」です。