この記事は、サムザップ Advent Calendar 2022 の12/19の記事です。
概要
2022年5月頃にCRI・ミドルウェア社が開発したCRIWARE Unity SDKに、Asset Support Add-onが新しく追加されました。このアドオンには、CRI関連の音声や動画データをUnityアセットとして扱えるようにするCRI Assetsと、UnityのAddressable Assets System(以下AAS)に対応したCRI Addressablesという2つの機能が追加されています。
本記事では、それらの機能について紹介できればと思います。
CRI関連データのインポート
Unityアセットとして扱えるCRI関連のデータとインポート後に付与されるコンポーネントは以下になります
ファイル種別 | 概要 | インポート後に付与されるコンポーネント |
---|---|---|
ACFファイル | 全体設定ファイル | CriAtomAcfAsset |
ACBファイル | 音声キューシートのバイナリファイル | CriAtomAcbAsset |
AWBファイル | 音声ストリーミング再生用の波形データのバイナリファイル | CriAtomAwbAsset(CriAssetBaseをただ継承しているだけなので、ドキュメントは無し) |
USMファイル | Sofdec動画のバイナリファイル | CriManaUsmAsset |
上記のファイルをUnityプロジェクトのStreaming Assetsフォルダ以外のフォルダに追加すると、インポートの処理が走りそれぞれのUnityアセットが生成されます。
例えば、ACBファイルだとCriAtomAcbAssetクラスのScriptableObjectが生成されます。
同名のACBファイルをAWBファイルを一緒に追加すると、ACBアセットにAWBアセットが紐付けされた状態でインポートされます。ACBアセットにAWBアセットが紐付いていると、ACBアセットをロードすることでAWBアセットも自動でロードされストリーミング再生ができるようになります。
また、インポートされたACBアセットは、Inspectorタブからプレビュー再生することもできます。
ビルド時にアセットの実データをどこに配置するかの設定
CRI関連ファイルのUnityアセットが生成されたら、DeployTypeというプロパティでビルド時に生成するアセットの実データ(ACBファイル、AWBファイルなどのバイナリデータ)をどこに配置するかという設定をします。
デフォルトで選択できる設定は以下になります。
ファイル種別 | 概要 |
---|---|
StreamingAssets | StreamingAssetに配置する |
OnMemory | アセットそのものの中に配置する(AWBファイルは、ストリーミング再生用のデータなので取り扱えない。) |
Addressables (Remote) | AASで設定したリモートデータ用のパス |
Addressables (Local) | AASで設定したローカルデータ用のパス |
OnMemoryを指定したアセットをロードするとデータは即メモリに展開されるため、ストリーミング再生用のデータを扱うAWBファイルに対しては、OnMemoryを選択できず、AWBファイルを配信用のデータとする場合はAddressables (Remote)を指定することになります。
AASを使わずにAWBファイルを配信用データとして扱いたい場合は、DeployTypeを拡張して独自の実データの取り扱いやロード時の挙動を定義することもできます。1
音のロードと再生
DeployTypeをStreamingAssetsにしたACBアセットをロード及び再生する実装は以下のようになります。
ビルドすると、StreamingAssetsフォルダにACBファイル及びAWBファイルがそのまま配置されるので、ロードと再生は従来の実装と変わらないです。
using CriWare;
using CriWare.Assets;
using System.Threading.Tasks;
public class SoundPlayTest : MonoBehavior
{
[SerializeField] private Button _playButton;
private CriAtomExPlayer _player;
private async void Awake()
{
_player = new CriAtomExPlayer();
_playButton.onClick.AddListener(async () =>
{
CriAtomExAcb acb = await LoadAcbAssetFromStreamingAssets("acbFileName", "awbFileName")
PlayAcb(acb, 1);
});
}
/// <summary>
/// StreamingAssetsからACBを非同期ロードする
/// </summary>
private async Task<CriAtomExAcb> LoadAcbAssetFromStreamingAssets(string acbFileName, string awbFileName)
{
string acbPath = $"{CriWare.Common.streamingAssetsPath}/{acbFileName}";
string awbPath = $"{CriWare.Common.streamingAssetsPath}/{awbFileName}";
// ACBアセットのStreamingAssetsからのロード
CriAtomExAcbLoader loader = CriAtomExAcbLoader.LoadAcbFileAsync(null, acbPath, awbPath);
await new WaitUntil(() =>
{
var status = loader.GetStatus();
return status == CriAtomExAcbLoader.Status.Complete
|| status == CriAtomExAcbLoader.Status.Error;
});
if (loader.GetStatus() == CriAtomExAcbLoader.Status.Complete)
{
return loader.MoveAcb();
}
return null;
}
/// <summary>
/// ACBを再生する
/// </summary>
private CriAtomExPlayback PlayAcb(CriAtomExAcb acb, int cueId, float volume = 1.0f)
{
// プレイヤーへのキューの設定
player.SetCue(acb, cueId);
player.SetVolume(volume);
// 再生
CriAtomExPlayback playBack = player.Start();
return playBack;
}
}
DeployTypeをAddressables (Remote)にしたACBアセットをロード及び再生する実装は以下のようになります。
注意するところとしては、カタログファイルロード完了後に、カタログファイル内のCRIアセット向けの情報を更新する必要があります。2
using CriWare;
using CriWare.Assets;
using System.Threading.Tasks;
public class SoundPlayTest : MonoBehavior
{
[SerializeField] private Button _playButton;
private CriAtomExPlayer _player;
private async void Awake()
{
_player = new CriAtomExPlayer();
_playButton.onClick.AddListener(async () =>
{
CriAtomExAcb acb = await LoadAcbFromAddressable("acbAddress");
PlayAcb(acb, 1);
});
// リモートカタログの有無を確認してAddressablesを初期化
var catalogs = await Addressables.CheckForCatalogUpdates().Task;
if (catalogs.Count > 0)
{
await Addressables.UpdateCatalogs(catalogs).Task;
}
else
{
await Addressables.InitializeAsync().Task;
}
// カタログロード完了後に、カタログ内のCriAddressables向けの情報を修正する
// この処理を呼ぶことによってAddressables.GetDownloadSizeAsync等が正しい結果を返すようになる
CriWare.Assets.CriAddressables.ModifyLocators();
}
/// <summary>
/// Addressable経由でACBを非同期ロードする
/// </summary>
private async Task<CriAtomExAcb> LoadAcbFromAddressable(string address)
{
// ACBアセットのAddressableからのロード
var acbAsset = await Addressables.LoadAssetAsync<CriAtomAcbAsset>(address);
if (acbAsset.Loaded == false)
{
// ACBアセットのキューシートのロード
acbAsset.LoadAsync();
await new WaitUntil(() => acbAsset.Loaded || acbAsset.Status == CriAtomExAcbLoader.Status.Error);
}
if (acbAsset.Loaded)
{
return acbAsset.Handle;
}
return null;
}
/// <summary>
/// ACBを再生する
/// </summary>
private CriAtomExPlayback PlayAcb(CriAtomExAcb acb, int cueId, float volume = 1.0f)
{
// プレイヤーへのキューの設定
player.SetCue(acb, cueId);
player.SetVolume(volume);
// 再生
CriAtomExPlayback playBack = player.Start();
return playBack;
}
}
動画の再生
スクリプトからUSMアセットを指定して再生する際は、CriManaMovieControllerForAssetというコンポーネントを使います。
CriManaMovieControllerForAssetでは、RenderTargetプロパティで描画対象種別をRendererコンポーネントにするかGraphicコンポーネントにするかを選択できます。
Graphicを選択した場合は、既存のCriManaMovieControllerForUIと同じ描画方法になります。
CriManaMovieControllerForUIと違う点としては、ループの設定やアディティブムービーとして再生するかの設定などがUSMアセット側が持つようになったことで無くなっています。
DeployTypeをStreamingAssetsにしたUSMアセットをロード及び再生する実装は以下のようになります。
ビルドすると、StreamingAssetsフォルダにUSMファイルがそのまま配置されるので、USMファイルの設定と再生は従来の実装とほぼ変わらないです。
using CriWare;
using CriWare.Assets;
using System.Threading.Tasks;
public class MoviePlayTest : MonoBehavior
{
[SerializeField] private Button _playButton;
[SerializeField] private CriManaMovieControllerForAsset _movieController;
private async void Awake()
{
_playButton.onClick.AddListener(async () =>
{
SetUsmFromStreamingAssets("usmFileName");
PlayUsm();
});
}
/// <summary>
/// StreamingAssetsフォルダのUSMファイルをプレイヤーに設定する
/// </summary>
private void SetUsmFromStreamingAssets(string usmFileName)
{
// プレイヤーにUSMファイルをセットする
_movieController.player.SetFile(null, $"{CriWare.Common.streamingAssetsPath}/{usmFileName}");
}
/// <summary>
/// USMを再生する
/// </summary>
private void PlayUsm()
{
_movieController.Play();
}
}
DeployTypeをAddressables (Remote)にしたUSMアセットのロードと再生の実装は以下のようになります。
CriMana.Playerに、CriManaUsmAssetをプレイヤーに設定する拡張メソッドが用意されているので、それを使うのが便利です。
using CriWare;
using CriWare.Assets;
using System.Threading.Tasks;
public class MoviePlayTest : MonoBehavior
{
[SerializeField] private Button _playButton;
[SerializeField] private CriManaMovieControllerForAsset _movieController;
private async void Awake()
{
_playButton.onClick.AddListener(async () =>
{
await LoadAndSetUsmAssetFromAddressable("usmAddress");
PlayUsm();
});
// リモートカタログの有無を確認してAddressablesを初期化
var catalogs = await Addressables.CheckForCatalogUpdates().Task;
if (catalogs.Count > 0)
{
await Addressables.UpdateCatalogs(catalogs).Task;
}
else
{
await Addressables.InitializeAsync().Task;
}
// カタログロード完了後に、カタログ内のCriAddressables向けの情報を修正する
// この処理を呼ぶことによってAddressables.GetDownloadSizeAsync等が正しい結果を返すようになる
CriWare.Assets.CriAddressables.ModifyLocators();
}
/// <summary>
/// Addressable経由でUSMアセットを非同期ロードしプレイヤーに設定する
/// </summary>
private async Task<CriManaUsmAsset> LoadAndSetUsmAssetFromAddressable(string address)
{
// USMアセットのAddressableからのロード
var usmAsset = await Addressables.LoadAssetAsync<CriManaUsmAsset>(address);
// プレイヤーにUSMアセットをセットする
_movieController.player.SetAsset(usmAsset);
}
/// <summary>
/// USMを再生する
/// </summary>
private void PlayUsm()
{
_movieController.Play();
}
}
まとめ
CRI関連データをアセットバンドルやAddressableとして扱うとなると、拡張子に.bytesを付けてバイナリデータとして配信してダウンロード後に元の形式のファイルとしてPersistantDataPathなどに書き込みを行ったり、Addressableの場合は独自にプロバイダーを実装したりする必要がありましたが、Asset Support Add-onを使うとこれらの必要がなくなります。
AASとCRIWAREを使おうと考えている方は、Asset Support Add-onの導入も考えてみても良いかもしれません。
明日は、@chrno001さんの記事です。