前回紹介したUnityでのセーブ・ロード処理に続いて今度はサウンド管理についての話題です。
Unityは魅力の多いゲーム制作ツールではありますが、大きな欠点の一つとして標準ではサウンドを管理する機能がないことが挙げられます。したがって素の状態ではたくさんのサウンドファイルを扱うのが難しいので、アセットを導入するかサウンド管理クラスを自作する必要があります。
そこでUnityでの開発を進めるにあたり、このサウンド管理クラス(一般的にSoundManagerと呼ばれます)を自作することにしたので、ここではその仕組みなどについて説明します。
SoundManagerの仕様・仕組み
今回作ったSoundManagerはとてもシンプルなもので、仕様や仕組みは次のようになっています。
仕様
- Resourcesフォルダから自動的にBGMやSEを読み込む
- ファイル名を指定してBGMやSEを再生
仕組み
- Awake()のタイミングでResourcesフォルダからAudioClipを配列に読み込み、その配列からキーがファイル名・値が配列のインデックスのDictionaryを作成する
- 辞書からインデックスを取り出してAudioClipを再生
1つ目が分かりにくいので表にすると次のようになります。
配列のインデックス | AudioClipのファイル名(※) | Dictionaryのキー | Dictionaryの値 |
0 | test1 | test1 | 0 |
1 | test2 | test2 | 1 |
2 | test3 | test3 | 2 |
※これが配列に入っているわけではない。
ここで、やりたいことは「ファイル名を指定してAudioClipを再生する」ことなのですが、AudioClipは配列に入っているので直接ファイル名から検索することができません。そこでDictionaryを作って、ファイル名から配列のインデックスを得るようにすればいいというわけですね。
SoundManagerのC#スクリプト
それではSoudManagerのC#スクリプトを載せておきます(利用規約の範囲内でご利用ください)。
using System.Collections.Generic; using UnityEngine; public class SoundManager : SingletonMonoBehaviour<SoundManager> { [SerializeField, Range(0, 1), Tooltip("マスタ音量")] float volume = 1; [SerializeField, Range(0, 1), Tooltip("BGMの音量")] float bgmVolume = 1; [SerializeField, Range(0, 1), Tooltip("SEの音量")] float seVolume = 1; AudioClip[] bgm; AudioClip[] se; Dictionary<string, int> bgmIndex = new Dictionary<string, int>(); Dictionary<string, int> seIndex = new Dictionary<string, int>(); AudioSource bgmAudioSource; AudioSource seAudioSource; public float Volume { set { volume = Mathf.Clamp01(value); bgmAudioSource.volume = bgmVolume * volume; seAudioSource.volume = seVolume * volume; } get { return volume; } } public float BgmVolume { set { bgmVolume = Mathf.Clamp01(value); bgmAudioSource.volume = bgmVolume * volume; } get { return bgmVolume; } } public float SeVolume { set { seVolume = Mathf.Clamp01(value); seAudioSource.volume = seVolume * volume; } get { return seVolume; } } public void Awake() { if (this != Instance) { Destroy(gameObject); return; } DontDestroyOnLoad(gameObject); bgmAudioSource = gameObject.AddComponent<AudioSource>(); seAudioSource = gameObject.AddComponent<AudioSource>(); bgm = Resources.LoadAll<AudioClip>("Audio/BGM"); se = Resources.LoadAll<AudioClip>("Audio/SE"); for(int i = 0; i < bgm.Length; i++) { bgmIndex.Add(bgm[i].name, i); } for (int i = 0; i < se.Length; i++) { seIndex.Add(se[i].name, i); } } public int GetBgmIndex(string name) { if (bgmIndex.ContainsKey(name)) { return bgmIndex[name]; } else { Debug.LogError("指定された名前のBGMファイルが存在しません。"); return 0; } } public int GetSeIndex(string name) { if (seIndex.ContainsKey(name)) { return seIndex[name]; } else { Debug.LogError("指定された名前のSEファイルが存在しません。"); return 0; } } //BGM再生 public void PlayBgm(int index) { index = Mathf.Clamp(index, 0, bgm.Length); bgmAudioSource.clip = bgm[index]; bgmAudioSource.loop = true; bgmAudioSource.volume = BgmVolume * Volume; bgmAudioSource.Play(); } public void PlayBgmByName(string name) { PlayBgm(GetBgmIndex(name)); } public void StopBgm() { bgmAudioSource.Stop(); bgmAudioSource.clip = null; } //SE再生 public void PlaySe(int index) { index = Mathf.Clamp(index, 0, se.Length); seAudioSource.PlayOneShot(se[index], SeVolume * Volume); } public void PlaySeByName(string name) { PlaySe(GetSeIndex(name)); } public void StopSe() { seAudioSource.Stop(); seAudioSource.clip = null; } }
プログラムの解説
シングルトン
まず注意点として、このSoundManagerはSingletonMonoBehaviourを継承しています。シングルトンについて分からないという方は姉妹サイトのページ「シングルトンなゲーム管理オブジェクトの作り方」を見てもらいたいのですが、簡単に言えば「ゲーム中に一つしかないことが保証されているオブジェクト」のことです。サウンド管理オブジェクトがいくつもあっては困るのでこのようにしています。
プロパティ
プロパティに関しては
- Volume
- BgmVolume
- SeVolume
の3種類で、Volumeがマスタ音量になっています。
Awake()メソッド
Awake()内では次のような処理を行っています。
- もし別のSoundManagerがあれば削除(シングルトン関係の処理)
- シーンをまたいでもSoundManagerがアタッチされているゲームオブジェクトを削除しないようにする(DontDestroyOnLoad)
- AudioSourceの自動アタッチ
- AudioClipの読み込み
- Dictionaryに値を追加
その他のメソッドについて
サウンドを再生したり停止したりするメソッドと、指定したファイル名のAudioClipが入っている配列のインデックスを取得するメソッドがあります。
使い方
サウンドファイルをResource/Audio/BGM(またはSE)に入れたら、好きな場所からPlayBgm(orSE)ByName(“ファイル名”)で呼び出すだけです。