同人ゲームサークル reverse snare 開発ブログ

主に2DSTGを作ってる同人ゲームサークル reverse snareの開発記、イベント・作品宣伝を行うブログです。

ADX2LEでインタラクティブミュージック(横遷移)の実装

製作中の「アルカイック・デスブリンガー」ではインタラクティブミュージックを実装したい。
その一歩として、次の体験版で実装予定のキャラセレ/チュートリアルのBGMでの実装を試みる。

インタラクティブミュージックとは

(以下IM)

ゲームの進行状態や操作に応じて音楽の展開を制御して、場面に合わせた音楽でゲームプレイを盛り上げる手法。

  1. 縦遷移…マリオシリーズヨッシー搭乗パーカッションやマリカwii以降のメニューの階層を進めたときに楽器が増えていくあたりがわかりやすい。
    強化状態やピンチな状態で楽器の追加変更を行う。

  2. 横遷移…F-ZERO GXファイナルラップBGMに遷移するとき、急に切り変わらず音楽的な区切り(4小節毎など)まで待ってから切り替える。
    ゲームの進行が固定的なスクロールシューティングでは、道中は曲流しっぱなしで十分だが、横遷移を使うのであれば、「ボス戦の形態によって曲が進む」などだろうか。

大復活の裏中ボスは、ステージ開始時に別曲を読み込んで再生しているが、条件によってゲーム進行を一部だけ変えるのであれば、IMで実装してもよいかもしれない。

キャラセレ/チュートリアルでIMをどう使う?

実現したいのは以下の通り。
横遷移を使用する。

太文字はチャレンジングな内容で問題になりそうな事項。

  • キャラセレのループからチュートリアルのループに遷移
    • 遷移するときは専用のセクションを通過したい
  • キャラセレのループが終わらないままチュートリアル終了した場合、Aジングルでゲーム開始、
    チュートリアルのループならBジングルでゲーム開始
  • 変拍子の曲でインタラクティブしたい
    • 主にボス戦で多用する予定だし、先にどのようなデータ用意/実装が必要か見ておきたい
  • セクションごとに微妙にテンポチェンジしたら生演奏っぽいかな?
    • 普段やってるわけでもないのになんでこれ目標にしたの?????
  • 曲のセクションを遷移しても空間系の余韻を残すべき。なので遷移タイミングすべてでwavを分割しておく

曲の作成

というわけで曲が出来上がった。

  • Aパート:キャラセレ用パート「7776」
    • その名の通り7/4 x3と6/4で構成されたパート
    • キャラセレ中はこのパートでループする
    • ゲームがチュートリアルに遷移した場合は、4,8小節目を遷移用フレーズ(8/4)に切り替えてBパートへ遷移
    • ゲームがゲーム開始状態に遷移したときは各小節の終わりからジングル:end1に遷移
  • Bパート:チュートリアル用前半パート「piano」
    • ピアノのメロが主体のパート
    • 自動的にCパートに遷移する。最後の小節だけ7/8拍子
    • ゲームがゲーム開始状態に遷移したときは4小節の区切りからジングル:end2に遷移
  • Cパート:チュートリアル用後半パート「DHE」
    • switchのユグドラを遊んでいたせいかスティング作品の戦闘準備BGMぽくなったパート
    • チュートリアル中はこのパートでループする
    • ゲームがゲーム開始状態に遷移したときは2小節の区切りからジングル:end2に遷移
  • Aパートで終了時ジングル「end1」
  • B,Cパートで終了時ジングル「end2」

音楽的なつながりとか、ディミニッシュのフレーズでゴリっとやってしまった。
end2の最初の3音のヒット、「THE ASTONISHING」で聞いたような場面転換感。

というわけで遷移ポイントごとにwavに書き出してCRI Atom Craftに取り込む。
空間系の余韻を残すという目標によってデータサイズ爆上げ君。

CRI Atom Craftでデータ作成

テンポ/拍子設定

イムルーラ横のボタンを押すと時間基準表示かビート表示が選べる。
https://game.criware.jp/manual/adx2_tool/jpn/contents/criatom_tools_atomcraft_edit_timeline_timebase_setting.html?highlight=%E3%82%BF%E3%82%A4%E3%83%A0%E3%83%99%E3%83%BC%E3%82%B9

テンポ/拍子を設定できる。7/8とか分母も設定できる。

ミリ秒指定は面倒なのでクオンタイズされた操作ができるのはありがたいが、同一キュー内でテンポチェンジがあった場合は変更できない。

タイムラインの右クリックからビート同期情報を作成できるが、ここでテンポ/拍子変更してもタイムルーラには反映されない。ビート同期情報は縦遷移のテンポ合わせに必要なものなので、横遷移のみの今回には関係ない。
変拍子だけなら、正直、小節頭がずれててもクオンタイズされていれば目的のデータ設定はできそうだが、テンポチェンジとなると厳しい。

タイムライン上で右クリック>サブシーケンス作成で編集中のキュー内でサブシーケンスを持てる。
サブシーケンスの中では別のテンポ/拍子設定が使えるので、変更があるたびにサブシーケンス内に波形を置いていく。

ブロックの設定

https://game.criware.jp/manual/adx2_tool/jpn/contents/criatom_tools_atomcraft_sequence_block_property.html

キュー内でのみブロックを設定する。 サブシーケンスや子キューで持つと親キューの進行にかかわらず内部でループすることがあるので。

  • ブロックの終端ミリ秒
    • テンポチェンジと変拍子が絡む場合は自前のexcelで計算する。変拍子では付属の計算機で出しにくいのでね…
  • ブロック遷移先
    • デフォルトの遷移先。次のセクションに続けるなら「次のブロック」、ループするなら戻したいブロックを指定。遷移先を設定するときはインスペクタの下のブロックの情報設定を開く必要あり。
  • ブロック遷移タイミング
    • 今回のAパートのような、「遷移先によって遷移タイミングを変える」を実装したい場合はブロックエンド時に限定してブロック数を増やしたほうが良い。プログラムで現在ブロックIndexはとれるが、現在の指定分割位置が取れないため
    • 他には「XからYに遷移する時は、2小節目はX’につなぐが4小節目はX''につなぐ」などつなぎ部分を切り替えた曲を用意する場合もブロックエンドのほうがよさそう
    • 上記に該当しなければ、入力の手間が省けるため、指定分割でもよい
  • ブロック遷移の振る舞い
    • 余韻を残したい場合は「何もしない」

多重再生が発生しないか確認

ここからが本当の地獄だ…

  • テンポ変更によってクオンタイズされた操作がほぼ不可
  • 余韻云々のためwavが分割されすぎている
  • ブロック遷移の振る舞い「何もしない」により、一度wav再生されると1フレーズ流れっぱなし

上記のためか、下記が起こる場合がある。

  • 同セクションループ時に、次ブロックサブシーケンスが再生される
  • 分割ブロックから遷移したとき繊維前の次波形が再生される

必要に応じて5~15ミリ秒程、各wavを後ろにずらす。

確認がすんだら書き出し

ついでにCSVで情報を書き出しておくと便利かも。

Unityに取り込む

ブロック制御するときの再生プログラム

CriAtomExPlaybackで次ブロック指定や現在の再生情報の取得を行うことができる。

宣言

CriAtomExPlayer player;
CriAtomExPlayback playback;

//CSVからブロック名を抽出すると楽。識別しやすい命名にしよう!!!!!!!
enum SectionState : int {
    fillin
    , _7776_123
    , _7776_4
    , _7776_567
    , _7776_8
    , _7778_8
    , piano_123
    , piano_4
    , DHE_1_peak
    , DHE_2to8and1peakless
    , end2
    , end1
}

再生ファイル指定

player = new CriAtomExPlayer(true);

//Assets\StreamingAssets以下を参照してファイル指定
player.SetCue(CriAtom.GetAcb("CharactorSelect_Tutorial"), 1);

再生

playback = player.Start();

現在のブロック取得と遷移先ブロック指定

int cur = playback.GetCurrentBlockIndex();
if(cur==(int)SectionState._7776_123){
  playback.SetNextBlockIndex((int)SectionState._7778_8);
}

ブロック設定で触れた、

今回のAパートのような、「遷移先によって遷移タイミングを変える」を実装したい場合はブロックエンド時に限定してブロック数を増やしたほうが良い。プログラムで現在ブロックIndexはとれるが、現在の指定分割位置が取れないため

について、もし分割で設定してしまっている場合は、別途フラグの用意、シーケンス位置で判定してどうにかする(敗北)。
曲前半であるほどブロックの差し込みによる手間が爆増するため、データ修正をあきらめてコードを書く。

const long _7776_3_START = 6884;
const long _7776_3_END = 9558;

const long _7776_7_START = 17202;
const long _7776_7_END = 19877;

bool is7776_3or7() {
    long seqPos = playback.GetSequencePosition();
    return (seqPos >= _7776_3_START && seqPos <= _7776_3_END) ||
      (seqPos >= _7776_7_START && seqPos <= _7776_7_END);
}

----

//ゲーム状態操作時
if (is7776_3or7()) {
    playback.SetNextBlockIndex((int)SectionState._7778_8);
} else {
    waitFor7776_3or7 = true;
}

----

//遷移タイミング待ち
if(is7776_3or7() && waitFor7776_3or7){
   playback.SetNextBlockIndex((int)SectionState._7778_8);
   waitFor7776_3or7 = false;
}

多重再生が発生しないか確認

地獄は終わらない…

ツールで多重再生などが発生しないことを確認したにもかかわらず、Unity上で発生してしまう… 各wavをさらに8msほど遅くして解決。

ビルドして、Application.targetFrameRateでFPSを下げたり、PCの省電力設定で多重再生が起きないか確認。
ほかのプロセスで走ってるはずなのでFPSは確認の意味ない気もする。

実装完了!

お疲れさまでした。

その他気になった点など

  • ツール付属のマニュアル.chmファイルよりウェブ公開されてるユーザーズマニュアルのほうが詳細。
  • 多重再生については読み込み速度や処理速度の問題か?と見ていたが、ツールデフォルトのオンメモリ再生から変更していないので、サンプル位置の調節でしか対処できない?
    • CriAtomExPlayer.Prepare()はストリーミング再生での事前読み込みなので関係ない。

今後の製作でIMをどう用いるか

今回横遷移のIMを実装したのは、曲の展開がある場合だと横遷移だろうと決めつけていたため。(F-ZERO GXの制御だけ想定していた)
縦で楽器の差し抜きに限らず曲展開も可能だということを、IMについて調べてる間に気づいたが、対応する曲制作が技術的に可能かわからない、データ作成の手間が多そう、変拍子で縦はさらに難度高そうで、今後やるかどうかはわからない。
(「この小節だけ1拍抜き」とかやりたいので縦で同期とるの絶望的では?)
縦を使うならコード進行に影響のないパーカッションの増減が最もエコノミーか。
強化状態になったらドラムをズンドコさせたバージョンに切り替えるとか?

IM用のデータを用意するなら

  • 拍子は自由でOK!
  • テンポチェンジは音楽的に必要がない場合は使用しない(演奏感のブレを求めて使用しない)
    • 次の作成時、テンポチェンジがない曲で多重再生が発生するのであれば、サンプル位置補正は作業手順に含めてしまう
  • ブロック遷移タイミングは基本ブロックエンドで、プログラム側で制御しやすくする。
  • 遷移可能タイミングを増やすと分割wav数が増えるので、絞る
    • 短いループに留めて縦のパート増減で尺稼ぎ?

以上、IMの試作でした。 今月はキャラセレ/チュートリアルの画面素材作成に入ります。