Unity

【Unity開発に使えるサウンド実装】BGM・SEのそれぞれ音量調整できる設計【備忘録】

本記事内には、アフィリエイトリンクを含む場合があります

サウンドはゲームの楽しさに大きく影響します。

開発の際に、サウンドをつけていきますが、
ユーザーのお好みでBGMとSE(効果音やボイス)の大きさをそれぞれ調整できたり、
どちらかを消音にできる設計にすることは、
個人的に必須だと思ってます。

(自分の好きなBGMとして流しながらプレイできると、ご飯何杯でも食えるって感じで)  

そこで、本記事では、
BGMとSEをそれぞれ調整できる設計でサウンドを実装する方法をまとめます。

なぜまとめかというと、 ネットでは非常に参考になる先人のチップスがあり、
私じしんもそれらを見て学ぶことが多く、

開発に慣れてきたら、 自分がパッとみて、
すぐ実装できるような自分用のノートを、このブログでは作りたいからです。
(ネットに繋がる環境であればいつでも確認できてよい^^)

 

BGMの追加

BGMとして使いたい音楽ファイルを
Assets_Sounds_BGMという階層でフォルダを作り、そこに格納します。
(テスト用に2つほど、格納)

なお、ここでテストに使う音楽ファイルは、
DOVA効果音フリーで拝借しました。

シーンには、
空のゲームオブジェクトで
SoundManagerと名付けて作り、さらにその配下にBGMObjectという名で作成します。
BGMObjectにAudioSourceコンポーネントをアタッチ。

BGMなので、基本PlayOnAwakeとLoopにチェックを入れときます。

(画像中のSEやスクリプトを入れているのは、次の項目で解説)

 

SE(効果音やボイスなど)の追加

使いたい効果音のファイルを
Assets_Sounds_SEという階層に格納します。
(テスト用に5つほど格納)

シーンでは、
SoundManagerの配下に、
空のゲームオブジェクトでSEObjectを作り、
さらに、その配下に空のゲームオブジェクトを4つ
NoiseSE、VoiceSE、UISE、EngineSEという名で作ってみます。
それら4つにAudioSourceコンポーネントをアタッチ。

Volumeはお好みで設定(とりあえず0.2に)。

なお、4つのオブジェクト(Audio Source)を設けた理由は、以下の通り。

NoiseSE:プレイ中の現象やエフェクトの効果音を鳴らす音楽プレイヤーをもつもの
VoiceSE:キャラクターのセリフ、声の効果音を鳴らす音楽プレイヤーをもつもの
UISE:UI動作に関する効果音を鳴らす音楽プレイヤーをもつもの
EngineSE:特定のボタンを押しているときに鳴らし、離したら止まるという、独立した制御が可能な音楽プレイヤーをもつもの

EnigineSEについて、ここでは効果音を、"ボタン押下中に鳴り、離すと止まる"という制御を行える用に設けています。
"ボタン押下中に鳴り、離すと止まる"という制御をする場合、
もしその音楽プレイヤーで他の効果音も鳴らすようにしていると、
他の効果音も意図せず、鳴り続けたり、止まったりしてしまいます。

なので、"ボタン押下中に鳴り、離すと止まる"という制御で鳴らしたい効果音の分だけ、
専用に〇〇SEを設けると良いでしょう。
(他に良い方法があれば、コメントで教えてください^^;)

 

これら4つのSEを、自分の入力操作で鳴らしてみます。
そのためのスクリプトは以下の通り。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.InputSystem;

public class SoundManager : MonoBehaviour
{
    [SerializeField] AudioClip _taikodonSE; ////フォルダから紐付け
    [SerializeField] AudioClip _supoSE; ////フォルダから紐付け
    [SerializeField] AudioClip _voiceStartSE; ////フォルダから紐付け
    [SerializeField] AudioClip _papattoBtnSE; ////フォルダから紐付け
    [SerializeField] AudioClip _rocketEngineSE; ////フォルダから紐付け

    GameObject _bGMObj;
    GameObject _sEObj;

    AudioSource _bGMAudioSource;
    AudioSource _noiseSEAudioSource;
    AudioSource _voiceSEAudioSource;
    AudioSource _uiSEAudioSource;
    AudioSource _engineSEAudioSource;////長押し再生、話して停止させるSE専用

    void Start()
    {
        
        _bGMObj = transform.GetChild(0).gameObject;
        _sEObj = transform.GetChild(1).gameObject;
        _bGMAudioSource = _bGMObj.GetComponent<AudioSource>();
        _noiseSEAudioSource = _sEObj.transform.GetChild(0).gameObject.GetComponent<AudioSource>();
        _voiceSEAudioSource = _sEObj.transform.GetChild(1).gameObject.GetComponent<AudioSource>();
        _uiSEAudioSource = _sEObj.transform.GetChild(2).gameObject.GetComponent<AudioSource>();
        _engineSEAudioSource = _sEObj.transform.GetChild(3).gameObject.GetComponent<AudioSource>();
    }

    void Update()
    {
        // if (Input.GetKeyDown (KeyCode.LeftArrow))
        if (Keyboard.current.leftArrowKey.wasPressedThisFrame)
        {
            _noiseSEAudioSource.PlayOneShot(_taikodonSE);
        }
        // if (Input.GetKeyDown (KeyCode.RightArrow))
        if (Keyboard.current.rightArrowKey.wasPressedThisFrame)
        {
            _noiseSEAudioSource.PlayOneShot(_supoSE);
        }
        // if (Input.GetKeyDown (KeyCode.UpArrow))
        if (Keyboard.current.upArrowKey.wasPressedThisFrame)
        {
            _voiceSEAudioSource.PlayOneShot(_voiceStartSE);
        }
        // if (Input.GetKeyDown (KeyCode.UpArrow))
        if (Keyboard.current.downArrowKey.wasPressedThisFrame)
        {
            _uiSEAudioSource.PlayOneShot(_papattoBtnSE);
        }

        // if(Input.GetKey(KeyCode.Space)|| boostDown)
        if(Keyboard.current.spaceKey.isPressed)
        {
            RunningEngine();
        }
        else
        {
            StopEngine();
        }   
    }

    public void RunningEngine()
    {
        if(!_engineSEAudioSource.isPlaying)
        {
            _engineSEAudioSource.PlayOneShot(_rocketEngineSE);
        }
    }
    
    void StopEngine()
    {
        _engineSEAudioSource.Stop();
    }
}

 

SoundManager.csというC#スクリプトを作ったら、SoundManagerと名付けたオブジェクトにアタッチ。

ちなみに、スクリプトはInput Systemを導入している場合のキー入力で書いています。
(旧版のInputManagerはコメントアウトに記述してます)

InputSystemの入力キーはこちら参考↓
https://sorceryforce.net/ja/tips/unity-input-keyboard

【余談】
自分は、色んなプラットフォームで遊べるようにするのに、
Input Systemは必須なので、基本的にInputSystemで設計しています。

 

色々と入力キーを押してみてください。
PlayOneShotは、同AudioSourceで重複して音が鳴らせるので、
NoiseSEで、太鼓の音に「スポッ」音を被せて鳴らせます。

また、スペースキーの押下中、エンジン音は鳴り続けている際も、全く他の効果音に影響はないことが確認できるでしょう。

BGMはスクリプトでは、BGMObjectの宣言と代入だけで、
特に何も制御していません。

ボスが現れたら、
違うBGMをAudioClipに代入するようなコードを書いたりして、
カスタムしていけば良いでしょう。

 

これでサウンドを鳴らすっということに関しては実装できました。

 

それぞれの音量調整

前述のような方法で実装すれば、音量調整も楽にできます。

Unityの機能であるAudioMixerが使います。

【参考】ここの記事でわかりやすく解説されていました↓
https://kingmo.jp/kumonos/unity-audiomixer-control-volume/

 

まず、Soundsフォルダに、右クリックのCreateから、AudioMixerを作成。
GameAudioとでも名付けます。

WindowタブからAudio_AudioMixerを選べば、設定できるウィンドウが出てきます。

GameAudioを選択した状態で、その設定をいじっていきます。
Groupsの「+」からMasterの配下にBGMとSEを作成します。

ここから、作成したトラック?のBGM、SEそれぞれに同じ処理を施します。

まずは、BGM。
BGMをクリックし、そのInspectorのVolumeを右クリックすると、
「Expose 'Volume(of BGM)' to script」が出てくるのでクリック。

さらに、AudioMixerウィンドウの右上の
「Exporsed Parameters」から「MyExporsedParam」を選択します。
この名前ではわかりにくいので、「BGM」などに変更。

これをSEにも行なっていけば、AudioMixerの準備は完了です。

 

次に、シーンの各AudioSourceコンポーネントのOutPutの項目に、
GameAudioを紐付けていきます。

BGMObjectのAudioSourceには
GameAudio_Master_BGMを、

4つの子オブジェクトSEのAudioSourceには
GameAudio_Master_SEを、それぞれ紐付けます。

これによって、各AudioSourceが放つ音楽は
AudioMixer(のBGMやSE)のVolumeによって、
音量の大小を制御できるようになります。

そして、ゲームではスライダーなどのUIから、
AudioMixerのVolumeを変化させられるよう設計します。

 

ここでは、BGMとSEの音量をスライダーで制御できるようにしてみます。

シーンにスライダーを2つ作成します。
テキストでBGM、SEと示しておきます。

また、スライダーのValueを0.5にして
デフォルトでは、中間の音量で始まるようにします。

それでは、スライダーを使って音量調整できるように、
SoundManager.csに追記していきます。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.InputSystem;
//追記
using UnityEngine.Audio;
using UnityEngine.UI;

public class SoundManager2 : MonoBehaviour
{
    [SerializeField] AudioClip _taikodonSE; ////フォルダから紐付け
    [SerializeField] AudioClip _supoSE; ////フォルダから紐付け
    [SerializeField] AudioClip _voiceStartSE; ////フォルダから紐付け
    [SerializeField] AudioClip _papattoBtnSE; ////フォルダから紐付け
    [SerializeField] AudioClip _rocketEngineSE; ////フォルダから紐付け

    GameObject _bGMObj;
    GameObject _sEObj;

    AudioSource _bGMAudioSource;
    AudioSource _noiseSEAudioSource;
    AudioSource _voiceSEAudioSource;
    AudioSource _uiSEAudioSource;
    AudioSource _engineSEAudioSource;////長押し再生、話して停止させるSE専用

    //追記
    [SerializeField] AudioMixer _audioMixer;
    [SerializeField] Slider _bGMSlider;
    [SerializeField] Slider _sESlider;

    void Start()
    {
        
        _bGMObj = transform.GetChild(0).gameObject;
        _sEObj = transform.GetChild(1).gameObject;
        _bGMAudioSource = _bGMObj.GetComponent<AudioSource>();
        _noiseSEAudioSource = _sEObj.transform.GetChild(0).gameObject.GetComponent<AudioSource>();
        _voiceSEAudioSource = _sEObj.transform.GetChild(1).gameObject.GetComponent<AudioSource>();
        _uiSEAudioSource = _sEObj.transform.GetChild(2).gameObject.GetComponent<AudioSource>();
        _engineSEAudioSource = _sEObj.transform.GetChild(3).gameObject.GetComponent<AudioSource>();

        //追記
        SetBGMVolume(_bGMSlider.value);
        SetSEVolume(_sESlider.value);
        _bGMSlider.onValueChanged.AddListener(SetBGMVolume);
        _sESlider.onValueChanged.AddListener(SetSEVolume);
    }

    //追記
    public void SetBGMVolume(float volume)
    {
        _audioMixer.SetFloat("BGM",Mathf.Clamp(Mathf.Log10(volume) * 20f,-80f,0f));
    }
    public void SetSEVolume(float volume)
    {
        _audioMixer.SetFloat("SE",Mathf.Clamp(Mathf.Log10(volume) * 20f,-80f,0f));
    }

    void Update()
    {
        // if (Input.GetKeyDown (KeyCode.LeftArrow))
        if (Keyboard.current.leftArrowKey.wasPressedThisFrame)
        {
            _noiseSEAudioSource.PlayOneShot(_taikodonSE);
        }
        // if (Input.GetKeyDown (KeyCode.RightArrow))
        if (Keyboard.current.rightArrowKey.wasPressedThisFrame)
        {
            _noiseSEAudioSource.PlayOneShot(_supoSE);
        }
        // if (Input.GetKeyDown (KeyCode.UpArrow))
        if (Keyboard.current.upArrowKey.wasPressedThisFrame)
        {
            _voiceSEAudioSource.PlayOneShot(_voiceStartSE);
        }
        // if (Input.GetKeyDown (KeyCode.UpArrow))
        if (Keyboard.current.downArrowKey.wasPressedThisFrame)
        {
            _uiSEAudioSource.PlayOneShot(_papattoBtnSE);
        }

        // if(Input.GetKey(KeyCode.Space)|| boostDown)
        if(Keyboard.current.spaceKey.isPressed)
        {
            RunningEngine();
        }
        else
        {
            StopEngine();
        }   
    }

    public void RunningEngine()
    {
        if(!_engineSEAudioSource.isPlaying)
        {
            _engineSEAudioSource.PlayOneShot(_rocketEngineSE);
        }
    }
    void StopEngine()
    {
        _engineSEAudioSource.Stop();
    }
}

 

 

スクリプトを更新したら、
インスペクター上でSoundManagerコンポーネントの項目に
AudioMixerのGameAudio、BGMSlider、SESliderを紐づけます。

値の制御ですが、
スライダーのValueをlogに入れて、20掛けています。
Mathf.Clampで最大と最小を決めて、値を丸めることで、
ちょうどよい音量調整ができるようになります。

Mathf.Clampがとてもわかりやすく解説されてる記事はこちら↓
https://nekojara.city/unity-clamp

 

サウンドの実装まとめは、以上です。

ゲーム開発の都度使えるテクニックだと思うので、
ぜひブックマークしとくとよいでしょう!^^

 

ABOUT ME
いなも@システマライフハッカー
”仙豆”を開発することを夢見て、健康食品会社で働いていたものの、2016年に出会ったロシアの武術”システマ”こそ、その糸口があると感銘し、勝手にシステマ普及活動を始める。 一方で、クリエイティブなモノ作りが好きで、DX社会で楽しみを見出せる"Unity”を活かして、”スマートかつ快適な暮らし”のヒントを発信している。

COMMENT

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

CAPTCHA