プレイするゲームがないなら、自分で作ればいいじゃない!
ということで Unity 初心者が「ゲームを作る」ゲームをやっていきます!
今回は「2Dサウンド(BGM、SE)対応」の記事です
内容
「BGM、SE」を再生するクラス【SoundManager】の作成
音を鳴らすには
音に必要なのは「耳と音源」になります
Audio Listener
Unity での耳、Main Camera に始めから追加されている
2Dゲームの場合はこのままで問題ない
Audio Source
Unity での音源、音を鳴らしたいオブジェクトに追加する
今回はサウンドマネージャーのスクリプトから追加します
Audio Clip
音のデータ、これを AudioSource で再生する
BGM等のサイズの大きいデータは「ロードタイプをストリーミング」にしておくといい
![](https://kaleido-blog.com/wp-content/uploads/2022/01/unity_sound_1.jpg)
BGMとSEのスクリプト
別に分けなくてもいいかもしれないが…
SE用
AudioSource を設定して、再生するだけのシンプルなもの
public class SeSound
{
AudioSource source = null;
// オーディオソースの設定
public void SetAudioSource(AudioSource _source)
{
source = _source;
}
// 再生
public void Play(AudioClip _clip, float _volume)
{
source.PlayOneShot(_clip, _volume);
}
}
AudioSource.PlayOneShot(AudioClip, float)
音データと音量(0~1)を設定する
※音量の部分は省略可能(その場合は1になる)
重複して再生ができる:SE1を再生中にSE2を再生してもSE1が消えない
BGM用
SEの処理に停止関係とフェードの処理を追加している
public class BgmSound
{
// BGM状態
private enum eBgmState
{
BGM_STOP = 0,
BGM_PLAY,
BGM_FADEIN,
BGM_FADEOUT,
};
AudioSource source = null; // オーディオソース
float fade = 0; // フェード処理用
float fadeTime = 0; // フェード時間
float volume = 0; // ボリューム
bool isPause = false; // 一時停止中か
eBgmState state = eBgmState.BGM_STOP; // サウンドの状態
// 更新
public void Update()
{
if (isPause) { return; }
// フェードイン
if(state == eBgmState.BGM_FADEIN)
{
fade += Time.deltaTime;
if(fade >= fadeTime)
{
state = eBgmState.BGM_PLAY;
SetVolume(volume);
}
else
{
source.volume = Mathf.Lerp(0, volume, fade / fadeTime);
}
}
// フェードアウト
else if (state == eBgmState.BGM_FADEOUT)
{
fade += Time.deltaTime;
if (fade >= fadeTime)
{
Stop();
}
else
{
source.volume = Mathf.Lerp(volume, 0, fade / fadeTime);
}
}
}
// オーディオソースの設定
public void SetAudioSource(AudioSource _source)
{
source = _source;
}
// 再生中か
public bool IsPlaying()
{
// 一時停止も判定に入れる?
//if (isPause)
//{
// return false;
//}
return source.isPlaying;
}
// 再生
public void Play(AudioClip _clip, float _volume, float _fade, bool _loop)
{
fade = 0;
fadeTime = _fade;
volume = _volume;
isPause = false;
source.clip = _clip;
source.volume = 0;
source.loop = _loop;
source.Play();
state = eBgmState.BGM_FADEIN;
}
// フェードアウト
public void FadeOut(float _fade)
{
fade = 0;
fadeTime = _fade;
state = eBgmState.BGM_FADEOUT;
}
// 一時停止
public void Pause()
{
isPause = true;
source.Pause();
}
// 一時停止解除
public void UnPause()
{
isPause = false;
source.UnPause();
}
// 停止
public void Stop()
{
source.Stop();
state = eBgmState.BGM_STOP;
}
// 音量設定
public void SetVolume(float _volume)
{
if (state != eBgmState.BGM_PLAY)
{
volume = _volume;
return;
}
// 再生中の音は設定する
source.volume = _volume;
}
}
AudioSource.Play(ulong)
ディレイタイムを設定する
※ディレイタイムの部分は省略可能(その場合は0になり、即再生される)
重複して再生されない:BGM1を再生中にBGM2を再生するとBMG1が消える
SoundManager のスクリプト
BGMとSEのクラスを管理するクラス
まだ単純な操作しかできない、適宜欲しい機能を追加する
※ LikeDictionary は後述
public class SoundManager : MonoBehaviour
{
public static SoundManager Instance { get; private set; } = null; // シングルトン
[SerializeField] LikeDictionary<string, AudioClip> soundDictionary; // サウンドデータ
BgmSound[] bgmTbl = new BgmSound[2]; // BGM
SeSound se; // SE
List<string> nameList = new List<string>(); // SE用の再生リスト
float bgmVolume = 1.0f; // BGMのボリューム
float seVolume = 1.0f; // SEMのボリューム
void Awake()
{
if (Instance == null)
{
// インスタンスがない場合は代入
Instance = this;
// シーン切り替え時に消さないようにする
DontDestroyOnLoad(this.gameObject);
}
else
{
// ある場合は2つ目なのでゲームオブジェクトを消す
Destroy(this.gameObject);
return;
}
// LikeDictionaryの初期化
soundDictionary.Init();
// オーディオソースの設定
for (int i=0; i<bgmTbl.Length; ++i)
{
bgmTbl[i] = new BgmSound();
bgmTbl[i].SetAudioSource(gameObject.AddComponent<AudioSource>());
}
{
se = new SeSound();
se.SetAudioSource(gameObject.AddComponent<AudioSource>());
}
nameList.Clear();
}
void LateUpdate()
{
// BGMの更新
foreach (BgmSound _bgm in bgmTbl)
{
_bgm.Update();
}
// 再生があれば再生
foreach (string _name in nameList)
{
Play(_name);
}
// 再生リストを空に
nameList.Clear();
}
// BGM再生
public void PlayBgm(string _name, float _fade = 0, bool _loop = true)
{
foreach (BgmSound _bgm in bgmTbl)
{
if (!_bgm.IsPlaying())
{
_bgm.Play(soundDictionary.Value(_name), bgmVolume, _fade, _loop);
break;
}
}
}
// BGM切り替え
public void ChangeBgm(string _name, float _fade = 0, bool _loop = true)
{
// 今鳴っているものをフェードアウト
foreach (BgmSound _bgm in bgmTbl)
{
if (_bgm.IsPlaying())
{
_bgm.FadeOut(_fade);
break;
}
}
// 再生
PlayBgm(_name, _fade, _loop);
}
// SE再生(予約)
public void PlaySe(string _name)
{
// 同じフレームに同じSEは再生しないようにする
if(nameList.Exists(n => n == _name)){
return;
}
nameList.Add(_name);
}
// BGMの一時停止
public void PauseBgm()
{
foreach (BgmSound _sound in bgmTbl)
{
_sound.Pause();
}
}
// BGMの一時停止を解除
public void UnPauseBgm()
{
foreach (BgmSound _sound in bgmTbl)
{
_sound.UnPause();
}
}
// BGMの音量を設定
public void SetBgmVolume(float _volume)
{
bgmVolume = Mathf.Clamp(_volume, 0, 1.0f);
foreach (BgmSound _sound in bgmTbl)
{
_sound.SetVolume(bgmVolume);
}
}
// SEの音量を設定
public void SetSeVolume(float _volume)
{
seVolume = Mathf.Clamp(_volume, 0, 1.0f);
}
// SE再生
void Play(string _name)
{
se.Play(soundDictionary.Value(_name), seVolume);
}
}
1フレームで同じ SE を鳴らさないように nameList で制御している
void LateUpdate()
Update処理の最後で呼び出される
BGM の更新(フェード関係)と List にある SE の再生を行う
LikeDictionary のスクリプト
サウンド情報をインスペクタービューで設定したかったので、色々試した結果
Dictionary を使って名前から AudioClip を参照する形になりました
![](https://kaleido-blog.com/wp-content/uploads/2022/01/unity_sound_2.jpg)
Dictionary だと…
しかし Unity の仕様で Dictionary はインスペクタービューに表示されません!
ということで色々調べて「エディタ拡張」など様々なやり方を見た結果…
List で設定したものを Dictionary に移すという形になりました
LikeDictionary.cs
値しか取れない、他の機能は適宜追加する
[Serializable]
public class LikeDictionary<Tkey, Tvalue>
{
[Serializable]
private struct DictionaryData
{
public Tkey key;
public Tvalue value;
}
[SerializeField] List<DictionaryData> list; // エディター用のリスト
Dictionary<Tkey, Tvalue> dictionary; // サウンドデータ
// list ⇒ dictionary にデータを移す
public void Init()
{
dictionary = new Dictionary<Tkey, Tvalue>();
while (list.Count > 0)
{
dictionary.Add(list[0].key, list[0].value);
list.RemoveAt(0);
}
}
// 値を取得
public Tvalue Value(Tkey _key)
{
return dictionary[_key];
}
}
Init を呼ぶと List ⇒ Dictionary にデータを移す
処理的に設定数が増えてくると重くなりそうですが
求めすぎると先に進めなくなるので、重くなった時に考えることにしましょう
再生してみる
空のオブジェクトを作成⇒「SoundManager のスクリプト」を追加します
※そのオブジェクトをプレハブにしておく
インスペクタービューで「再生用の名前とサウンドデータ」を設定する
あとは音を再生したい箇所にスクリプトを書いていきましょう
// BGM再生の例
SoundManager.Instance.ChangeBgm("bgm_title", 0.2f);
// SE再生の例
SoundManager.Instance.PlaySe("cursor_move");
サンプル動画
さいごに
音を鳴ると一気に雰囲気が変わりますね!
今回のものは簡単な設定しかしていませんが
Audio~ は他にも色々な設定が可能なので、確認してみるといいかもしれません
コメント