Unity + ADX2におけるサウンドデータの読み込みと破棄


Unity for ADX2導入後のデータ読み込み

CRI ADX2 および ADX2 LEは、ゲーム開発環境においてサウンドの演出開発を今日曲にサポートするミドルウェアです。
イントロ付きループBGMのストリーミング再生や、Androidにおける再生遅延の対策機能などの性能的アドバンテージもあります。

ADX2を導入する際に考える必要があることとして、音声データハンドリングの要素があります。
Unity Audioはシーンに紐付いたAudio Clipを自動的にロードしたり、スクリプトから任意のタイミングでメモリに展開することができます。

ADX2の場合も、音声データのマウントやロードを明示的に行うことでパフォーマンスの改善ができます。
本記事では、当社で開発中のゲーム「デモリッション ロボッツ K.K.」の事例を使いながら解説します。
ADX2を全く初めて触る、という場合は以下の記事を先にご覧ください。

Unityのサウンド機能をADX2で強化する
https://qiita.com/Takaaki_Ichijo/items/16e6501fc07f5b3b3377

ADX2には製品版のADX2と、個人開発者向けの無償版ADX2 LEの2つのエディションがあります。
本記事では、無償版である「ADX2 LE」のv2.10.05を使用しています。

CRI ADX2 LE
https://game.criware.jp/products/adx2-le/

ADX2が出力するファイルの種類

ADX2では、waveファイルなど元の音声データをUnity Editorにインポートせず、別ツールである「Atom Craft」を使って再生用データを作ります。これはADX2のSDKに含まれています。

Atom Craftでは、専用コーデック「HCA」への圧縮のほか「この音はフェードインアウト」「この音はランダムにピッチが変わる」といったメタデータ的な情報を音に埋め込みます。

データの出力は、複数の音をまとめたバイナリファイルになります。たとえば「システムSE」「キャラクターAのセリフ」「ステージ1のBGM」のように、用途によって分け、ひと塊のデータとして出力します。ゲーム実行時にはこの塊単位でロードします。
UnityからADX2のランタイムを介してそれらのデータを読み込み、スクリプトで再生、というフローになります。

Atom Craftからは「ACF」「ACB」「AWB」の3種類のデータが出力されます。

  • 「ACF」ファイルは、ゲームプロジェクト1つにつき1個の「全体設定」ファイルです。ゲームの開始時に読み込まれます。
  • 「ACB」ファイルが音データを含むバイナリファイルで、いくつかの音をまとめて固めています。圧縮音声データをメモリに乗せて再生(Unity Audioで言うところのCompressedInMemory設定)するためのファイルです。
  • 「AWB」は、ストリーミング再生(Unity AudioにおけるStreaming設定)を使う場合に登場します。ストリーミング再生の場合もACBファイルが必要です。

ADX2 for Unityにおけるファイルハンドリング

ゲーム起動時に全部ロードする

CRI ADX2は、「Cri Atom」コンポーネントをシーンに配置して使います。デフォルトでは「CRIWARE」ゲームオブジェクトにアタッチされています。
これはゲーム中1つだけ存在しており、ADX2のシステムがロードした音声データを管理します。

もっとも簡単な利用方法は、ゲーム中に使うサウンドデータの参照をすべてインスペクターで設定してしまうことです。
「Add CueSheet」をクリックすると、ACFファイル、AWBファイルのファイルパスを文字列で指定しておくことができます。

上の図ではシステム用音声データのacfと、BGMのacf/awbを起動時にロードする設定です。
(ファイルパスはルートを省略するとStreamingAssetsから読むようになります、この場合はAssets/StreamingAssets/audioにデータが入っている想定です)

ですが、ゲームの起動直後に必要な音というのはあまりありません。BGMやキャラクターのボイスデータ、技の効果音など、複数大量にある音声データはゲームの場面に合わせてロードとアンロードを行うべきです。

ゲーム起動後に任意のタイミングでロードする

さて本題です。CriAtom.csのインスペクターでacb, awbファイルを登録せず、ゲーム起動後に任意のタイミングでロードを行うコードを用意します。
具体的には、次のようなメソッド経由でロードします。

CRIAtomCueSheetLoad.cs

    private const string adx2FileDirectoryName = "ADX2Files";
    private CriAtomExAcb cueSheet;
    private CriAtomExPlayer criAtomExPlayer;

    private void Awake()
    {
        criAtomExPlayer = new CriAtomExPlayer();
    }

    private async Task LoadCueSheet(string cueSheetName)
    {
        CriAtom.AddCueSheetAsync(cueSheetName, Path.Combine(adx2FileDirectoryName, cueSheetName + ".acb"), "");

        await UniTask.WaitUntil(() => CriAtom.CueSheetsAreLoading == false);

        cueSheet = CriAtom.GetCueSheet(cueSheetName).acb;
    }

    public void Play(string cueName)
    {
        criAtomExPlayer.SetCue(cueSheet, cueName);
        criAtomExPlayer.Start();
    }

これは、ディレクトリAssets/StreamingAssets/ADX2Files/下に配置されているacbファイルをロードする非同期処理です。
開発中のゲーム「デモリッションロボッツKK」では、ゲーム中のメニューUIのボタン音などをこのファイルに固めており、ゲーム起動時のサークルロゴ表示中に読み込んでいます。

非同期のロード処理を呼んで、キューシートのロード処理が終わるまで待ちます。
* UniTask.WaitUntilはUniTaskの機能です。簡単に条件待ちができて便利。

コルーチンを利用して実装する場合は次のコードになります。

CRIAtomCueSheetLoadCoroutine.cs

    public IEnumerator LoadCueSheetCoroutine(string cueSheetName, string path)
    {
        CriAtom.AddCueSheetAsync(cueSheetName, path, "");

        while (CriAtom.CueSheetsAreLoading == true)
        {
            yield return null;
        }

        cueSheet = CriAtom.GetCueSheet(cueSheetName).acb;
    }

データの多重ロードに注意

ADX2 for Unityを使ったサウンドの再生は、標準のUnity Audioと異なり、シーンを破棄してもデータが自動でアンロードされません。
たとえばステージ毎に違うBGMを読み込んでいる場合などは、明示的なアンロードが必要です。
アンロードはひとつメソッドを呼ぶだけです。

UnloadAdx2Data.cs

CriAtom.RemoveCueSheet("StageBGM");

アンロードはキューシート名を指定します。この呼び出しをシーン破棄処理と同時に行うことが大事です。

「デモリッションロボッツKK」では、ステージのシーンにBGMのacb, awbデータの名前を保持しておき、ステージデータの初期化時にCriAtom.AddCueSheetAsyncを読んでいます。
ところが、最初はこのアンロード処理を忘れており、タイトル画面に戻って同じステージをやり直すたびに同じデータがメモリにどんどん読まれてしまっていました。
そうなってくるとメモリを圧迫するほか、CRI Atom Initializerで指定したファイルマウント数を超えるとエラーが出て音が再生できなくなります。使わなくなったBGM、ボイス、環境音などは適宜アンロードしておくとよいでしょう。

場面に応じた音声データのロードとアンロード

基本的に、繰り返し使う短い音はメモリに常駐させておくべきです。たとえばUIのボタン音や、キャラクターの足音、通常攻撃ヒット音などです。
これらは再生レスポンスが重要であるため、ロードを挟まずに再生できると理想的です。

逆に、ある場面で1度しか使わない音声データは、必要になる直前にロードして再生し終わったら破棄するとよいでしょう。カットシーン中のセリフデータなどがこれにあたります。
プレイヤーキャラクターの切り替えなど、プレイヤーの捜査によって常駐させておく音声データが変わる場面もありえます。

また、BGMや環境音など、再生レスポンスが気にならない音に関しては、ストリーミング再生の設定にしておきましょう。
ストレージから少しづつデータを読むストリーミング再生なら、CriAtomがずっとacbファイルを握っていても、メモリ使用量は大きくはなりません。

その他のADX2ファイルハンドリング

ADX2のデータとAsset Bundle

ADX2のデータは、UnityがSerializedしたデータではないのでStreamingAssets下に置き、バイナリファイルとして読み込んで再生できます。
png等の画像データを外部から読み込むときと同様です。そのため、Unityの外からデータを取得したい場合も、AssetBundleファイルに含めずにサーバー等から直接取得して使用できます。
外のサーバーからデータを読み込んで再生するサンプルについては次の記事にまとめています。

Unity + PlayFab + ADX2で、Asset Bundleを介さないサウンドデータの配信を実装する
https://qiita.com/Takaaki_Ichijo/items/bc6ef09fa55d496e5800

(研究課題)Resource Managerとの兼ね合い

Addressable Asset Systemの基盤であるリソースロードシステムResource Managerを使ってADX2にデータを渡したい、あるいは必要なデータを取ってきたいという要望を耳にはさみました。こちらも調査の上、記事を用意します。