ゲーム開発
Unity
UnrealEngine
C++
Blender
Houdini
ゲーム数学
ゲームAI
グラフィックス
サウンド
アニメーション
GBDK
制作日記
IT関連
ツール開発
フロントエンド関連
サーバサイド関連
WordPress関連
ソフトウェア設計
おすすめ技術書
音楽
DTM
楽器・機材
ピアノ
ラーメン日記
四コマ漫画
その他
おすすめアイテム
おもしろコラム
  • ゲーム開発
    • Unity
    • UnrealEngine
    • C++
    • Blender
    • Houdini
    • ゲーム数学
    • ゲームAI
    • グラフィックス
    • サウンド
    • アニメーション
    • GBDK
    • 制作日記
  • IT関連
    • ツール開発
    • フロントエンド関連
    • サーバサイド関連
    • WordPress関連
    • ソフトウェア設計
    • おすすめ技術書
  • 音楽
    • DTM
    • 楽器・機材
    • ピアノ
  • ラーメン日記
    • 四コマ漫画
      • その他
        • おすすめアイテム
        • おもしろコラム
      1. ホーム
      2. 20240122_01_unity_audio_standard

      【Unity】第一回 UnityAudioを使いこなす 〜サウンド再生処理編〜

      UnityサウンドUnityAudio
      2024-01-22

      マイケル
      マイケル
      みなさんこんにちは! マイケルです!
      エレキベア
      エレキベア
      こんにちクマ〜〜
      マイケル
      マイケル
      今日からしばらくUnityでのサウンド周りの実装について取り上げていきます! 後々CRIやWwise等のサウンドミドルウェアを触ってみようと思いますが、その前にUnity標準で搭載されているUnityAudioでどのようなことが出来るのか?をまとめていこうと思います!
      エレキベア
      エレキベア
      サウンドミドルウェアは一定規模のゲーム開発ではほとんど採用されているクマね でも確かにUnityAudioについても知らないことが多そうクマ
      マイケル
      マイケル
      調べながら改めて触ってみると、サウンドエフェクトやオーディオスペクトラムの描画も実装できて、想像以上にいろんなことが出来るなと思いました。 基本的なサウンド再生でしか触っていない方も多いと思うので、これを機にいろいろ触ってみると面白いと思います!
      【Unity】第一回 UnityAudioを使いこなす 〜サウンド再生処理編〜
      2024-01-22
      【Unity】第二回 UnityAudioを使いこなす 〜AudioMixer活用編〜
      2024-01-22
      【Unity】第三回 UnityAudioを使いこなす 〜オーディオスペクトラム描画編〜
      2024-01-22

      参考書籍

      マイケル
      マイケル
      学習するにあたり、下記書籍を参考にさせていただきました!

      Unityサウンド エキスパート養成講座

      マイケル
      マイケル
      Unityでのサウンド処理をまとめた数少ない書籍です! 発売日からそれなりに経過していますが、UnityAudioだけでなくCRIと連携した使い方まで解説されているため、興味を持った方はぜひ一読してみてください!
      エレキベア
      エレキベア
      サウンド専門となると書籍は中々ないクマから持っておく価値はありそうクマね

      UnityAudioの構成要素

      マイケル
      マイケル
      UnityのAudio機能については公式マニュアルにまとまっていますが、大きく下記のような構成になっています。

      Unity公式マニュアル - オーディオの概要

      機能名
      概要
      AudioClip
      ・サウンドのアセット形式
      AudioSource
      ・AudioClipの再生処理を行うためのコンポーネント
      AudioListener
      ・ゲーム内空間の耳にあたるコンポーネント
      AudioMixer
      ・AudioSourceが鳴らしている音を分類し調整やエフェクト処理を行う機能
      エレキベア
      エレキベア
      AudioClipやAudioSourceはよく使うクマが、それ以外はあまり聞き覚えがないクマね
      マイケル
      マイケル
      各機能はそれぞれ下記のようになっています。

      AudioClip

      マイケル
      マイケル
      AudioClipは Unityでオーディオファイルを扱う形式 です。 こちらはUnityにインポートした際に自動的にこの形式に変換されます。
      20240122_01_unity_audio_standard_01

      Unityスクリプトリファレンス - AudioClip

      共通設定
      項目名
      概要
      Force To Mono
      モノラル音源へ変換するか
      Load In Background
      メモリへロードする際、非同期読み込みを行うか
      Ambisonic
      VR向けの設定
      プラットフォーム別設定
      項目名
      概要
      LoadType
      読み込み方法 ※後述
      Preload Audio Data
      シーンロード時にサウンドデータをメモリに読み込むか
      Compression Format
      圧縮設定
      LoadTypeの種類
      項目名
      概要
      効果
      Decompress On Load
      デコード処理を全て行ってからメモリに配置する
      CPU負荷:小
      メモリ容量:大
      Compressed In Memory
      圧縮データを展開せずにメモリに読み込み、再生時に展開する
      CPU負荷:中
      メモリ容量:中
      Streaming
      圧縮データをメモリに置かず、再生時に随時ストレージから読み込む
      CPU負荷:大
      メモリ容量:小
      マイケル
      マイケル
      音源の種類や用途によって設定を変えることで最適化することができます。 具体例として、BGMとSEで設定を分ける例は下記になります。
      BGMの設定例

      容量が大きいため Load In Background で非同期読み込みを行い、
      Compressed In Memory で再生時に展開する。

      20240122_01_unity_audio_standard_06
      SEの設定例

      再生速度を優先するため Force To Mono でモノラルに設定し、
      Preload Audio Data、Decompress On Load で事前に読み込み、デコード処理を行う。

      20240122_01_unity_audio_standard_05
      エレキベア
      エレキベア
      CPU負荷やメモリ容量を考慮しながら、再生速度と品質どちらを優先すべきかで設定しているクマね

      AudioSource

      マイケル
      マイケル
      AudioSourceは AudioClipの再生処理を行うためのコンポーネント です。
      20240122_01_unity_audio_standard_02

      Unityスクリプトリファレンス - AudioSource

      マイケル
      マイケル
      AudioClip項目にclipを設定することで再生することができます。 その他、ボリュームやピッチ調整等の再生時の設定が主な役割ですが、こちらは後ほど触ってみましょう。
      エレキベア
      エレキベア
      AudioClipをAudioSourceで再生する・・・ ここまでは分かったクマね

      AudioListener

      マイケル
      マイケル
      AudioListenerは ゲーム内空間の耳にあたるコンポーネント で、ゲーム中に一つしか存在しません。 こちらのを介してゲーム全体の音の再生、停止等を行えます。
      20240122_01_unity_audio_standard_03
      ▲MainCameraにデフォルトで付いている

      Unityスクリプトリファレンス - AudioListener

      エレキベア
      エレキベア
      MainCameraに設定されてるの気付かなかったクマ・・・

      AudioMixer

      マイケル
      マイケル
      AudioMixerは AudioSourceが鳴らしている音を分類し調整やエフェクト処理を行う機能 です。 こちらは設定手順等が複雑なので、詳細は次回解説します!
      20240122_01_unity_audio_standard_07

      Unityスクリプトリファレンス - AudioMixer

      エレキベア
      エレキベア
      これはあまり触ってない人も多そうクマね

      プロジェクト全体のサウンド設定

      マイケル
      マイケル
      最後に、ゲーム全体のサウンド設定は Edit > Project Settings > Audio から変更することができます。 全体のボリューム設定や最大同時再生数などの項目があります。
      20240122_01_unity_audio_standard_04

      Unityスクリプトリファレンス - AudioSettings

      エレキベア
      エレキベア
      まあ基本はデフォルトでよさそうクマね

      オーディオの再生処理を実装する

      マイケル
      マイケル
      それでは早速オーディオの再生処理を実装してみます。 今回作ったサンプルプロジェクトは下記のGitHubリポジトリとして上げていますので、こちらもよければご参照ください!

      GitHub - unity-audio-sample

      20240122_01_unity_audio_standard_09
      ▲サンプルプロジェクト内で今回解説する範囲の機能

      サウンドの再生処理

      マイケル
      マイケル
      サウンド関連の処理については、基本的に UnityAudioServiceクラス内にまとめてあります。

      GitHub - UnityAudioService.cs

      マイケル
      マイケル
      まず使用するAudioClipの読込処理は下記になります。 サンプルということで今回はResources配下から読み込むようにしています。
              /// <summary>
              /// Audioファイル格納パス
              /// </summary>
              private static readonly string AudioFileDirPath = "Audio/";
      
              /// <summary>
              /// キャッシュしたAudioClip
              /// key: ファイル名
              /// </summary>
              private readonly IDictionary<string, AudioClip> _cachedAudioDictionary = new Dictionary<string, AudioClip>();
      
              /// <summary>
              /// AudioClipの読み込み
              /// </summary>
              /// <param name="audioName"></param>
              /// <returns></returns>
              private AudioClip LoadAudioClip(string audioName)
              {
                  // ファイル名をキーとしてキャッシュする
                  if (!_cachedAudioDictionary.ContainsKey(audioName))
                  {
                      var audioClip = Resources.Load(AudioFileDirPath + audioName) as AudioClip;
                      _cachedAudioDictionary.Add(audioName, audioClip);
                  }
                  return _cachedAudioDictionary[audioName];
              }
      
      ▲AudioClipの読み込み
      マイケル
      マイケル
      そして読み込んだAudioClipをAudioSourceに設定するのですが、 今回は下記のようにオブジェクトにAddComponentする形で生成しました。 BGM用で二つ生成しているのは、この後紹介するクロスフェード処理で同時再生する時に必要になるためです。
      
              /// <summary>
              /// AudioSource
              /// </summary>
              private readonly AudioSource _seAudioSource; // SE再生用
              private readonly List<AudioSource> _bgmAudioSourceList; // BGM再生用
      
              /// <summary>
              /// 一時停止中か?
              /// </summary>
              private bool _isPause = false;
      
              public UnityAudioService()
              {
      
      ・・・略・・・
      
                  // AudioManagerオブジェクトを作成
                  var audioManager = GameObject.Find(AudioManagerObjectName);
                  if (audioManager == null)
                  {
                      audioManager = new GameObject(AudioManagerObjectName);
                      Object.DontDestroyOnLoad(audioManager);
                      _seAudioSource = CreateSeAudioSource(audioManager);
      
                      // BGMはフェード用で2つ生成しておく
                      _bgmAudioSourceList = new List<AudioSource>();
                      _bgmAudioSourceList.Add(CreateBgmAudioSource(audioManager));
                      _bgmAudioSourceList.Add(CreateBgmAudioSource(audioManager));
                  }
                  _isPause = false;
              }
      
              /// <summary>
              /// BGM用AudioSource生成
              /// </summary>
              /// <param name="parentObject"></param>
              /// <returns></returns>
              public AudioSource CreateBgmAudioSource(GameObject parentObject)
              {
                  return CreateAudioSource(parentObject, true);
              }
      
              /// <summary>
              /// SE用AudioSource生成
              /// </summary>
              /// <param name="parentObject"></param>
              /// <returns></returns>
              public AudioSource CreateSeAudioSource(GameObject parentObject)
              {
                  return CreateAudioSource(parentObject, false);
              }
      
              /// <summary>
              /// AudioSource生成
              /// </summary>
              /// <param name="parentObject"></param>
              /// <param name="isLoop"></param>
              /// <returns></returns>
              private AudioSource CreateAudioSource(GameObject parentObject, bool isLoop)
              {
                  var audioSource = parentObject.AddComponent<AudioSource>();
                  audioSource.loop = isLoop;
                  audioSource.playOnAwake = false; // デフォルトでtrueのため明示的にオフにする
                  return audioSource;
              }
      
      ▲AudioSourceの生成
      マイケル
      マイケル
      あとは生成したAudioSourceに対して、Play関数PlayOneShot関数 を呼び出すことで再生することができます。 今回は別途Optionクラスも用意し、ボリュームやピッチ調整も行えるようにしました。
              /// <summary>
              /// 効果音再生
              /// </summary>
              /// <param name="audioName"></param>
              /// <param name="option"></param>
              public void PlayOneShot(string audioName, IAudioService.AudioOption option = null)
              {
                  PlayOneShot(_seAudioSource, audioName, option);
              }
      
              /// <summary>
              /// 効果音再生
              /// </summary>
              /// <param name="audioSource"></param>
              /// <param name="audioName"></param>
              /// <param name="option"></param>
              public void PlayOneShot(AudioSource audioSource, string audioName, IAudioService.AudioOption option = null)
              {
                  var audioClip = LoadAudioClip(audioName);
                  audioSource.volume = option?.Volume ?? 1f;
                  audioSource.pitch = option?.Pitch ?? 1f;
                  audioSource.PlayOneShot(audioClip);
              }
      
              /// <summary>
              /// BGM再生
              /// </summary>
              /// <param name="audioName"></param>
              /// <param name="option"></param>
              public void PlayBgm(string audioName, IAudioService.AudioOption option = null)
              {
                  StopAllBgm();
      
                  var audioSource = _bgmAudioSourceList[0];
                  audioSource.clip = LoadAudioClip(audioName);
                  audioSource.volume = option?.Volume ?? 1f;
                  audioSource.pitch = option?.Pitch ?? 1f;
                  audioSource.Play();
              }
      
              /// <summary>
              /// BGM停止
              /// </summary>
              public void StopAllBgm()
              {
                  // 再生中のBGMを全て取得して停止する
                  var audioSources = _bgmAudioSourceList.Where(audioSource => audioSource.isPlaying);
                  if (audioSources.Count() <= 0)
                  {
                      return;
                  }
      
                  foreach (var audioSource in audioSources)
                  {
                      audioSource.Stop();
                      audioSource.clip = null;
                  }
              }
      
      ▲BGM、SEの再生処理
              /// <summary>
              /// オプション
              /// </summary>
              public class AudioOption
              {
                  /// <summary>
                  /// ボリューム
                  /// </summary>
                  public float Volume = 1f;
      
                  /// <summary>
                  /// ピッチ
                  /// </summary>
                  public float Pitch = 1f;
      
                  public AudioOption(float volume, float pitch)
                  {
                      Volume = volume;
                      Pitch = pitch;
                  }
              }
      
      ▲指定できるオプション
      マイケル
      マイケル
      これでオーディオファイル名とオプションを指定して関数呼び出しすることで再生できるようになっているはずです。
      エレキベア
      エレキベア
      ここまでは簡単クマ〜〜〜

      フェードイン、フェードアウト

      マイケル
      マイケル
      次は少し凝って、再生・停止を行う際に音量をフェードさせるようにしてみます。 今回はUnity標準機能のみで実装するためにコルーチンを使用しましたが、MonoBehaviourが必要になるため可能であればUniTaskを使用して実装した方がいいと思います。
              /// <summary>
              /// BGMフェードイン再生
              /// </summary>
              public void PlayBgmFadeIn(MonoBehaviour mono, string audioName, float fadeTime, IAudioService.AudioOption option = null)
              {
                  var audioSource = _bgmAudioSourceList.FirstOrDefault(audioSource => !audioSource.isPlaying);
                  if (audioSource == null)
                  {
                      Debug.LogError($"all play audio sources!!");
                      return;
                  }
      
                  StopAllBgmFadeOut(mono, fadeTime);
                  mono.StartCoroutine(PlayBgmFadeInCoroutine(audioSource, audioName, fadeTime, option));
              }
      
              private IEnumerator PlayBgmFadeInCoroutine(AudioSource audioSource, string audioName, float fadeTime, IAudioService.AudioOption option = null)
              {
                  var startVolume = 0f;
                  var targetVolume = option?.Volume ?? 1f;
      
                  audioSource.clip = LoadAudioClip(audioName);
                  audioSource.volume = startVolume;
                  audioSource.pitch = option?.Pitch ?? 1f;
                  audioSource.Play();
      
                  for (var t = 0f; t < fadeTime; t += Time.deltaTime)
                  {
                      audioSource.volume = Mathf.Lerp(startVolume, targetVolume, Mathf.Clamp01(t / fadeTime));
                      yield return null;
                  }
                  audioSource.volume = targetVolume;
              }
      
              /// <summary>
              /// BGMフェードアウト停止
              /// </summary>
              public void StopAllBgmFadeOut(MonoBehaviour mono, float fadeTime)
              {
                  // 再生中のBGMを全て取得して停止する
                  var audioSources = _bgmAudioSourceList.Where(audioSource => audioSource.isPlaying);
                  if (audioSources.Count() <= 0)
                  {
                      return;
                  }
      
                  foreach (var audioSource in audioSources)
                  {
                      mono.StartCoroutine(StopBgmFadeOutCoroutine(audioSource, fadeTime));
                  }
              }
      
              private IEnumerator StopBgmFadeOutCoroutine(AudioSource audioSource, float fadeTime)
              {
                  var startVolume = audioSource.volume;
                  var targetVolume = 0f;
                  for (var t = 0f; t < fadeTime; t += Time.deltaTime)
                  {
                      audioSource.volume = Mathf.Lerp(startVolume, targetVolume, Mathf.Clamp01(t / fadeTime));
                      yield return null;
                  }
                  audioSource.volume = targetVolume;
      
                  audioSource.Stop();
                  audioSource.clip = null;
              }
      
      ▲再生・停止時のフェード処理
      マイケル
      マイケル
      あとはこの関数に対してフェードさせる時間を渡すようにすれば完了です。 今回は線形補間で変化させましたが、より自然に見せたい場合にはイージング関数等を用いるとよいと思います。
              private void PlayFadeBgm01()
              {
                  AudioService.PlayBgmFadeIn(this, AudioName.BgmMain, 1f);
              }
      
              private void PlayFadeBgm02()
              {
                  AudioService.PlayBgmFadeIn(this, AudioName.BgmPlay, 1f);
              }
      
      エレキベア
      エレキベア
      あとは処理の連打防止なんかも考えないといけなそうクマね

      オブジェクトからSEを鳴らす

      マイケル
      マイケル
      最後に、オブジェクト自身の位置を考慮したSE再生について紹介します。
      マイケル
      マイケル
      AudioSourceは位置情報も反映するため、遠くから鳴らすと音が小さく、近くで鳴らすと音が大きくなるといった3D再生が可能になっています。 この位置情報を有効にするためには、オブジェクトにアタッチしたAudioSourceに対して spatialBlend(通常再生と3D再生のブレンド具合) の値を0以上にする必要があります。
              private AudioSource _audioSource;
      
              private void Start()
              {
                  // AudioSourceを設定
                  _audioSource = AudioService.CreateSeAudioSource(gameObject);
                  _audioSource.spatialBlend = 0.7f; // 3D再生のブレンド具合
      
      ・・・略・・・
      
              }
      
      ▲オブジェクト側のAudioSourceのspatialBlend値を設定する
      マイケル
      マイケル
      あとは設定したAudioSourceを渡す形で再生すれば、位置情報が反映された状態で再生されるはずです。
      
              public void PlayOneShot(string audioName)
              {
                  var option = new IAudioService.AudioOption(1f, _sePitch);
                  AudioService.PlayOneShot(_audioSource, audioName, option);
              }
      
      ・・・略・・・
      
              // ----- 移動 -----
              private class StateMove : StateMachine<RobotBehaviour>.StateBase
              {
                  private static readonly float Speed = 3.5f;
                  private Vector3 _targetPosition;
      
                  public override void OnStart()
                  {
                      // 範囲内でランダムに目的地を決める
                      var randomPosX = UnityEngine.Random.Range(-3f, 3f);
                      var randomPosY = UnityEngine.Random.Range(-2f, 2f);
                      var randomPosZ = UnityEngine.Random.Range(-3f, 3f);
                      _targetPosition = new Vector3(randomPosX, randomPosY, randomPosZ);
      
                      // 動き始める時にSEを再生する
                      Owner.PlayOneShot(AudioName.SeApproach);
                  }
      
      ・・・略・・・
      
              }
      
      ▲AudioSourceを渡して再生する
      マイケル
      マイケル
      サンプルではランダムに動き回るオブジェクトから再生する機能を用意していますので、よければ確認してみてください!
      20240122_01_unity_audio_standard_08
      ▲「Start3DSE」ボタン押下で確認できます。

      エレキベア
      エレキベア
      (見覚えのあるロボットクマ・・・)
      マイケル
      マイケル
      ちなみにアニメーションによってSEを再生したい場合には、下記のようにアニメーションイベントを設定して呼び出すことでモーションに応じた再生が可能です。 こちらも組み込んでありますので、興味のある方はご参照ください!
      20240122_01_unity_audio_standard_10
      ▲アニメーションイベントの設定

      20240122_01_unity_audio_standard_11
      ▲設定したアニメーションイベント

              public void PutHitCallback()
              {
                  // アニメーション内のイベントで呼び出される
                  PlayOneShot(AudioName.SeDecide);
              }
      
      ▲アニメーションイベントから呼び出す関数
      エレキベア
      エレキベア
      これで基本的なサウンドの再生周りはマスターしたクマね

      おわりに

      マイケル
      マイケル
      というわけで今回はUnityAudioによる再生周りの処理についてでした! どうだったかな??
      エレキベア
      エレキベア
      改めてみるとなんとなく使ってた機能も多かったクマ でもやっぱりサウンドがあると楽しいクマね
      マイケル
      マイケル
      次回は更に踏み込んで、AudioMixerを使用した設定画面やエフェクト適用周りを試してみます! お楽しみに!!
      エレキベア
      エレキベア
      楽しみクマ〜〜〜〜〜

      【Unity】第一回 UnityAudioを使いこなす 〜サウンド再生処理編〜 〜完〜

      【Unity】第一回 UnityAudioを使いこなす 〜サウンド再生処理編〜
      2024-01-22
      【Unity】第二回 UnityAudioを使いこなす 〜AudioMixer活用編〜
      2024-01-22
      【Unity】第三回 UnityAudioを使いこなす 〜オーディオスペクトラム描画編〜
      2024-01-22

      UnityサウンドUnityAudio
      2024-01-22

      関連記事
      【Unity】Timeline × Excelでスライドショーを効率よく制作する
      2024-10-31
      【Unity】Boidsアルゴリズムを用いて魚の群集シミュレーションを実装する
      2024-05-28
      【ゲーム数学】第九回 p5.jsで学ぶゲーム数学「フーリエ解析」
      2024-05-12
      【Unity】GoでのランキングAPI実装とVPSへのデプロイ方法についてまとめる【Go言語】
      2024-04-14
      【Unity】第二回 Wwiseを使用したサウンド制御 〜インタラクティブミュージック編〜
      2024-03-30
      【Unity】第一回 Wwiseを使用したサウンド制御 〜基本動作編〜
      2024-03-30
      【Unity】第二回 CRI ADXを使用したサウンド制御 〜インタラクティブミュージック編〜
      2024-03-28
      【Unity】第一回 CRI ADXを使用したサウンド制御 〜基本動作、周波数解析編〜
      2024-03-28