Unity+MIDIで音ゲーを作るならコレ!(1) の続きです。
MIDIファイルの中身を知る
前回はMIDIファイルをインポートし、再生するところまで行いました。
今回は、曲のリズムに合わせて「何か」をしてみます。
ところで、再生中のConsoleウインドウに大量のメッセージが出ているのに気が付きましたでしょうか?
MIDIファイルは、mp3のような空気の振動をデータ化したものとは違い、
- 音を鳴らす
- 音を止める
- (音色など)設定を変える
といったデータの集まりでできています。
例えば
Note On ・・・ Note:060 Velocity:075 ・・・
は、「ノートナンバー60を音量75で鳴らす」という情報を含んでいます。
つまり、音が鳴るたびに何らかの命令が出ているので、このタイミングで何かを行うことができれば、曲に合わせて何かを行ったことになります。
スクリプトを作成する
まずはMIDIToObj.cs
というスクリプトを用意し、中身を下のプログラムに置き換えてください。
using MidiPlayerTK;
using System.Collections.Generic;
using UnityEngine;
public class MIDIToObj : MonoBehaviour
{
// Start is called once before the first execution of Update after the MonoBehaviour is created
void Start()
{
}
// Update is called once per frame
void Update()
{
}
public void OnMidiEvent(List<MPTKEvent> midievents)
{
foreach(MPTKEvent ev in midievents)
{
string msg = $"cmd:{ev.Command} tr:{ev.Track} ch:{ev.Channel} note:{ev.Value}";
Debug.LogWarning(msg);
}
}
}
次に、Hierarchyウインドウで右クリック>Create Emptyで空のオブジェクトを作成し、わかりやすいように名前をMIDIToObjとします。
さらにこのオブジェクトに、先ほど作成したMIDIToObj.csファイルをドラッグ&ドロップでアタッチします。
イベントを登録する
「何らかのタイミングで」処理をすることをイベントと呼びます。
先ほど音を再生したMidiFilePlayerにはその機能が備わっています。
MidiFilePlayerのInspectorの真ん中あたりに、ShowUnityEventという項目があるので、左端の▲をクリックして開きます。
次に、開いた項目の上から2番目(まんなか)の枠の右側にある+ボタンをクリックし、イベントを追加します。
None(Object)のところにHierarchyウインドウにあるMIDIToObjをドラッグ&ドロップします。
さらにNo Functionのリストをクリックし、MIDITOObj > OnMidiEventを選択します。
Consoleウインドウに、白い吹きだしのメッセージに交じって黄色い三角のメッセージが出ていると思います。
そこでConsoleウインドウの右上の[ふきだし999+]の部分をクリックすると、三角のメッセージだけが残ります。
よく使われている音を探す
黄色い三角のメッセージの中で、出現頻度が高いものを探してみます。
Consoleウインドウの左から2番目の"Collapse"を押すと同じメッセージがまとまるので、頻出のコマンドがわかりやすくなります。
出現頻度が高い=その曲でよく使われる音のタイミングで「何か」をすることで、曲に合わせて動いている感じが出やすくなるかと思います。
曲に合わせてメッセージを出す
先ほどのMIDIToObj.cs
を書き換えて、曲に合わせてメッセージを出してみます。
using MidiPlayerTK;
using MPTK.NAudio.Midi;
using System.Collections.Generic;
using UnityEngine;
public class MIDIToObj : MonoBehaviour
{
// Start is called once before the first execution of Update after the MonoBehaviour is created
void Start()
{
}
// Update is called once per frame
void Update()
{
}
public void OnMidiEvent(List<MPTKEvent> midievents)
{
foreach(MPTKEvent ev in midievents)
{
if (ev.Command == MPTKCommand.NoteOn && ev.Channel == 9)
{
string msg = $"cmd:{ev.Command} tr:{ev.Track} ch:{ev.Channel} note:{ev.Value}";
Debug.LogWarning(msg);
}
}
}
}
書き換えたのは
foreach(MPTKEvent ev in midievents)
{
+ if (ev.Command == MPTKCommand.NoteOn && ev.Channel == 9)
+ {
string msg = $"cmd:{ev.Command} tr:{ev.Track} ch:{ev.Channel} note:{ev.Value}";
Debug.LogWarning(msg);
+ }
}
の部分です。無数に届くイベントから、cmd:NoteOnかつch:9
のときだけメッセージを出すようにしました。
if (ev.Command == MPTKCommand.NoteOn && ev.Channel == 9)
の代わりに、
if (ev.Command == MPTKCommand.NoteOn && ev.Value == 46)
のようにすると、cmd:NoteOnかつnote:46
の時だけメッセージが表示されるようになります(Consoleウインドウの左から2番目の"Collaspe"を解除しておかないとメッセージがまとめられてしまいます)。
あとはメッセージの代わりにオブジェクトを出すだけです。ここでは割愛します。
音に合わせて オブジェクトを出すことはできたのですが、
多くの音ゲーは 音が出る以前にすでにオブジェクトが出現しており、
音が出るタイミングでオブジェクトがボーダーラインを超えるといった必要が出てきます。
次回はこちらについて行う予定です。