CRI Assets/AddressablesでCriAtomExPlayerを使って再生
2022年6月24日にADX LEでAsset Support Add-onが使えるようになりました。
これでUnityのAddressable Asset Systemを使ってCRI Assetsを読み込めます。
個人的には待望の機能。ありがとうCRIWARE
Asset Support Add-onには以下が含まれます。
- CRI Assets
- CRI Addressables
CRI Assetsが何かというと、要するに.acf、.awb、.acbファイルのことです。
今まではこれらをただのファイルとして扱っていましたが、Unityアセットとして扱えるようになります。
CRI Addressablesは、Unityアセット化した.acf、.awb、.acbファイルをAddressable Asset System(AASやAddressablesなどと言われる)で読み込めるようになります。
なおAASはリソース管理のためのシステムで、ローカルやサーバーにあるリソースの読み込みを補助してくれます。便利なので使いたかったのですが、今まではADXが対応していないので二の足を踏んでいました。
これでようやくAASの移行に踏み切れるわけです。
ということで対応を行っていたのですが、CriAtomSourceを使わずにCriAtomExPlayerを使っている既存の処理をどう置き換えればいいのかよく分かりませんでした。
実装までこぎつけたのでどうぞ。
本記事はCriAtomSourceを使わず、CriAtomExPlayerを使いたい方向けです。
CriAtomSourceを使っている場合は、GameObjectにアタッチしているCriAtomをCriAtomAssetsに置き換え、同じくCriAtomSourceをCriAtomSourceForAssetに置き換えて、awbファイルとacbファイルのDeploy Typeを変えれば動くと思います。
前後のソースコード
前提条件
- 初期化設定を行っている
- CriAtomSourceは使わない
- 音源はすべてストリームで再生する(acbファイルとawbファイルがある)
- acfファイルはEditor上で事前に指定
予め説明すると、スクリプトでacbファイルとawbファイルをADXに渡し、キューシートを追加して再生する処理です。
音源をストリームで再生すること以外は公式ブログで解説されている内容と同じシチュエーションですし、一般的な使い方の範疇でしょう。
初期化設定については以下を参照。
従来の方法で音源を再生
CriAtomExPlayerのラッパークラス
AddSoundSourceメソッドの引数に注目。
キューシート名、acbファイルパス、awbファイルパスの3つです。
using CriWare;
using System.Collections;
public class SoundPlayHandler
{
private string _cueSheetName;
private CriAtomExPlayer _criAtomExPlayer;
private CriAtomExPlayback _criAtomExPlayback;
private CriAtomCueSheet _criAtomCueSheet;
public SoundPlayHandler(bool isEnableAudioSyncedTimer)
{
_criAtomExPlayer = new CriAtomExPlayer(isEnableAudioSyncedTimer);
}
public void AddSoundSource(string cueSheetName, string addressAcbFilePath, string addressAwbFilePath)
{
_cueSheetName = cueSheetName;
_criAtomCueSheet = CriAtom.AddCueSheet(_cueSheetName, addressAcbFilePath, addressAwbFilePath);
}
public IEnumerator PreparePlay()
{
// キューシートの読み込みが終わっていない場合は完了を待つ
while (_criAtomCueSheet.IsLoading)
{
yield return null;
}
// キュー設定
_criAtomExPlayer.SetCue(_criAtomCueSheet.acb, _cueSheetName);
// 再生開始時間設定
_criAtomExPlayer.SetStartTime(0);
// 再生準備開始
_criAtomExPlayback = _criAtomExPlayer.Prepare();
// 再生準備完了待ち
while (_criAtomExPlayback.GetStatus() != CriAtomExPlayback.Status.Playing)
{
yield return null;
}
}
public void Play(CriAtomEx.ResumeMode resumeMode)
{
_criAtomExPlayer.SetStartTime(0);
_criAtomExPlayback.Resume(resumeMode);
}
}
呼び出し元
呼び出し元からキューシート名、ACBファイルパス、AWBファイルパスを指定して再生します。
SoundPlayHandler handler = new SoundPlayHandler(true);
handler.AddSoundSource("TestCueSheet", "Test.acb", "Test.awb");
yield return StartCoroutine(handler.PreparePlay());
handler.Play(CriAtomEx.ResumeMode.PreparedPlayback);
Asset Support Add-onを使って音源を再生
CriAtomExPlayerのラッパークラス
AddSoundSourceメソッドの引数がacbファイルパスのみになりました。
キューシート名を引数からなくしたのは、CriAtomAcbAssetのnameで取得できるためです。
awbファイルについては後述。
なおAASでacbファイルを読み込むため、acbファイルパスはAAS上のパスになることに注意。
using System.Collections;
using UnityEngine;
using UnityEngine.AddressableAssets;
using UnityEngine.ResourceManagement.AsyncOperations;
using CriWare;
using CriWare.Assets;
public class SoundPlayHandler
{
private CriAtomExPlayer _criAtomExPlayer;
private CriAtomExPlayback _criAtomExPlayback;
private CriAtomAcbAsset _criAtomAcbAsset;
public SoundPlayHandler(bool enableAudioSyncedTimer)
{
_criAtomExPlayer = new CriAtomExPlayer(enableAudioSyncedTimer);
}
public void AddSoundSource(string addressAcbFilePath)
{
// 音源取得
var handle = Addressables.LoadAssetAsync<CriAtomAcbAsset>(addressAcbFilePath);
_criAtomAcbAsset = handle.WaitForCompletion();
// 音源登録
CriAtomAssetsLoader.AddCueSheet(_criAtomAcbAsset);
}
public IEnumerator PreparePlay()
{
// キューシートの読み込みが終わっていない場合は完了を待つ
while (!_criAtomAcbAsset.Loaded)
{
yield return null;
}
// キュー設定
_criAtomExPlayer.SetCue(_criAtomAcbAsset.Handle, _criAtomAcbAsset.name);
// 再生開始時間設定
_criAtomExPlayer.SetStartTime(0);
// 再生準備開始
_criAtomExPlayback = _criAtomExPlayer.Prepare();
// 再生準備完了待ち
while (_criAtomExPlayback.GetStatus() != CriAtomExPlayback.Status.Playing)
{
yield return null;
}
}
public void Play(CriAtomEx.ResumeMode resumeMode)
{
_criAtomExPlayer.SetStartTime(0);
_criAtomExPlayback.Resume(resumeMode);
}
}
CriAtomCueSheetだった内部プロパティがCriAtomAcbAssetに変わっているため、キューシート周りの処理も微妙に変わっています。
従来の方法に比べてすっきりしたコードになった印象です。
呼び出し元
呼び出し元もすっきりしました。
SoundPlayHandler handler = new SoundPlayHandler(true);
handler.AddSoundSource("Assets/AddressablesTest/BGM.acb");
yield return StartCoroutine(handler.PreparePlay());
handler.Play(CriAtomEx.ResumeMode.PreparedPlayback);
GameObjectの変更
acfファイルの指定方法
初期化設定を行っていればCRIWAREオブジェクト(Cri Atomスクリプトがアタッチされてるやつ)があります。
Cri Atomスクリプトコンポーネントを削除してCri Atom Assetsスクリプトコンポーネントを追加し、それにacfファイルを指定します。
- 通常時
→Cri Atomスクリプト - Asset Support Add-onを使った時
→Cri Atom Assetsスクリプト
ということですね。
マニュアルにも記載があります。
CRI Assetsを利用する場合、不要になるコンポーネントがいくつかあります。
また、利用しなくなったコンポーネントに代わって同等の機能を持ったコンポーネントがCRI Assets向けに追加されています。
利用しなくなったコンポーネント
CriAtom
CriAtomSource追加されたコンポーネント
CRIWARE Unity Plugin Manual: 従来の利用方法からの移行時の注意点
CriAtomAssets
CriAtomSourceForAsset
awbファイルはどこ行った?
スクリプトを見ると分かるのですが、まるでawbファイルの存在がなくなったかのようでした。なぜか?
Asset Support Add-onを使った時は、acbファイルにawbファイルを紐づけるためです。
プロジェクトウィンドウからacbファイルを選んでみましょう。
ここでAwbからファイルを指定してください。
今回はAASを使ってacbファイルを読み込むので、Deploy Typeも「Addressables (Local)」に変えておく必要があります。
※ファイルがリモートにある時はAddressables (Remote)を指定します
念のためawbファイルも選んで、Deploy Typeだけ変えます。
この辺の該当マニュアルは以下です。
[共通] Deploy Type
Deploy Typeでは「アセットの実データをどこに配置するか」を指定します。
詳しくは「 Deploy Typeの設定 」をご参照ください。[ACBアセット] AWBの紐づけ
インポートしたACBアセットを選択すると、Inspector上でAWBアセットの設定が行えます。
CRIWARE Unity Plugin Manual: アセットインポート後の設定
ストリーミングデータを含むキューシートについては、本設定でACBとAWBの紐づけを行ってください。
この設定により、ACBアセットをロードすることで自動的に対応するAWBアセットもロードされ、ストリーミングキューの再生が行えるようになります。
余談ですが、ストリーム再生ではなくメモリ再生の場合にはawbファイルがないんですよね。
なのでストリーム再生の音源とメモリ再生の音源が混在していると処理を分けなきゃいけないんですけど、Asset Support Add-onを使うとこういう仕様になるので処理の共通化が楽になります。
(試してないけど上記のソースコードでそのまま動くのでは?)
キューシートの解放について
今回は載せていませんが、読み込んだキューシートを破棄する時はCriAtomAssetsLoader.ReleaseCueSheet();を呼びましょう。
元々はCriAtom.RemoveCueSheet();だったやつですね。
あとがき
これでacbファイルとawbファイルが入ったリソースもAASで管理できるようになります。
やったね!
Asset Support Add-onが登場したばかりで、ググっても公式マニュアルしか情報がなくて実装に1日かかってしまいました。
叩き台として活用いただければ幸いです。
ちなみにクラス仕様に関してはクラスリファレンスが公開されています。
例えばCriAtomAcbAssetは以下です。
LoadAsync()について
CriAtomAcbAssetのクラスリファレンスにはLoadAsync()の説明があります。
これで少しハマったので補足。
void LoadAsync()
キューシートのロード (非同期)
説明:
キューシートを非同期でロードします。
本メソッドの実行後、CriAtomAcbAsset.Loaded が true になってから
キューシートへのアクセスを行ってください。
つまり、
- LoadAsync()の呼び出し
- Loadedがtrueになるまで待つ
- SetCue()などの呼び出し
リファレンスを見る限りでは、上記の順番でやってね、ということに見えますね。
しかし今回はLoadAsync()を呼び出してはいけません。
というのも、CriAtomAssetsLoader.AddCueSheet()でCriAtomAcbAssetのインスタンスを渡すと自動でLoadAsync()を呼び出してくれるからです。
AddCueSheet()を呼ぶ前にLoadAsync()を呼んでいたのですが、以下のエラーでうまくいきませんでした。
InvalidOperationException: [CRIWARE] xxxxxx (CriAtomAcfAsset) is already loaded.
xxxxxxはキューシート名です。
ソースを直接見たらCriAtomAssetsLoader.AddCueSheet()を呼んだ時にLoadAsync()を呼ばれることが分かり、自発的にLoadAsync()を呼ぶのをやめてみたら動きました。
2回呼んじゃダメなんでしょうね。