Ludumdare 35に第5回サウンドゲームジャムで作ったVoipadiusで参加したのですが、
やっぱりWebプレーヤーで遊べると気軽に遊んでもらえそうなので対応してみた。
Webプレーヤー版Voipadius(ボイパディウス)
ボイスパーカッションバトルを味わえるような2人以上で遊ぶ楽器(?)です。
出てくるキャラに合わせてボタンを押す。人数が多い場合は適当に交代する。
音が鳴る。すべての音は声の録音素材です。(ドラムとかトランペットとか動物とかすべてボイパ。すごい。)
ダウンロード
AtomCraftプロジェクトファイル(Voipadius_AtomProject.zip)
Unityプロジェクト(Voipadius.unitypackage)
波形素材とAtomCraftプロジェクトの変更や再利用は自由にどうぞ。
音を差し替えてみたりして遊んでみてね。
(Unity5.3.4f1,ADX2LEv202)
scenesのgameSceneとVirtualNanoPadSceneを使ってます。
追加しておいてください。
MidiJackのアクセスで止まってしまう場合はコメントアウトしてみて。
やることは
- ADX2LEのキューの名前、IDを修正
- ADX2LEの音をwav化(バウンス)する
- ADX2LEの警告を無効化する
- AtomSourceのキュー再生をAudioSourceの再生にする
の4ステップ。
ADX2LEのキューの名前、IDを修正
Unityの標準再生は波形ファイルをAudioClipにするので、対応するAudioClipを作るための波形を用意する。
キュー名とキューIDで再生していたのだが、
IDはやめて名前を番号にすることで、すべて名前で呼べるようにした。
MIDIデバイスからの入力Note番号をそのままキューIDにするような形で作っている。
(60から始まるように少しずらしているけど)
0は無音用として、
キューIDがMIDINoteNoとして動作するようにしている。
元々あった名前はコメントに退避
ADX2LEの音をwav化(バウンス)する
バウンス設定を修正して、一つずつバウンス。
マテリアルの「bounce」フォルダにキュー名で波形が作成される
あらかじめ名前を修正していたのでいい感じに。
マテリアルのフォルダに波形が出来る
たまに再生位置がずれたりして失敗している場合があるので再生してチェックし、失敗した波形はバウンスをやり直す。
あと、ランダムなども気に入らない場合はバウンスをやり直す。
あと、波形エディタで若干トリミングしています。
ADX2LEの警告を無効化する
Webプレーヤーでビルドするといくつか警告でビルドを中断されるので
以下の修正
#endif
#elif UNITY_WIIU || UNITY_PSP2 || UNITY_PS3 || UNITY_PS4 || UNITY_WEBGL
#define CRIWARE_UNSUPPORT_SEQUENCE_CALLBACK
#elif UNITY_WEBPLAYER
#else
#error undefined platform if supporting sequence callback
#endif
#elif UNITY_PSP2
StandardMemory = StandardMemoryNsrVoicePoolId, /**< 機種標準のメモリ再生ボイスプールID */
StandardStreaming = StandardStreamingNsrVoicePoolId, /**< 機種標準のストリーミング再生ボイスプールID */
#elif UNITY_WEBPLAYER
#else
#error unsupported platform
#endif
#elif UNITY_PS4
Atrac9Memory = 6, /**< [PS4] ATRAC9メモリ再生ボイスプールID */
Atrac9Streaming = 7, /**< [PS4] ATRAC9ストリーミング再生ボイスプールID */
#elif UNITY_WEBPLAYER
#else
#error unsupported platform
#endif
UNITY_WEBPLAYERの#ifをつけただけ。
AtomSourceのキュー再生をAudioSourceの再生にする
ファイル名でサクッとやりたかったのですが、
ここから少し面倒なことに、Unity5.3.4f1のWebプレーヤーだとResources.Loadがうまくいかない。
(あ、Resourcesに入れてなかったからか...とこれ書いてる時に気がついたがまぁいいや)
ので、[Resources.Loadを使わない方法でのシーンでのclip管理]的なTipsということで・・・
名前リスト用意して、clipを名前で指定できるようにしている。
シーンをキューシートみたいに見せかける話
シーンに無理やりすべてのAudioClipをシーンに追加。
bounceフォルダをUnityプロジェクトに入れて、
空のGameObject作って、その下に配置。
この時、名前の後ろに「 (1)」が勝手につく。
このAudioClipをclipArrayに入れて呼び出せるようにした。(ちと回りくどいかもね・・・)
でも、シーン単位で複数AudioClipを管理しているみたいにできるね。きっと。
public class PlayADX2 : SingletonMonoBehaviourFast<PlayADX2>
{
public Text beatCountText;
#if UNITY_WEBPLAYER
AudioSource[] source = new AudioSource[2];
int souceCount = 0;
Dictionary<string,AudioClip> clipArray = new Dictionary<string,AudioClip>();
string[] clipNameList = {
"60",
"61",
"62",
"63",
"64",
"65",
"66",
"67",
"68",
"69",
"70",
"71",
"72",
"73",
"74",
"75",
"76",
"BD",
"blulu",
"HH",
"hituzi_0",
"hituzi_1",
"hituzi_2",
"hituzi",
"neco_0",
"neco_1",
"neco_2",
"neco",
"rest",
"SD",
"SD2",
"TPLow",
"TPLow2",
"TPLow3"
};
AudioClip NameToClip(string name)
{
return clipArray[name];
}
AudioClip IdToClip(int id)
{
if(id < 60){
AudioClip clip = null;
switch(id)
{
case 0:clip = clipArray["rest"];break;
case 1:clip = clipArray["BD"];break;
case 2:clip = clipArray["blulu"];break;
case 3:clip = clipArray["SD"];break;
case 4:clip = clipArray["TPLow"];break;
case 5:clip = clipArray["SD2"];break;
case 6:clip = clipArray["TPLow2"];break;
case 7:clip = clipArray["HH"];break;
case 8:clip = clipArray["TPLow3"];break;
case 9:clip = clipArray["hituzi"];break;
case 10:clip = clipArray["neco"];break;
case 11:clip = clipArray["hituzi_0"];break;
case 12:clip = clipArray["neco_0"];break;
case 13:clip = clipArray["hituzi_1"];break;
case 14:clip = clipArray["neco_1"];break;
case 15:clip = clipArray["hituzi_2"];break;
case 16:clip = clipArray["neco_2"];break;
}
return clip;
}
return clipArray[id.ToString()];
}
#else
CriAtomSource source;
#endif
public GameManager2 gm;
void Start()
{
#if UNITY_WEBPLAYER
for(int i = 0;i<2;i++){
source[i] = this.gameObject.AddComponent<AudioSource>();
source[i].volume = 0.71f;
}
foreach(string name in clipNameList)
{
//AudioClip newClip = Resources.Load("bounce/"+name,typeof(AudioClip)) as AudioClip;
GameObject obj = GameObject.Find(name + " (1)");
if(obj == null)
{
Debug.LogWarning("obj null " + "name");
}
AudioSource newClip = obj.gameObject.GetComponent<AudioSource>();
if(newClip == null)
{
Debug.LogWarning("null " + "bounce/"+name);
} else {
//Debug.Log(name);
clipArray.Add(name,newClip.clip);
}
}
#else
source = this.gameObject.AddComponent<CriAtomSource>();
#endif
再生部分
AudioSourceは2つくらいにした。(同時に2つしか鳴らないけどまぁいいか)
IDでも名前に変換して呼び出すようにした。
/// <summary>
/// キュー名で再生
/// </summary>
/// <param name="name">Name.</param>
public void Play(string name)
{
if (isSequencePlaying)
{
quantizePlayCueName = name;
} else
{
#if UNITY_WEBPLAYER
source[souceCount%2].clip = NameToClip(name);
source[souceCount%2].Play();
souceCount++;
#else
source.Play(name);
#endif
}
}
public void PlayId(int id)
{
//Debug.Log("PlayId" + id.ToString());
if (isSequencePlaying)
{
quantizePlayCueId = id;
} else
{
#if UNITY_WEBPLAYER
source[souceCount%2].clip = IdToClip(id);
source[souceCount%2].Play();
souceCount++;
#else
source.Play(id);
#endif
}
}
AISACとかも呼べないので、
if UNITY_WEBPLAYERで呼ばないようにした
if (ccNumber == 1)
{
#if UNITY_WEBPLAYER
#else
source.SetAisac(2, ccValue); // filter
#endif
}
if (ccNumber == 2)
{
#if UNITY_WEBPLAYER
#else
source.SetAisac(3, ccValue); // fx1
#endif
#おまけ
おまけ1.実は簡易MMLとして、32分音符固定MMLになってます。
オクターブもないですけど・・・
おまけ2.MusicEngineぽいもののの簡易的なものとしてクオンタイズプレイをつけてあります。
シーケンス再生中は16分音符でクオンタイズ再生されます。
if ((beatCount32 % 2) == 0)
{
if (quantizePlayCueName != "")
{
#if UNITY_WEBPLAYER
source[souceCount%2].clip = NameToClip(quantizePlayCueName);
source[souceCount%2].Play();
souceCount++;
#else
source.Play(quantizePlayCueName);
#endif
quantizePlayCueName = "";
}
if (quantizePlayCueId != 999)
{
#if UNITY_WEBPLAYER
source[souceCount%2].clip = IdToClip(quantizePlayCueId);
source[souceCount%2].Play();
souceCount++;
#else
source.Play(quantizePlayCueId);
#endif
quantizePlayCueId = 999;
}
}
おまけ3.MidiJackをデバイスごとに受け取れるように変更
ただ、iOSとかWEBPLAYERだと読んだ時に止まってしまうようなので回避もしてる。
void Update()
{
uint playerDeviceId = 0;
#if UNITY_WEBPLAYER || UNITY_IOS
#else
switch (gm.turn)
{
case 1:
playerDeviceId = MidiDriver.Instance.connectedSourceArray [MidiDriver.Instance.connectedSourceArray.Count - 1];
break;
case 2:
playerDeviceId = MidiDriver.Instance.connectedSourceArray [MidiDriver.Instance.connectedSourceArray.Count - 2];
break;
}
#endif
#region padInput
for (int noteNumber = 0; noteNumber < 128; noteNumber++)
{
if (playerDeviceId > 0)
{
if (MidiMaster.GetKeyDown(playerDeviceId, noteNumber))
{
//Debug.Log("noteNo" + noteNumber.ToString());
if (noteNumber >= 36 && noteNumber < 36 + 16)
{
// Scene1
this.PlayId(noteNumber - 35);
}
}
}
}
#endregion
終わりに
ADX2LEをプロトタイプとして使っていて、
今回みたいに未対応プラットフォーム(Webプレーヤーとか)だった場合に、
ADX2LEを複数波形の管理ツールとして使うのもありかもと思った。
(ボリューム調整やコメントつけたり、名前変更とかも楽だし。)
Unityのマルチシーンロード機能を使えば、
「シーン=キューシート」と見たてて、複数オーディオのパッケージとして、シーンを作成していくとかできそう。