CRI ADX2(Unity)
もっともシンプルなサウンドマネージャーの作り方

CRIWARE Unity プラグインでは、ADXをゲームへ素早く組み込めるように
コンポーネントとして様々な機能を提供しています。

上記に加えてADXでは、Unity標準のオーディオ機能と比べて以下の細かい制御が可能です。

  • 1つの音声再生プレーヤーで複数の音声を個別に再生/停止できる
  • Monobehaviourを継承しないクラスで再生できる

つまり、ADXではGameObjectとして音声再生プレーヤーをたくさん作っておく必要はありません!
ADXを上手に利用することで、GCを抑えたよりよいパフォーマンスの実装が可能になります。

ただしこれらの利点を活用するためには、ADX独自の概念と様々なAPIを理解する必要があります。
そのうえで自分のゲームにあった実装を考えるのはとてもたいへんですね!

今回の記事では、ADXの「どのAPIをどのように利用すると」より高度でより効率的な実装になるのか、
その基本となる実装例をサンプルプロジェクトと共に紹介します。

 

今回使用するADXの機能

ADXには独自のデータを再生するためのクラスと、再生中のサウンドを表現した構造体が存在します。

  • CriAtomExPlayer
    • 音声を再生するためのプレーヤーです
    • このプレーヤーは複数の音声を同時に再生できます
    • 後述のCriAtomExPlaybackを作るクラスととらえてみましょう
  • CriAtomExPlayback
    • 実際に再生された音声を表現したオブジェクトです
    • 個別の音声に対する操作はこのオブジェクトを介して行います

これらは、ADXの中でも利用する機会の多いオブジェクトとなります。
ぜひ理解してADXをさらに使いこなしましょう!

 

サンプルプロジェクトについて

まずは、こちらのサンプルプロジェクト一式をダウンロードしてください。

このプロジェクトにはADX LEのプラグインが含まれています。
CRI ADX LE に関するユーザー使用許諾契約書」の内容をご確認いただき、同意のうえでダウンロードしてください。

SoundManagerSample.zipの詳細

  • AtomCraftProjectフォルダ
    • 音声を差し替えてみるなど、いろいろ試したい場合にご活用ください!
  • UnityProjectフォルダ
    • Unity で開き、Scenes/main シーンを開いてみましょう

このサンプルでは、シーン上にりんごちゃんとリンゴが配置されていて
これらのゲームオブジェクトの挙動に応じて音声が再生されます。

 

サウンドマネージャークラス

ではさっそくサウンドマネージャークラスの実装をみていきましょう!

Scripts/SimpleSoundManager.cs

public class SimpleSoundManager : IDisposable
{
(中略)
    private CriAtomExPlayer player;
    
    public struct SimplePlayback
    {
        CriAtomExPlayer player;
        CriAtomExPlayback playback;

        internal SimplePlayback(CriAtomExPlayer player, CriAtomExPlayback pb)
        {
            this.player = player;
            this.playback = pb;
        }
        
        public void SetVolumeAndPitch(float vol, float pitch)
        {
            this.player.SetVolume(vol);
            this.player.SetPitch(pitch);
            this.player.Update(playback);
        }

        public void Stop()
        {
            this.playback.Stop();
        }

        public bool IsPlaying()
        {
            return this.playback.GetStatus() == CriAtomExPlayback.Status.Playing;
        }
    }
    
    public SimplePlayback StartPlayback(CriAtomCueReference cue, float vol = 1.0f, float pitch = 0)
    {
        Player.SetCue(cue.AcbAsset.Handle, cue.CueId);
        Player.SetVolume(vol);
        Player.SetPitch(pitch);
        SimplePlayback pb = new SimplePlayback(Player, Player.Start());
        return pb;
    }
}

今回のサンプルスクリプトでは、マネージャークラスへ音声再生を要求すると
CriAtomExPlaybackをラップしたSimplePlaybackが返されます。
返ってきたSimplePlaybackを介して実際に再生されている音の操作ができます!

 

シンプルな音の再生

次はサンプルの中で音を再生している処理を見てみましょう。

このクラスはキー入力に応じてりんごちゃんを移動させるクラスです。
この中で、SimpleSoundManager.Instance.StartPlayback(cue)を利用して足音を再生しています。

Scripts/RingoAvatarController.cs

public class RingoAvatarController : MonoBehaviour
{
    [SerializeField] private CriAtomCueReference cue;

    private void Update()
    {
        float moveHorizontal = Input.GetAxis("Horizontal");
        float moveVertical = Input.GetAxis("Vertical");
        Vector3 velocity = new Vector3(-moveVertical, 0.0f, moveHorizontal);

        if (velocity == Vector3.zero) { return; }
        velocity.Normalize();
        velocity *= transformSpeed * Time.deltaTime;
        transform.position += velocity;
        transform.rotation = Quaternion.Slerp(transform.rotation, Quaternion.LookRotation(velocity), reflectionSpeed);

        SimpleSoundManager.Instance.StartPlayback(cue);
    }
}


足音は再生中に音量などのパラメーターを変化させる必要がないので、戻り値は捨ててしまっています。
※キューリミット有効かつ先着優先に設定しているので、スクリプトから多重再生しても足音が重ならないようになっています。

StartPlayback()時に必要なキューなどの情報はCriAtomCueReferenceをシリアライズして持っています。

 

パラメーターを指定した音の再生

サウンドのピッチを変更してキューを再生するにはどうしたらよいでしょう?

以下のスクリプトは、りんごちゃんがリンゴを拾ったときに効果音を再生するクラスです。
リンゴを拾ったタイミング(コライダーの衝突時)で音を再生し、数秒後にりんごをリスポーンします。

Scripts/AppleRespawner.cs

class AppleRespawner : Monobehaviour {

    [SerializeField] private CriAtomCueReference cue;
    
    private void Start()
    {
        originalPos = this.gameObject.transform.position;
        mesh = this.gameObject.transform.Find("Apple_mesh").gameObject;
        appleCollider = this.gameObject.GetComponent<CapsuleCollider>();
    }
    
    void OnCollisionEnter(Collision collision)
    {
        SimpleSoundManager.Instance.StartPlayback(cue, pitch: 500);
        StartCoroutine(RespawnApple());
    }
    
}

さきほどの再生とは異なり、ピッチを指定してキューを再生しています。
SimpleSoundManager.Instance.StartPlayback(cue, pitch: 500);

ピッチの指定はどのように実装しているのでしょうか?

class SimpleSoundManager
public CriAtomExPlayer Player { get => player; }

public SimplePlayback StartPlayback(CriAtomCueReference cue, float vol = 1.0f, float pitch = 0)
{
    Player.SetCue(cue.AcbAsset.Handle, cue.CueId);
    Player.SetVolume(vol);
    Player.SetPitch(pitch);
    SimplePlayback pb = new SimplePlayback(Player, Player.Start());
    return pb;
}

SimpleSoundManager.StartPlayback を見ると CriAtomExPlayer に対してパラメーターを設定していますね!

これらパラメーター設定APIを CriAtomExPlayer.Start() 呼び出し前に設定しておくと
そのパラメーターで音声が再生されます。

 

再生中の音声のパラメーター変更

すでに再生されている(CriAtomExPlayback が発行されている)
音声のボリュームなどを変えるにはどうすればよいでしょうか?

SimplePlayback.SetVolumeAndPitch() の実装をみてみましょう。

public struct SimplePlayback
{
    CriAtomExPlayer player;
    CriAtomExPlayback playback;

    public void SetVolumeAndPitch(float vol, float pitch)
    {
        this.player.SetVolume(vol);
        this.player.SetPitch(pitch);
        this.player.Update(playback);
    }
}

StartPlaybackと同じように CriAtomExPlayer にパラメーターを設定したあと
CriAtomExPlayback を引数にとる CriAtomExPlayer.Update(CriAtomExPlayback) を呼び出していますね。
これで再生中の音声ごとのパラメーターが更新できました。

今回のスクリプトは、サンプルシーンで使う最低限の機能だけを実装したものになっています。
これをベースにしつつ、ご自身のゲームで使いたい表現に合わせた API を追加してみてください!

Tips

プレーヤーをプーリングして使うことはできない?

Unity標準オーディオのように、1音1プレーヤーのように利用しても問題ありません。
ADXはゲーム開発者が使いやすいと思ったAPIの使い方を幅広くサポートします!

プレーヤーを複数作る必要があるときはどんなとき?

例えば音声同期タイマー付き CriAtomExPlayer や CriAtomExPlayer.AttachFader()
CriLipsAtomAnalyzer のアタッチなどなど
CriAtomExPlayer を特殊なプレーヤーにするAPIがあります。

こういった場合は CriAtomExPlayer をそれぞれ個別に作ったほうがよいでしょう。

CriAtomSourceを使ったほうがいい?

CriAtomSource は CriAtomExPlayer をコンポーネント対応したラッパークラスです。

ただし、3Dポジショニング機能を利用する場合は CriAtomExPlayer の他に
必要なオブジェクトが出てきたり、座標の更新処理などが発生します。
CriAtomSource は3Dポジショニング機能に関するその他のオブジェクトを生成、管理してくれます。

こういった3Dポジショニング機能を利用する場合は CriAtomSource を利用すると気軽に実装できます。

複数のCriAtomExPlaybackのパラメーターを更新する場合は注意!

CriAtomExPlaybackのパラメーターを更新するには
いったんCriAtomExPlayerにパラメーターをセットする必要がありました。

したがって複数のCriAtomExPlaybackのパラメーターを更新しようとすると
CriAtomExPlayerに設定済みのパラメーターが意図せず残ってしまう場合があります。

これを回避するために以下のAPIでは、CriAtomExPlayerへ同じパラメーターを必ず設定しています。

  • SimpleSoundManager.StartPlayback()
  • SimplePlayback.SetVolumeAndPitch()