【Unity】BGMをSingletonにしてシーンで切り替えないようにする

Unity

Unity初心者です。

初心者の為、いろいろな場所で躓くので、備忘録として実施した技術についてブログに記載するようにしています。

AudioSource(BGM)につきまして、シーンごとで切り替えをしたくないと思ったのですが、シーンが切り替わるとゲームオブジェクトが破棄されてしまうので困りました。

その時に解決した方法について記載をしたいと思います。

シーンが切り替わるとBGMが消えてしまう

冒頭にも書きましたが、シーンが切り替わるとゲームオブジェクトが破棄されてしまうためBGMが消えてしまいます。

次のシーンでも同じBGMを使い回したい場合も多いので工夫が必要でした。(メニューを別シーンにしたけどフィールドシーンの音楽をそのまま使いたいとか)

※新しいシーンでBGMを始めても、1回BGMが終わるので、これも違和感があります。

↑自分は「AudioMaster」というオブジェクトでAudioSourceを使用していたが、これが別シーンに引き継げない。

対応として「DontDestroyOnLoad」というメソッドを使うと、シーンを切り替えてもオブジェクトが破棄されなくなるらしいので、これでBGMをシーン間で引き継ぐことが出来ました。

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

public class DontDestroy : MonoBehaviour {

     void Start () {
             DontDestroyOnLoad (this);
     }

}

↑BGM操作用のオブジェクトにAddする

オブジェクトが破棄されないのに再作成される

しかし、次の問題が発生しました。

このゲームオブジェクトは残り続けるのに、そのオブジェクトがあるシーンを開くと、オブジェクトが再作成されてしまうということです。

つまりBGMが2重に再生されてしまいました。

どちらかのBGMを切ればいいじゃん?

と思ったのですがオブジェクトが無限増殖すると気持ち悪いし性能面も気になるので、既にあれば再作成しないようにしました。

public class MenuGameMaster : MonoBehaviour
{    
    static AudioMaster prevaudiomaster = null;
    public AudioMaster audiomaster;

    void Start(){
        if(prevaudiomaster == null){
            prevaudiomaster = audiomaster;
            audiomaster.play();
        } else {
            Destroy(audiomaster);
            audiomaster = prevaudiomaster;
        }
    }
}

↑BGM再生用とは別のオブジェクトのスクリプト

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

public class AudioMaster : MonoBehaviour
{
    void Start () {
        DontDestroyOnLoad (this);
    }

    public void play(){
        AudioSource audioSource = GetComponent<AudioSource>();
        audioSource.Play();
    }

    public void stop(){
        AudioSource audioSource = GetComponent<AudioSource>();
        audioSource.Stop();
    }
}

↑BGM用ゲームオブジェクトのスクリプト

静的な変数に前回のBGM用ゲームオブジェクトを保存しておき、もしそれがあれば作成されるBGM用ゲームオブジェクトをDestroyします。

これで擬似的にSingletonになったので、BGMが2重に再生されてしまう現象も回避できました。

(1回作って破棄しているので、もっとうまいやり方もありそうではありますが…)

BGM再生用のオブジェクトはゲーム内で1つでもいいのでは?

DontDestroyOnLoadを使用しているのでゲーム内では常にこのオブジェクトがあることが想定できます。

であれば、BGMの再生はこのオブジェクトに全部任せるという方法も良さそうな気がしますね。

SEとかは個別のオブジェクトでも良いとかなと思ったのですが、SEも1オブジェクトに任せれば、音量調整機能とかもシンプルに作れます。

もしAudioSourceがゲーム内に複数ある時は、静的な変数で値を使い回さないといけないので若干可読性が低いです。

↓こんな感じで引数でAudioClipを渡して変更すれば良さそう

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

public class AudioMaster : MonoBehaviour
{
    void Start () {
        DontDestroyOnLoad (this);
    }

    public void play(AudioClip audioClip){
        AudioSource audioSource = GetComponent<AudioSource>();
        audioSource.clip = audioClip;
        audioSource.Play();
    }

    public void stop(){
        AudioSource audioSource = GetComponent<AudioSource>();
        audioSource.Stop();
    }
}

↓呼び元

AudioMaster am = GameObject.Find("AudioMaster").GetComponent<AudioMaster>();
AudioClip audio = Resources.Load("Audio/hoge") as AudioClip;
am.play(audio);

コメント

タイトルとURLをコピーしました