Unityで簡易的な音量管理システムを作ってみた

<h1>はじめに</h1>
皆さんこんにちは！
今回は簡単な音量管理システムを作ってみました。
小規模なゲーム開発等に使えるかもです。

2Dゲームでしか動作確認してませんが、多分3Dの方でも使えると思います。

<h1>1.実現できること</h1>
1.使用するBGM・SEをリスト化

2.ESCキーで音量調節画面の表示/非表示の切り替え

3.スライダーで音量の調整

4.シーンが変わっても変更した音量の大きさを保持する

5.BGM・SEの音を鳴らす

<h1>2.準備するもの</h1>
・AudioSourceがコンポーネントされたGameObject（BGM用・SE用の二つ）

・「AudioSetting_Script.cs」と「Update_Volume.cs」

・「AudioSetting_Script.cs」をコンポーネントする音量設定画面
👇こんな感じのやつ
![ボム兵のやつ - Test1 - WebGL - Unity 2021.3.15f1 Personal _DX11_ 2024_01_15 2_19_49.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3290562/86003c77-fe97-af63-4c6d-8fd8fb0d05e0.png)
BGM・SEの音量を調節するためのスライダーを設置しておいてください。
スライダーの名前はそれぞれ「BGMVolumeSlider」「SEVolumeSlider」にしておいてください。「もどる」ボタンは無くて大丈夫です。

上記の音量設定画面のGameObjectに「AudioSetting_Script.cs」をコンポーネントしておいてください。
この音量設定画面をカメラの外に**有効化した状態つまり、setActiveをTrueにした状態**で配置してください。

👇こんな感じ![ボム兵のやつ - Test1 - WebGL - Unity 2021.3.15f1 Personal _DX11_ 2024_01_15 14_48_46.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3290562/9b2a357f-6e30-0bc2-a59d-6022c368e81a.png)

これで音量設定画面がゲーム中に映らなくなります。
音量設定画面を非表示にしない理由は後程解説します。

<h1>3.コード</h1>
コードは、メインスクリプトの「AudioSetting_Script.cs」と音量を保持するスクリプトの「Update_Volume.cs」を用意します。

```C#:AudioSetting_Script.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

public class AudioSetting_Script : MonoBehaviour
{
    // スライダー
    private Slider BGMvolumeSlider;  
    private Slider SEvolumeSlider;  
    
    // オーディオソース
    public GameObject BGMaudioSource;
    public GameObject SEaudioSource;

    // BGM・SEリスト
    public List<AudioClip> BGMs;
    public List<AudioClip> SEs;

    // 音量管理システムが開かれているかどうか
    public bool Opened_Audio_Setting = false;

    void Start()
    {
        // シーン内からSliderを探して取得
        BGMvolumeSlider = GameObject.Find("BGMVolumeSlider").GetComponent<Slider>();
        SEvolumeSlider = GameObject.Find("SEVolumeSlider").GetComponent<Slider>();

        // 保存された音量を反映
        BGMvolumeSlider.value = Update_Volume.BGMsliderValue;
        SEvolumeSlider.value = Update_Volume.SEsliderValue;
    }

    void Update()
    {
        // スライダーの値を取得
        Update_Volume.BGMsliderValue = BGMvolumeSlider.value;
        Update_Volume.SEsliderValue = SEvolumeSlider.value;
        // オーディオの音量を設定
        BGMaudioSource.GetComponent<AudioSource>().volume = Update_Volume.BGMsliderValue;
        SEaudioSource.GetComponent<AudioSource>().volume = Update_Volume.SEsliderValue;
        
        // スライダーの値が変更された時の処理を登録
        BGMvolumeSlider.onValueChanged.AddListener(ChangeVolumeBGM);
        SEvolumeSlider.onValueChanged.AddListener(ChangeVolumeSE);

        //エスケープキーが押されたときの処理
        if (Input.GetKeyDown(KeyCode.Escape)&&!Opened_Audio_Setting)
        {
            Open_Audio_Setting();
        }else if(Input.GetKeyDown(KeyCode.Escape)&&Opened_Audio_Setting)
        {
            Close_Audio_Setting();
        }
    }

    void ChangeVolumeBGM(float newVolume)
    {
        // スライダーの値によって音量を変更
        BGMaudioSource.GetComponent<AudioSource>().volume = newVolume;
    }
    void ChangeVolumeSE(float newVolume)
    {
        // スライダーの値によって音量を変更
        SEaudioSource.GetComponent<AudioSource>().volume = newVolume;
    }

    void PauseGame()
    {
        Time.timeScale = 0f; // ゲームの時間を停止
    }
    void ResumeGame()
    {
        Time.timeScale = 1f; // ゲームの時間を再開
    }

    //音量調節画面を開く
    public void Open_Audio_Setting(){
        if(!Opened_Audio_Setting){
            gameObject.transform.position = new Vector3(gameObject.transform.position.x, gameObject.transform.position.y - 8f, gameObject.transform.position.z);
            Opened_Audio_Setting = true;
            PauseGame();
        }
    }

    //音量調節画面を閉じる
    public void Close_Audio_Setting(){
        if(Opened_Audio_Setting){
            gameObject.transform.position = new Vector3(gameObject.transform.position.x, gameObject.transform.position.y + 8f, gameObject.transform.position.z);    
            Opened_Audio_Setting = false;
            ResumeGame();
        }
    }

    //SEを鳴らす
    public void Play_SE(int seIndex){
        SEaudioSource.GetComponent<AudioSource>().clip = SEs[seIndex];
        SEaudioSource.GetComponent<AudioSource>().Play();
    }

    //BGMを鳴らす
    public void Play_BGM(int bgmIndex){
        BGMaudioSource.GetComponent<AudioSource>().clip = BGMs[bgmIndex];
        BGMaudioSource.GetComponent<AudioSource>().Play();
    }
}
```

```C#:Update_Volume
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Update_Volume : MonoBehaviour
{
    public static float BGMsliderValue = 0.5f;
    public static float SEsliderValue = 0.5f;
}
```
<h1>4.コード解説</h1>
<h3>1.使用するBGM・SEをリスト化</h3>

```C#:AudioSetting_Script
public List<AudioClip> BGMs;
public List<AudioClip> SEs;
```

まあ特にここは特質していう事は無いですね。
使用する楽曲をリスト化することで管理が楽になります。

<h3>2.ESCキーで音量調節画面の表示/非表示の切り替え</h3>
ESCキーでいつでも音量調整ができるようにします。

```C#:AudioSetting_Script
void Update()
{
    //エスケープキーが押されたときの処理
    if (Input.GetKeyDown(KeyCode.Escape)&&!Opened_Audio_Setting){
        Open_Audio_Setting();
    }
    else if(Input.GetKeyDown(KeyCode.Escape)&&Opened_Audio_Setting){
        Close_Audio_Setting();
    }
}
```
`Input.GetKeyDown(KeyCode.Escape)`でESCキーが押されたかどうか確認します。
Opened_Audio_SettingがFalseの時（音量調整画面が表示されていない）は`Open_Audio_Setting()`を実行し、Trueの時（音量調整画面が表示されている）は`Close_Audio_Setting()`を実行します。

```C#:AudioSetting_Script
//👇Opened_Audio_SettingがFalseの時に実行される
//音量調節画面を開く
public void Open_Audio_Setting(){
    if(!Opened_Audio_Setting){
        gameObject.transform.position = new Vector3(gameObject.transform.position.x, gameObject.transform.position.y - 8f, gameObject.transform.position.z);
        Opened_Audio_Setting = true;
        PauseGame();
    }
}
void PauseGame()
{
    Time.timeScale = 0f; // ゲームの時間を停止
}

//👇Opened_Audio_SettingがTrueの時に実行される
//音量調節画面を閉じる
public void Close_Audio_Setting(){
    if(Opened_Audio_Setting){
        // ゲームオブジェクトの相対的な移動
        gameObject.transform.position = new Vector3(gameObject.transform.position.x, gameObject.transform.position.y + 8f, gameObject.transform.position.z);    
        Opened_Audio_Setting = false;
        ResumeGame();
    }
}
void ResumeGame()
{
    Time.timeScale = 1f; // ゲームの時間を再開
}
```
`Open_Audio_Setting()`で音量調節画面の位置を変更しています。今回は現在の音量調節画面の位置からy軸に-8だけ移動させています。この時、Opened_Audio_SettingをTrueにしています。
`PauseGame()`ではゲームの進行を一時停止しています。

`Close_Audio_Setting()`は`Open_Audio_Setting()`の逆のことをやっているだけです。
`ResumeGame()`で、一時停止されたゲームを再開できます。

<h3>3.スライダーで音量の調整</h3>

```C#:AudioSetting_Script
void Start()
{
    // シーン内からSliderを探して取得
    BGMvolumeSlider = GameObject.Find("BGMVolumeSlider").GetComponent<Slider>();
    SEvolumeSlider = GameObject.Find("SEVolumeSlider").GetComponent<Slider>();

    // 保存された音量を反映
    BGMvolumeSlider.value = Update_Volume.BGMsliderValue;
    SEvolumeSlider.value = Update_Volume.SEsliderValue;
}
```
まず、シーンから「BGMVolumeSlider」・「SEVolumeSlider」というスライダーを取得して、「BGMvolumeSlider 」・「SEvolumeSlider」でそれぞれ取得します。そのスライダーの値をUpdate_Volume.csで記録されている値に設定します。

```C#:Update_Volume
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Update_Volume : MonoBehaviour
{
    public static float BGMsliderValue = 0.5f;
    public static float SEsliderValue = 0.5f;
}
```
変数をstaticで宣言するとグローバルな変数になるので、どこからでもこの変数にアクセスできるようになります。初期値はスライダーの最大値の半分の値に設定しています。

```C#:AudioSetting_Script
void Update()
{
    // スライダーの値を取得
    Update_Volume.BGMsliderValue = BGMvolumeSlider.value;
    Update_Volume.SEsliderValue = SEvolumeSlider.value;
    // オーディオの音量を設定
    BGMaudioSource.GetComponent<AudioSource>().volume = Update_Volume.BGMsliderValue;
    SEaudioSource.GetComponent<AudioSource>().volume = Update_Volume.SEsliderValue;
}
```

`Update()`で常にUpdate_Volume.csにスライダーの値を記録しています。その大きさをAudioSourceの音量にしています。

上記のコードでは、「BGMaudioSourceはBGMvolumeSliderの値に、SEaudioSourceはSEvolumeSliderの値」が設定されるようになります。

```C#:AudioSetting_Script
void Update()
{
    // スライダーの値が変更された時の処理を登録
    BGMvolumeSlider.onValueChanged.AddListener(ChangeVolumeBGM);
    SEvolumeSlider.onValueChanged.AddListener(ChangeVolumeSE);
}

void ChangeVolumeBGM(float newVolume)
{
    // スライダーの値によって音量を変更
    BGMaudioSource.GetComponent<AudioSource>().volume = newVolume;
}
void ChangeVolumeSE(float newVolume)
{
    // スライダーの値によって音量を変更
    SEaudioSource.GetComponent<AudioSource>().volume = newVolume;
}
```
AddListener関数でスライダーの値が変更されたときに、その値をAudioSourceの音量にする関数をそれぞれ実行させています。

<h3>4.シーンが変わっても変更した音量の大きさを保持する</h3>
3.のコードで既に実装はできているので、ここでは仕組みだけ解説します。

![ｗｗｗ.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3290562/c58fd51e-284e-0364-a56e-c54a23db4a13.jpeg)
図にするとだいたいこんな感じです👆

ユーザーがスライダーを動かす（①）とUpdate_Volume.csの変数へその値を保存する事（②）と、AudioSourceの音量を変える事（④）を同時に実行します。

この処理を`Update()`に書くことで、常に実行し、Update_Volume.csの値・スライダーの値・各AudioSourceの音量が常に一致するようになります。

シーンが切り替わると、`Start()`でUpdate_Volume.csで保存された値がスライダーの値に設定されます（③）。これに伴い、AudioSourceの音量もその値に変更されます（④）。

これで、シーンが切り替わっても更新した音量を保持することができます。

<h3>5.BGM・SEの音を鳴らす</h3>

```C#:AudioSetting_Script
//SEを鳴らす
public void Play_SE(int seIndex){
    SEaudioSource.GetComponent<AudioSource>().clip = SEs[seIndex];
    SEaudioSource.GetComponent<AudioSource>().Play();
}

//BGMを鳴らす
public void Play_BGM(int bgmIndex){
    BGMaudioSource.GetComponent<AudioSource>().clip = BGMs[bgmIndex];
    BGMaudioSource.GetComponent<AudioSource>().Play();
}
```

1.で作成した楽曲リストから引数の位置にある楽曲を取得し、AudioSourceに入れて、鳴らしています。
音量を鳴らすのはこんな感じに関数にしておくと、他のスクリプトで呼び出すときとかボタンで鳴らすときとか便利です。

<h1>おわりに</h1>

以上、簡易的な音量管理システムでした。シーンごとにこのシステムを設置すればいつでもESCキーで呼び出して音量を調節できるようになります。本当はこのシステムをシーンごとに配置しなくても、このシステムごとシーン間で受け継げるようにしたかったんですができませんでした・・・。やり方知っている方や、もっと良い方法を知っている方がいれば教えてください。