Unityのゲームに効果音をつけよう AudioManager

Unityで効果音を付けたい。しかし、効果音を鳴らすコードを書くのはは面倒だ。そうだ一括管理しようそうしよう。

※ この記事は
>
[ぼくがかんがえたさいきょうのAudioManager【Unity】](http://kan-kikuchi.hatenablog.com/entry/AudioManager)
[ぼくがかんがえたさいきょうのAudioManager2【Unity】](http://kan-kikuchi.hatenablog.com/entry/AudioManager2)

をちょっとだけわかりやすく書いた記事です。

# この記事でできること

+ 効果音/BGMをScriptから鳴らすことができる
+ 効果音/BGMを1つのScriptから複数鳴らすことができる
+ AudioManagerをつくることができる

>
AudioManagerとは読んで字の如く、BGMやSEなどのオーディオファイルの再生や停止を管理するクラスです。
「音が鳴る物にAudioSouce付けて、全シーン毎にAudioListenerを用意して、オーディオファイルを追加する度にAudioClip付けて」ってのがめんどくさかったので、全シーン、どのスクリプトからもBGMとSEを操作出来るようにしたかったため作りました。(引用 [ぼくがかんがえたさいきょうのAudioManager【Unity】](http://kan-kikuchi.hatenablog.com/entry/AudioManager))

用意するもの

+ AudioManager.cs (新しく作る)
+ Resourcesフォルダ 
+ 音源

# AudioManager.csを書く

C#スクリプトを新規作成し、下のように書く

```csharp:AudioManager.cs
using UnityEngine;
using System.Collections;
using System.Collections.Generic;

/// <summary>
/// BGMとSEの管理をするマネージャ。シングルトン。
/// </summary>
public class AudioManager : AudioManager
{
	//ボリューム保存用のkeyとデフォルト値
	private const string BGM_VOLUME_KEY = "BGM_VOLUME_KEY";
	private const string SE_VOLUME_KEY  = "SE_VOLUME_KEY";
	private const float  BGM_VOLUME_DEFULT = 1.0f;
	private const float  SE_VOLUME_DEFULT  = 1.0f;


	//オーディオファイルのパス
	private const string BGM_PATH = "Audio/BGM";
	private const string SE_PATH  = "Audio/SE";


	//BGMがフェードするのにかかる時間
	public const float BGM_FADE_SPEED_RATE_HIGH = 0.9f;
	public const float BGM_FADE_SPEED_RATE_LOW  = 0.3f;
	private float _bgmFadeSpeedRate = BGM_FADE_SPEED_RATE_HIGH;

	//次流すBGM名、SE名
	private string _nextBGMName;
	private string _nextSEName;

	//BGMをフェードアウト中か
	private bool _isFadeOut = false;

	//BGM用、SE用に分けてオーディオソースを持つ
	private AudioSource       _bgmSource;
	private List<AudioSource> _seSourceList;
	private const int SE_SOURCE_NUM = 10;

	//全AudioClipを保持
	private Dictionary<string, AudioClip> _bgmDic, _seDic;
  
  public static AudioManager Instance = null;

	//=================================================================================
	//初期化
	//=================================================================================

	private void Awake ()
	{
		if (this != Instance) {
			Destroy (this);
			return;
		}

		DontDestroyOnLoad (this.gameObject);

		//オーディオリスナーおよびオーディオソースをSE+1(BGMの分)作成
		gameObject.AddComponent<AudioListener>();
		for(int i = 0; i < SE_SOURCE_NUM + 1; i++){
			gameObject.AddComponent<AudioSource>();
		}

		//作成したオーディオソースを取得して各変数に設定、ボリュームも設定
		AudioSource[] audioSourceArray = GetComponents<AudioSource> ();
		_seSourceList = new List<AudioSource> ();

		for(int i = 0; i < audioSourceArray.Length; i++){
			audioSourceArray [i].playOnAwake = false;

			if(i == 0){
				audioSourceArray [i].loop = true;
				_bgmSource = audioSourceArray [i];
				_bgmSource.volume = PlayerPrefs.GetFloat (BGM_VOLUME_KEY, BGM_VOLUME_DEFULT);
			}
			else{
				_seSourceList.Add (audioSourceArray [i]);
				audioSourceArray [i].volume  = PlayerPrefs.GetFloat (SE_VOLUME_KEY,  SE_VOLUME_DEFULT);
			}

		}

		//リソースフォルダから全SE&BGMのファイルを読み込みセット
		_bgmDic = new Dictionary<string, AudioClip> ();
		_seDic  = new Dictionary<string, AudioClip> ();

		object[] bgmList = Resources.LoadAll (BGM_PATH);
		object[] seList  = Resources.LoadAll (SE_PATH);

		foreach (AudioClip bgm in bgmList) {
			_bgmDic [bgm.name] = bgm;
		}
		foreach (AudioClip se in seList) {
			_seDic [se.name] = se;
		}

	}

	//=================================================================================
	//SE
	//=================================================================================

	/// <summary>
	/// 指定したファイル名のSEを流す。第二引数のdelayに指定した時間だけ再生までの間隔を空ける
	/// </summary>
	public void PlaySE (string seName, float delay = 0.0f)
	{
		if (!_seDic.ContainsKey (seName)) {
			Debug.Log (seName + "という名前のSEがありません");
			return;
		}

		_nextSEName = seName;
		Invoke ("DelayPlaySE", delay);
	}

	private void DelayPlaySE ()
	{
		foreach(AudioSource seSource in _seSourceList){
			if(!seSource.isPlaying){
				seSource.PlayOneShot (_seDic [_nextSEName] as AudioClip);
				return;
			}
		}
	}

	//=================================================================================
	//BGM
	//=================================================================================

	/// <summary>
	/// 指定したファイル名のBGMを流す。ただし既に流れている場合は前の曲をフェードアウトさせてから。
	/// 第二引数のfadeSpeedRateに指定した割合でフェードアウトするスピードが変わる
	/// </summary>
	public void PlayBGM (string bgmName, float fadeSpeedRate = BGM_FADE_SPEED_RATE_HIGH)
	{
		if (!_bgmDic.ContainsKey (bgmName)) {
			Debug.Log (bgmName + "という名前のBGMがありません");
			return;
		}

		//現在BGMが流れていない時はそのまま流す
		if (!_bgmSource.isPlaying) {
			_nextBGMName = "";
			_bgmSource.clip = _bgmDic [bgmName] as AudioClip;
			_bgmSource.Play ();
		}
		//違うBGMが流れている時は、流れているBGMをフェードアウトさせてから次を流す。同じBGMが流れている時はスルー
		else if (_bgmSource.clip.name != bgmName) {
			_nextBGMName = bgmName;
			FadeOutBGM (fadeSpeedRate);
		}
	}

	/// <summary>
	/// BGMをすぐに止める
	/// </summary>
	public void StopBGM ()
	{
		_bgmSource.Stop ();
	}

	/// <summary>
	/// 現在流れている曲をフェードアウトさせる
	/// fadeSpeedRateに指定した割合でフェードアウトするスピードが変わる
	/// </summary>
	public void FadeOutBGM (float fadeSpeedRate = BGM_FADE_SPEED_RATE_LOW)
	{
		_bgmFadeSpeedRate = fadeSpeedRate;
		_isFadeOut = true;
	}

	private void Update ()
	{
		if (!_isFadeOut) {
			return;
		}

		//徐々にボリュームを下げていき、ボリュームが0になったらボリュームを戻し次の曲を流す
		_bgmSource.volume -= Time.deltaTime * _bgmFadeSpeedRate;
		if (_bgmSource.volume <= 0) {
			_bgmSource.Stop ();
			_bgmSource.volume = PlayerPrefs.GetFloat (BGM_VOLUME_KEY, BGM_VOLUME_DEFULT);
			_isFadeOut = false;

			if (!string.IsNullOrEmpty (_nextBGMName)) {
				PlayBGM (_nextBGMName);
			}
		}

	}

	//=================================================================================
	//音量変更
	//=================================================================================

	/// <summary>
	/// BGMとSEのボリュームを別々に変更&保存
	/// </summary>
	public void ChangeVolume (float BGMVolume, float SEVolume)
	{
		_bgmSource.volume = BGMVolume;
		foreach(AudioSource seSource in _seSourceList){
			seSource.volume  = SEVolume;
		}

		PlayerPrefs.SetFloat (BGM_VOLUME_KEY,  BGMVolume);
		PlayerPrefs.SetFloat (SE_VOLUME_KEY,   SEVolume);
	}

}
```

## 空のオブジェクトにアタッチする。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/96970/b38bfff9-df3f-8789-5325-232982e8d6b2.png)

**重要**
基本的には、ゲームの最初のシーンに置く。
AudioManagerオブジェクトはシングルトン化しており、シーンが変わっても破壊されないようになっている。

# Resourcesフォルダを作る

![image.png](https://qiita-image-store.s3.amazonaws.com/0/96970/b693c25f-2ce6-0ff9-27cf-beb12f4952c1.png)

名前を大文字小文字まで正確に書こう。

# 音源をフォルダに入れる

効果音はSEフォルダ、BGMはBGMフォルダに入れよう。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/96970/e727ca8e-a351-4198-f1e6-678dd6ca0260.png)

このようになる。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/96970/24248e98-2c83-90ac-6312-debe63d005f6.png)

ここまでくればあとは使うのみ！

# 使い方

好きなScriptで
`AudioManager.Instance.PlaySE("SE名");`
`AudioManager.Instance.PlayBGM("BGN名");`
と書く。

引数に書いた名前の音源をResourcesフォルダから読み込み再生するのだ。例えば

```csharp
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Test : MonoBehaviour {

	// Use this for initialization
	void Start () {
        AudioManager.Instance.PlaySE("swish1");
		
	}
	
	// Update is called once per frame
	void Update ()
    {
        if (Input.GetMouseButtonDown (0)) {
            AudioManager.Instance.PlaySE("pickup01");
        }
		
	}
}
```

このように書く。

# おまけ

## 音源について

基本mp3形式なら大丈夫。
他にも.aif,.wav,.ogg .xm,.mod,.it,.s3m 形式をサポートしている。

iPhoneの録音機能の場合はm4a形式となるので、変換が必要である。

私はだいたいフリー音源サイトで探している。
http://taira-komori.jpn.org/game01.html

サイト名の表記が必要となる音源サイトが多いので、公開する場合は注意すること。