久々のUnity記事です。
BGM・SEはゲームには(ほぼ)必須の要素です。
それを管理するためのクラスをいい感じに実装できないかと思って色々調べた結果、シングルトンを使うのがいいんじゃないかという結論になり実際に書いてみました。
あくまで勉強&自分用で作ったスクリプトなので、「こんなやり方があるんだ~」くらいの感じで見てみてください。
スクリプト
BGM再生、SE再生、BGMのフェードアウト、音量の変更ができます。
とりあえず最低限これがあればいいかな、というのを入れただけなのでまだまだ改良の余地があります。
using UnityEngine;
/// <summary>
/// サウンド管理クラス
/// </summary>
public class SoundManager : MonoBehaviour
{
/// <summary>
/// BGMリスト
/// </summary>
public enum BGM_Type
{
Title,
Menu,
Field,
}
/// <summary>
/// SEリスト
/// </summary>
public enum SE_Type
{
Select,
Cancel,
}
public static SoundManager instance;
[SerializeField]
private AudioSource BGM_Source, SE_Source;
[SerializeField]
private AudioClip[] BGM_Clip, SE_Clip;
private float fadeOutSpeed;
private BGM_Type nextBGMIndex;
private float BGM_Volume = 1f;
private bool fadeFlg = false;
void Awake()
{
if (instance == null)
{
instance = this;
DontDestroyOnLoad(gameObject);
}
else
{
Destroy(gameObject);
}
}
void Update()
{
if (!fadeFlg) return;
BGM_Source.volume -= Time.deltaTime * fadeOutSpeed;
if (BGM_Source.volume <= 0)
{
BGM_Source.Stop();
BGM_Source.volume = BGM_Volume;
// 次のBGMを再生
PlayBGM(nextBGMIndex);
fadeFlg = false;
}
}
/// <summary>
/// BGM再生
/// </summary>
/// <param name="bgmIndex">再生するBGM番号</param>
public void PlayBGM(BGM_Type bgmIndex)
{
BGM_Source.clip = BGM_Clip[(int)bgmIndex];
BGM_Source.Play();
}
/// <summary>
/// SE再生
/// </summary>
/// <param name="seIndex">再生するSE番号</param>
public void PlaySE(SE_Type seIndex)
{
SE_Source.clip = SE_Clip[(int)seIndex];
SE_Source.PlayOneShot(SE_Source.clip);
}
/// <summary>
/// BGM停止
/// </summary>
public void StopBGM()
{
BGM_Source.Stop();
}
/// <summary>
/// BGMのフェードアウト
/// </summary>
/// <param name="fadeSpeed">フェードアウトのスピード</param>
/// <param name="bgmIndex">次に再生するBGM番号</param>
public void FadeOutBGM(float fadeOutSpeed, BGM_Type bgmIndex)
{
this.fadeOutSpeed = fadeOutSpeed;
nextBGMIndex = bgmIndex;
fadeFlg = true;
}
/// <summary>
/// 音量変更
/// 変更しない場合は-1を指定
/// </summary>
/// <param name="BGMVolume">BGM音量</param>
/// <param name="SEVolume">SE音量</param>
public void ChangeVolume(float BGMVolume, float SEVolume)
{
if (BGMVolume != -1)
{
BGM_Source.volume = BGMVolume;
BGM_Volume = BGMVolume;
}
if (SEVolume != -1)
{
SE_Source.volume = SEVolume;
}
}
}
使い方例
以下のサンプルではボタンで効果音とBGMのフェードアウトをするようになっています。
using UnityEngine;
public class Test : MonoBehaviour
{
SoundManager soundManager;
// Start is called before the first frame update
void Start()
{
soundManager = SoundManager.instance;
soundManager.PlayBGM(SoundManager.BGM_Type.Title);
}
// Update is called once per frame
void Update()
{
if (Input.GetKeyDown(KeyCode.Z))
{
soundManager.PlaySE(SoundManager.SE_Type.Select);
}
if (Input.GetKeyDown(KeyCode.X))
{
soundManager.PlaySE(SoundManager.SE_Type.Cancel);
}
if (Input.GetKeyDown(KeyCode.Space))
{
soundManager.FadeOutBGM(2f, SoundManager.BGM_Type.Menu);
}
}
}
Audio SourceはBGM用とSE用の2つをそれぞれ用意します。
Clipには音楽ファイルを設定しますが、スクリプトの列挙型(enum)で記述しているものと同じ順番になるようにします。
例えば今回のスクリプト例でいうとBGMはタイトル、メニュー、フィールドの順になります。

余談
実装はこんな感じで出来ますが、シングルトンで調べるとあまり使うのはよくないっぽい内容が結構でてきますね。
デメリットとして、主に「ユニットテストが難しくなる」「グローバル変数と同じ危険性がある」というのが問題になってくるようです。
このインスタンスは絶対1つだけじゃないとダメ!っていう場合なら多分大丈夫だと思うんですが、便利だからって乱用するのは良くない…ということだと思います。
その辺りもいずれ勉強が必要になりそうです。
コメント