CRI ADX2 + Addressables + 宴のマシマシセットを動かす

こちらはUnity Advent Calendar 2022 12/7の記事です

CRI ADX2(以下ADX)のAddressables対応およびUnity用ビジュアルノベルツール「宴」への導入について紹介します。まずADXとAddressablesの連携について紹介し、次に宴への導入について解説します。

ADXのAddressables対応

これまでのADXとAddressablesの状況

ADXのACBファイルなどはバイナリであるため、これまではUnityアセットとして取り込めませんでした。

AddressablesはUnityアセットを読み込む仕組みであるため、ADXのファイルはAddressablesに非対応であり、一時的に拡張子を.bytesや.txtなどのTextAssetとして認識させる方法を取っていました。

以前の方法はこちらにまとめています。

speakerdeck.com

一方でこの方法はメモリ効率が悪かったり、拡張子を書き換えるため編集がしにくかったりと課題が存在していました。

公式によるサポート

これらの課題を解決するため、CRIWAREから公式のサポートプラグインがリリースされました。

game.criware.jp

ADXのファイルがUnityアセットとして認識されることで、Addressablesに対応できるだけでなく、Unity上でCUEや音の確認までできるようになりました。また、これまでは必ずStreamingAssetsフォルダに配置していましたが、Addressablesと連携することでファイルの配置場所も自由に変更できるようになります。

また、プラグインは無償版のCRI ADX LEでも使えるようになっています。ありがたや。

プラグインの詳細

サポートプラグインには以下の2つのプラグインが存在します。

  • CRI Assets 公式ドキュメント
    • ADXのファイルをUnityアセットとして認識させる機能
    • ファイルパスを指定したロードではなく、アセットの参照(インスペクタでのアタッチ)が可能になっている
    • 単体で使用可能で、Addressablesを使っていない場合でもオススメ
  • CRI Addressables 公式ドキュメント
    • CRI Assetsで認識したADXファイルをAddressablesでロード可能にする機能
    • CRI Assetsと併用する前提で使用

プラグインの使用方法

公式ドキュメントを参考にプラグインをプロジェクトに導入します。 導入自体はunitypackagesをプロジェクトに入れるだけです。

注意点としてはプラグインではAssembly Definitionの対応版を使用するため、プロジェクトにもAssembly Definitionの導入が必要かもしれません。

実はプラグインを導入したとしても既存の仕組みであるファイルパスでのロードもそのまま併用して使えます。音源ファイルをUnityに取り込まず、独自でダウンロードして使う場合など、ファイルパスを指定してロードしたほうが都合がよいケースもあります。プロジェクトの運用に応じて方法を変えられます。

ADXファイルのロード

具体的なAddressablesを利用したADXファイルのロード手順です。

  1. ADXファイルのDeployTypeを設定する
    1. アプリに含める場合はAddressables(Local)
    2. 外部リソースはAddressables(Remote)にする
  2. ADXファイルをAddressablesに登録する
  3. 以下のスクリプトで読み込む(UniTaskを使用)
// keyでロードするアセットを指定
var handle = Addressables.LoadAssetAsync<CriAtomAcbAsset>(key);
var acbAsset = await handle.Task;

// 必要に応じてロードを実行
if (!acbAsset.Loaded && !acbAsset.LoadRequested)
{
    acbAsset.LoadAsync();
}

// ロード完了まで待機
await UniTask.WaitUntil(() => acbAsset.Loaded);

return acbAsset.Handle;

上記はADXのファイルをCriAtomExAcbとして返すため、CriAtomExPlayerのSetCueに渡して再生します。

Addressablesでアセットのロードが完了した後にACBのロードを行います。そのためLoadAsyncでロードを行い、UniTask.WaitUntilでロードが終わるまで待つといった処理を行います。

CRI Assetsを利用したインスペクタでの音源指定は一連の処理を自動で行ってくれるため、動的に音源が変わらない場合はそちらの使用をオススメします。

宴との連携

宴について

はUnity用ビジュアルノベルツールで、エクセルでシナリオを作成することで簡単にノベルゲームを実現できます。ノベルゲームとしての基本の機能が揃っているだけでなく、必要に応じて改変しやすい作りになっています。また、国内で開発しているためサポートが厚いこともメリットです。ありがたや。

宴のAddressables対応

宴の標準ではResourcesフォルダや独自のAssetBundle管理の仕組みを用いてアセットのロード・管理を行いますが、開発者のサポートによりAddressablesでのロードにも切り替えられるようになっています。 公式ドキュメントに従いセットアップすることで、宴内部のアセットローダーが差し代わり、Addressablesでのロードが可能になります。

宴でのADXファイルロード

標準の仕組みからAddressablesへの移行について、画像ファイルはそのままスムーズに移行できますが、ADXファイルはひと手間必要です。 ADX2の使用方法を参考にサウンド再生にADX2を使うことはできますが、こちらはAddressablesに対応していません。そのため一部改変を行います。

  1. 上記ドキュメントを参考に宴の拡張を行う
    1. サウンドシートへのCueSheet列の追加が忘れがち
    2. 拡張コードの説明は解説なので対応は不要です
  2. 宴のスクリプトを以下のように変更する

UtageForAddressableCustomFileManagerのFindAsset

void FindAsset(AssetFileManager mangager, AssetFileInfo fileInfo, IAssetFileSettingData settingData, ref AssetFileBase asset)
{
    // サウンドデータならAddressablesCustomFileManagerからのロードはスキップする
    if (fileInfo.Setting.FileType == AssetFileType.Sound)
    {
        return;
    }
    asset = new UtageForAddressableCustomFile(mangager, fileInfo, settingData);
}

これによりSound指定のファイルはUtageForAddressableCustomFileManager経由ではなく、Adx2LeForUtage経由でAssetFileBaseが更新されます。 つまり、ADXのファイルはUtageForAddressableCustomFileではなく、Adx2AssetFileとして読み込みましょうということです。

Adx2AssetFileのLoadAsync

public override IEnumerator LoadAsync(Action onComplete, Action onFailed)
{
    if (CriAtom.GetCueSheet(this.CueSheet) == null)
    {
        // Addressableからの取得に変更
        var key = CueSheet;
        var handle = Addressables.LoadAssetAsync<CriAtomAcbAsset>(key);
        AcbAsset = handle.WaitForCompletion();
        CriAtomAssetsLoader.AddCueSheet(AcbAsset);

        // 元のコード
        // string acbPath = this.CueSheet + ".acb";
        // string awbPath = this.CueSheet + ".awb";
        // CriAtom.AddCueSheet(this.CueSheet, acbPath, awbPath);
    }
    IsLoadEnd = true;
    if (onComplete != null) onComplete();
    yield break;
}
// 取得したAcbAssetは外から利用するのでプロパティを定義しておく
public CriAtomAcbAsset AcbAsset { get; private set; }

ファイルパスを指定しての読み込みからCriAtomAcbAssetとAddressablesを利用した読み込みに変更します。 読み込んだAcbAssetはロードのためCriAtomAssetsLoaderに追加します。 外部公開したAcbAssetは次のAdx2Audioで使用します。

Adx2Audioの変更

// プロパティのSourceをForAssetのほうに変更する
public CriAtomSourceForAsset Source { get { return source ? source : (source = this.gameObject.AddComponent<CriAtomSourceForAsset>()); } }
CriAtomSourceForAsset source;
// 元コード
// public CriAtomSource Source { get { return source ?? (source = this.gameObject.AddComponent<CriAtomSource>()); } }
// CriAtomSource source;

// CoWaitDelayの内部を以下に変更
IEnumerator CoWaitDelay(float fadeTime, float delay)
{
    isFadeOuting = false;
    Adx2AssetFile adx2File = Data.File as Adx2AssetFile;
    // Source.cueNameからadx2File.CueNameに変更
    if (Adx2LeForUtage.DebugLog) Debug.Log(string.Format("Play {0} FadeIn{1}", adx2File.CueName, fadeTime));
    Source.volume = 0;
    Source.loop = Data.IsLoop;
    
    // CueSheetとCueNameでの指定をやめ、取得したAcbAssetを元にCriAtomCueReferenceを作成し指定する
    Source.Cue = new CriAtomCueReference(adx2File.AcbAsset, 0);
    // 元コード
    // Source.cueSheet = adx2File.CueSheet;
    // Source.cueName = adx2File.CueName;

CriAtomSourceではファイルパスからのロードのためCueSheetとCueNameを指定し再生を行っていましたが、Addressablesを利用するCriAtomSourceForAssetではアセットを直接指定しているため、AcbAssetのインスタンスを元にCueを構築します。 また、Source.cueNameのプロパティは取得できなくなるため、デバッグなどで表示する場合は代わりにadx2File.CueNameやSource.Cue.AcbAsset.nameで取得します。

以上の更新によりCRI ADX2+Addressables+宴の構成でも音源を鳴らすことができ、各ミドルウェアのいいとこどりを実現しています。