ゲーム開発
Unity
UnrealEngine
C++
ゲーム数学
ゲームAI
サウンド
アニメーション
GBDK
制作日記
3DCG
Houdini
Blender
USD
グラフィックス
テクノロジ
ツール開発
フロントエンド関連
サーバサイド関連
ソフトウェア設計
ハードウェア関連
おすすめ技術書
音楽
DTM
楽器・機材
ピアノ
その他
都会のエレキベア
ラーメン日記
四コマ漫画
おすすめアイテム
おもしろコラム
  • ゲーム開発
    • Unity
    • UnrealEngine
    • C++
    • ゲーム数学
    • ゲームAI
    • サウンド
    • アニメーション
    • GBDK
    • 制作日記
  • 3DCG
    • Houdini
    • Blender
    • USD
    • グラフィックス
  • テクノロジ
    • ツール開発
    • フロントエンド関連
    • サーバサイド関連
    • ソフトウェア設計
    • ハードウェア関連
    • おすすめ技術書
  • 音楽
    • DTM
    • 楽器・機材
    • ピアノ
  • その他
    • 都会のエレキベア
    • ラーメン日記
    • 四コマ漫画
    • おすすめアイテム
    • おもしろコラム
  1. ホーム
  2. 20240328_02_unity_cri_practice

【Unity】第二回 CRI ADXを使用したサウンド制御 〜インタラクティブミュージック編〜

サウンドUnityCRIADXインタラクティブミュージック
2024-03-28

マイケル
マイケル
みなさんこんにちは! マイケルです!
エレキベア
エレキベア
こんにちクマ〜〜〜
マイケル
マイケル
前回の記事に引き続き、CRIADXを使用したサウンド制御について触れていきます! 今回はサウンドミドルウェアの醍醐味である「インタラクティブミュージック」の実装例について紹介します! 具体的には下記のようなサンプルになります。
【Unity】第一回 CRI ADXを使用したサウンド制御 〜基本動作、周波数解析編〜
2024-03-28

▲前回の記事

20240327_01_unity-multi_audio_05
▲実装したインタラクティブミュージックのサンプル

エレキベア
エレキベア
おお〜〜〜 サウンドに合わせてオブジェクトが反応するのは見ていて楽しいクマね
マイケル
マイケル
サンプルプロジェクトのリポジトリと、CRI関連処理については下記になります! こちらも合わせてご参照ください!

GitHub - unity-multi-audio

▲サンプルプロジェクト

フォルダ
内容
CriAtomCraftプロジェクト
APIを実行する処理
サンプルゲーム固有のオーディオ関連処理
サンプルゲームに関する処理
CriAtomCraftプロジェクトから出力したサウンドデータ
エレキベア
エレキベア
中々ボリュームのあるサンプルクマ

参考書籍

マイケル
マイケル
今回実装するにあたり、下記書籍を参考にさせていただきました!

Unityサウンド エキスパート養成講座

マイケル
マイケル
UnityAudioとCRIADXを使用した実装について紹介されています。 ゲームサウンドにフォーカスした数少ない書籍なので、ぜひご一読ください!
エレキベア
エレキベア
Unityでのサウンド実装について幅広く知れる一冊クマね

インタラクティブミュージックの実装

マイケル
マイケル
それでは早速実装内容について見ていきます! インタラクティブミュージックの実装について、今回は下記について盛り込んでみました!
  • イントロループからのブロック遷移
  • オーディオ側からのイベント発行
  • ビート同期によるオブジェクト伸縮
  • AISAC値によるサウンド変化
エレキベア
エレキベア
いろいろ盛り込んでるクマね・・・
マイケル
マイケル
今回のために、サンプル実装がしやすい曲も作りました・・・ それぞれ見ていきましょう!

イントロループからのブロック遷移

マイケル
マイケル
最初に行ったのが「イントロループからのブロック遷移」処理です。 具体的にどういうことかというと、はじめはイントロをループしておき、プレイヤーが動き始めたらメインとなるブロックに遷移させるといった内容です!
20240327_01_unity-multi_audio_12
▲プレイヤーが動き始めるまでイントロをループさせる

エレキベア
エレキベア
なんかペルソナのダンジョン出発前とかで見たことがある気がするクマ
マイケル
マイケル
これを行うには、Cue内でブロックを作成して分割しておく必要があります。 今回は ・イントロ ・イントロループ ・メイン ・メインループ の4つのブロックに分割しました。

参考:CRI ADX Tools マニュアル - ブロック遷移

20240328_02_unity_cri_practice_01
▲Cueをブロック分割設定する

マイケル
マイケル
あとは下記のような関数を用意して、プレイヤーが動き始めたタイミングで遷移するブロックのインデックスを指定すれば完了です。 今回はメインブロックに遷移させたいので、インデックスは2になります。
        /// <summary>
        /// 遷移するブロックインデックスを設定する
        /// </summary>
        /// <param name="cueName">Cue名</param>
        /// <param name="nextBlockIndex">ブロックインデックス</param>
        public void SetNextBlockIndex(string cueName, int nextBlockIndex)
        {
            if (!_criAtomPlaybackCache.ContainsKey(cueName))
            {
                Debug.LogError($"not found CriAtomExPlayback => {cueName}");
            }
            _criAtomPlaybackCache[cueName].SetNextBlockIndex(nextBlockIndex);
        }
▲ブロックインデックスの指定
        /// <summary>
        /// ブロック遷移状態を設定する
        /// </summary>
        /// <param name="state">遷移状態</param>
        public void SetNextBlockState(IGameAudioService.NextBlockState state)
        {
            switch (state)
            {
                case IGameAudioService.NextBlockState.BgmAtomChainIntro:
                    // イントロ再生時は何もしない
                    break;
                case IGameAudioService.NextBlockState.BgmAtomChainMain:
                    // 対象インデックスに進める
                    const int playBlock = 2;
                    var eventName = GameCriAdxAudioSettings.CriAdx.CueName.BgmAtomChain;
                    _criAdxApiService.SetNextBlockIndex(eventName, playBlock);
                    break;
            }
        }
▲ブロック遷移処理
エレキベア
エレキベア
ツール側との連携で実装するクマね

オーディオ側からのイベント発行

マイケル
マイケル
次は曲がメインの部分に入ったらオブジェクトを表示&背景色を変更するという処理になります。
20240327_01_unity-multi_audio_05
▲メインの小節に入ったらオブジェクトと背景を表示する

マイケル
マイケル
これにはCueの中にコールバックを設定することで検知できるようにしました。 設定したタグ名をスクリプト側で受け取ってコールバック登録すれば実装は完了です。

参考:CRI ADX Tools マニュアル - シーケンスコールバックマーカー

20240328_02_unity_cri_practice_02
▲曲にコールバックを埋め込む


        /// <summary>
        /// イベントコールバックの設定
        /// </summary>
        /// <param name="tagName">タグ名</param>
        /// <param name="callback">コールバック</param>
        public void SetSequenceCallback(string tagName, Action callback)
        {
            // 本開発時にはremoveも考慮する必要がある
            CriAtomExSequencer.OnCallback += (ref CriAtomExSequencer.CriAtomExSequenceEventInfo info) =>
            {
                if (info.tag == tagName)
                {
                    callback.Invoke();
                }
            };
        }
▲シーケンスコールバックの設定
        /// <summary>
        /// サウンドイベント再生
        /// </summary>
        /// <param name="eventName">サウンドイベント名</param>
        /// <param name="option">再生オプション</param>
        public void PlaySoundEvent(string eventName, IGameAudioService.SoundPlayOption option = null)
        {
            _criAdxApiService.Play(GameCriAdxAudioSettings.CriAdx.GetCueSheetName(eventName), eventName,
                new ICriAdxApiService.SoundPlayOption()
            {
                FadeTimeMs = option?.FadeTimeMs ?? 0,
            });

            // コールバックイベントの追加
            if (option?.BeatSyncCallback != null)
            {
                _criAdxApiService.SetBeatSyncCallback(
                    GameCriAdxAudioSettings.CriAdx.GetCueSheetName(eventName), option.BeatSyncLabel, option.BeatSyncCallback);
            }
            if (option?.CustomEventCallback != null)
            {
                _criAdxApiService.SetSequenceCallback(option.CustomEventName, option.CustomEventCallback);
            }
        }
▲再生時に設定している
エレキベア
エレキベア
任意のタイミングで実行できるのはいろいろと応用が効きそうクマね

ビート同期によるオブジェクト伸縮

マイケル
マイケル
次はBGMのリズムに合わせてオブジェクトを伸縮させる処理についてです。 こちらはHi-Fi Rush等でよく見られるような挙動ですね
20240328_02_unity_cri_practice_03
▲今回はSphereとBoxを対象とした

マイケル
マイケル
これを行うには、予めキューにBGMや小節等のビート同期情報を設定しておく必要があります。 コールバックを設定する際に、設定ラベル名を見て判別するようにすれば実装は完了です。

参考:CRI ADX Tools マニュアル - ビート同期

20240328_02_unity_cri_practice_04
▲ビート同期情報を予め設定しておく

        /// <summary>
        /// ビート同期イベントコールバックの設定
        /// </summary>
        /// <param name="cueSheetName">CueSheet名</param>
        /// <param name="labelName">ラベル名</param>
        /// <param name="callback">コールバック</param>
        public void SetBeatSyncCallback(string cueSheetName, string labelName, Action callback)
        {
            var criAtomExPlayer = GetCriAtomExPlayer(cueSheetName);
            if (criAtomExPlayer == null)
            {
                return;
            }

            // 本開発時にはremoveも考慮する必要がある
            criAtomExPlayer.OnBeatSyncCallback += (ref CriAtomExBeatSync.Info info) =>
            {
                if (info.label == labelName)
                {
                    callback?.Invoke();
                }
            };
        }
▲ビート同期イベントの設定
エレキベア
エレキベア
これだけでリズムを取得することができるクマね
マイケル
マイケル
なお、SphereやBoxといった伸縮させるオブジェクトには、下記のようなインターフェイスを実装してビート数を受け取るよう実装しています。 こちらも気になる方はリポジトリの実装を参照してみてください!
namespace GameSample.Objects.BeatSync
{
    /// <summary>
    /// ビート同期オブジェクト
    /// </summary>
    public interface IBeatSyncBehaviour
    {
        /// <summary>
        /// ビート同期
        /// </summary>
        /// <param name="beatCount">ビート数</param>
        public void OnBeatSync(int beatCount);
    }
}

▲ビート同期オブジェクトのインターフェイス

AISAC値によるサウンド変化

マイケル
マイケル
最後に味付けにはなりますが、ボスに近づくほどに別のギターパートの音量を上げるよう変化させてみました。 こちらはAISACという機能を使用して、ゲームから設定したパラメータに応じてサウンド側のパラメータも編集させることで実現しています。
20240328_02_unity_cri_practice_06
▲ボスに近づくほどにギターパートの音量を上げるように変化させる

CRI ADX Tools マニュアル - AISAC

20240328_02_unity_cri_practice_05
▲AISAC値によるボリュームコントロール設定例

マイケル
マイケル
スクリプトからの呼び出しは簡単で、下記のようなAISAC値の設定関数を用意します。 ボスとの距離に応じてこの値を0-1の間で設定するようにすれば完了です!
        /// <summary>
        /// AISACコントロール値の設定
        /// </summary>
        /// <param name="cueSheetName">CueSheet名</param>
        /// <param name="controlName">AISACコントロール名</param>
        /// <param name="value">設定値</param>
        public void SetAisacControl(string cueSheetName, string controlName, float value)
        {
            var criAtomExPlayer = GetCriAtomExPlayer(cueSheetName);
            criAtomExPlayer?.SetAisacControl(controlName, value);
        }
▲AISAC値の設定
        /// <summary>
        /// ゲーム内パラメータを設定する
        /// </summary>
        /// <param name="eventName">サウンドイベント名</param>
        /// <param name="parameterName">パラメータ名</param>
        /// <param name="value">パラメータ値</param>
        public void SetGameParameter(string eventName, string parameterName, float value)
        {
            _criAdxApiService.SetAisacControl(GameCriAdxAudioSettings.CriAdx.GetCueSheetName(eventName), eventName, parameterName, value);
        }
▲ゲーム側の設定
エレキベア
エレキベア
ゲームパラメータに応じてサウンドを変化させるクマね これもいろいろと応用が効きそうな機能クマ

おわりに

マイケル
マイケル
というわけで今回はCRIを使用してインタラクティブミュージックを実装してみました! どうだったかな?
エレキベア
エレキベア
ミドルウェアに搭載されている機能を活用して いろんな演出が出来て楽しかったクマ〜〜
マイケル
マイケル
今回は無理やり詰め込んだサンプルになったけど、こんな感じでサウンドの細かい調整や凝った演出ができるのがサウンドミドルウェアの大きな魅力だね! 他にもいろんな機能が用意されているから、ぜひ活用していこう!!
マイケル
マイケル
CRIの実装解説は以上になります。 次回以降はWwiseの実装について触れていきますので、お楽しみに!!
エレキベア
エレキベア
クマ〜〜〜〜〜

【Unity】第二回 CRI ADXを使用したサウンド制御 〜インタラクティブミュージック編〜 〜完〜

【Unity】第一回 UnityAudioを使いこなす 〜サウンド再生処理編〜
2024-01-22
【Unity】第二回 UnityAudioを使いこなす 〜AudioMixer活用編〜
2024-01-22
【Unity】第一回 Wwiseを使用したサウンド制御 〜基本動作編〜
2024-03-30
【Unity】第二回 Wwiseを使用したサウンド制御 〜インタラクティブミュージック編〜
2024-03-30
【Unity】サウンドミドルウェアに依存しない設計を考える【CRI ADX・Wwise】
2024-03-27

サウンドUnityCRIADXインタラクティブミュージック
2024-03-28

関連記事
【Unity】Timeline × Excelでスライドショーを効率よく制作する
2024-10-31
【Unity】Boidsアルゴリズムを用いて魚の群集シミュレーションを実装する
2024-05-28
【ゲーム数学】第九回 p5.jsで学ぶゲーム数学「フーリエ解析」
2024-05-12
【Unity】GoでのランキングAPI実装とVPSへのデプロイ方法についてまとめる【Go言語】
2024-04-14
【Unity】第二回 Wwiseを使用したサウンド制御 〜インタラクティブミュージック編〜
2024-03-30
【Unity】第一回 Wwiseを使用したサウンド制御 〜基本動作編〜
2024-03-30
【Unity】第一回 CRI ADXを使用したサウンド制御 〜基本動作、周波数解析編〜
2024-03-28
【Unity】サウンドミドルウェアに依存しない設計を考える【CRI ADX・Wwise】
2024-03-27