【Unity】AudioSourceの再生終了判定などのサウンド管理【C#】

AudioSouceの再生終了判定
スポンサーリンク

Unityで音を使う時に便利なAudioSource。しかし終了判定をしたいと思った時に「isPlaying」だとポーズ中との違いが判定できないので少し困ってしまいます。

そこで今回は、そういった細々としたAudioSourceを使う上で足りなかった部分を補完するためのサウンド管理スクリプトを作ってみたいと思います。BGMファイルの再生終了時のステータス取得・途中再生・フェードイン・フェードアウトなど、基本的に使いそうなものをまとめて管理します!

こんな人におすすめ

・Unityでサウンド管理を行いたい
・再生終了時取得や途中再生の処理を行いたい
・フェードインとフェードアウト処理も追加したい

最終的な使用感

最終的にはゲーム内で使うBGMサウンドを、まとめて扱う管理クラスを作ります。

スクリプトの使用感

そのため、複数のサウンドを設定できる項目と、フェードイン・フェードアウト時間の指定をインスペクターで行い、あとは再生を呼び出すだけで処理を実行してくれるように設計したいと思います。

また追加として時間指定による途中再生機能や、設定した曲からランダムに再生といった機能も入れていきます!

サンプルプロジェクトの作成

サンプルプロジェクト

今回のサンプルでは以下のものを使います。

・空のオブジェクト2個
それぞれサンプルスクリプトの割り当てに使用します。

・BGM2曲
1曲でもスクリプト的には問題ないのですが、動きの確認のために2曲あるとチェックが行いやすいです。

・Audio Source
このAudio SourceにAudio Clipを設定しつつ曲を管理します。注意点として「Play On Awake」のチェックを外すことと、「Volume」を0に変更することだけは最初にやっておいてください!

・サンプルスクリプト2種類
メインのサウンド管理のスクリプトと、動きを簡易的に確認するためのスクリプトの2種類を次の項目に記載します。それぞれコピペしてください! ※名前を変えると上手く動かない部分が出てくるので、名前はそのままお使いください。

サンプルスクリプト

ここで2種類のサンプルスクリプトを作ります

まずはサウンド管理を行うための「SoundMNG」を作ります。スクリプトの新規作成を行い、以下のサンプルコードをコピペしてください!

■SoundMNG

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

public class SoundMNG : MonoBehaviour
{
    // Audio Source の設定
    [SerializeField]
    private AudioSource MainAudioSource;

    // BGM設定
    [SerializeField]
    private AudioClip[] Sounds;

    // フェードイン時間の設定(秒)
    [SerializeField]
    private float FadeInTime;

    // フェードアウト時間の設定(秒)
    [SerializeField]
    private float FadeOutTime;

    // 現在のボリューム計算用
    private float SoundTime;

    // MAXボリューム
    private float MaxVol;

    // 再生中の曲番号
    private int SoundNum;

    // BGM状態管理
    public enum BGM_STATE
    {
        WAIT,
        FADE_IN,
        NOW_PLAY,
        FADE_OUT,
        FADE_STOP,
        PAUSE,
        END,
    }
    private BGM_STATE _bgm_state;
    private BGM_STATE _state_work;

    //-------------------------------------------------//

    // 数値の初期化
    private void Awake()
    {
        MainAudioSource.volume = 0.0f;

        SoundTime = 0.0f;
        MaxVol = 1.0f;
        SoundNum = 0;

        _bgm_state = BGM_STATE.WAIT;
        _state_work = BGM_STATE.WAIT;
    }

    // 実行中の処理
    private void Update()
    {
        switch (_bgm_state)
        {
            // フェードイン処理
            case BGM_STATE.FADE_IN:
                if (FadeInTime <= 0.0f)
                {
                    MainAudioSource.volume = MaxVol;
                    _bgm_state = BGM_STATE.NOW_PLAY;
                }
                else if (FadeInTime >= Sounds[SoundNum].length)
                {
                    SoundTime += Time.deltaTime;
                    if (SoundTime >= Sounds[SoundNum].length * 0.9f)
                    {
                        SoundTime = Sounds[SoundNum].length * 0.9f;
                        _bgm_state = BGM_STATE.NOW_PLAY;
                    }
                    MainAudioSource.volume = SoundTime / (Sounds[SoundNum].length * 0.9f) * MaxVol;
                }
                else
                {
                    SoundTime += Time.deltaTime;
                    if(SoundTime >= FadeInTime)
                    {
                        SoundTime = FadeInTime;
                        _bgm_state = BGM_STATE.NOW_PLAY;
                    }
                    MainAudioSource.volume = SoundTime / FadeInTime * MaxVol;
                }
                break;

            // 通常再生中処理
            case BGM_STATE.NOW_PLAY:
                if (Sounds[SoundNum].length - MainAudioSource.time <= FadeOutTime)
                {
                    SoundTime = FadeOutTime;
                    _bgm_state = BGM_STATE.FADE_OUT;
                }
                break;

            // フェードアウト処理
            case BGM_STATE.FADE_OUT:
                if(FadeOutTime > 0.0f)
                {
                    SoundTime -= Time.deltaTime;
                    if (SoundTime <= 0.0f)
                    {
                        SoundTime = 0.0f;
                    }
                    MainAudioSource.volume = SoundTime / FadeOutTime * MaxVol;
                }

                if (!MainAudioSource.isPlaying)
                {
                    _bgm_state = BGM_STATE.END;
                }
                break;

            // フェードアウト停止処理
            case BGM_STATE.FADE_STOP:
                if (FadeOutTime > 0.0f)
                {
                    SoundTime -= Time.deltaTime;
                    if (SoundTime <= 0.0f)
                    {
                        SoundTime = 0.0f;
                        MainAudioSource.Stop();
                    }
                    MainAudioSource.volume = SoundTime / FadeOutTime * MaxVol;
                }
                else
                {
                    StopSoundNow();
                }

                if (!MainAudioSource.isPlaying)
                {
                    _bgm_state = BGM_STATE.END;
                }
                break;
        }
    }

    // 指定番号の再生(-1でランダム再生)
    public void StartSoundNum(int _num)
    {
        // 再生する曲番号の取得
        SoundNum = _num;
        
        // 最大曲数判定
        if (0 <= SoundNum && SoundNum < Sounds.Length)
        {
            MainAudioSource.clip = Sounds[SoundNum];
        }
        else
        {
            // 最大曲数を越える指定の場合 または -1 などが指定された場合はランダムで曲を選ぶ
            SoundNum = UnityEngine.Random.Range(0, Sounds.Length);
            MainAudioSource.clip = Sounds[SoundNum];
        }
        
        MainAudioSource.Stop();
        SoundTime = 0.0f;
        MainAudioSource.volume = 0.0f;
        _bgm_state = BGM_STATE.FADE_IN;
        MainAudioSource.Play();
    }

    // 途中再生(-1でランダム再生)
    public void StartSoundNum(int _num, float _time)
    {
        // 再生する曲番号の取得
        SoundNum = _num;

        // 最大曲数判定
        if (0 <= SoundNum && SoundNum < Sounds.Length)
        {
            MainAudioSource.clip = Sounds[SoundNum];
        }
        else
        {
            // 最大曲数を越える指定の場合 または -1 などが指定された場合はランダムで曲を選ぶ
            SoundNum = UnityEngine.Random.Range(0, Sounds.Length);
            MainAudioSource.clip = Sounds[SoundNum];
        }

        MainAudioSource.Stop();
        MainAudioSource.volume = MaxVol;

        if (_time < Sounds[SoundNum].length)
        {
            MainAudioSource.time = _time;
        }
        else
        {
            Debug.Log("時間指定が曲の最大時間を超えています");
            MainAudioSource.time = 0;
        }

        _bgm_state = BGM_STATE.NOW_PLAY;
        MainAudioSource.Play();
    }

    // 曲の一時停止
    public void SoundPause()
    {
        _state_work = _bgm_state;
        _bgm_state = BGM_STATE.PAUSE;
        MainAudioSource.Pause();
    }

    // 曲の再開
    public void SoundUnPause()
    {
        _bgm_state = _state_work;
        MainAudioSource.UnPause();
    }

    // 即停止
    public void StopSoundNow()
    {
        MainAudioSource.Stop();
        _bgm_state = BGM_STATE.END;
    }

    // フェード停止
    public bool StopSoundFadeOut()
    {
        bool _re = false;

        if (_bgm_state == BGM_STATE.FADE_IN ||
            _bgm_state == BGM_STATE.NOW_PLAY)
        {
            SoundTime = FadeOutTime;
            _bgm_state = BGM_STATE.FADE_STOP;
            _re = true;
        }

        return _re;
    }

    //------------- Get & Set -------------//

    // 現在のステータス取得
    public BGM_STATE GetNowBgmState()
    {
        return _bgm_state;
    }

    // 現在の再生BGM番号の取得
    public int GetSoundNum()
    {
        return SoundNum;
    }

    // 現在の再生時間取得
    public float GetAudioSourceTime()
    {
        return MainAudioSource.time;
    }

    // 現在の曲の最大再生時間の取得
    public float GetNowPlaySoundMaxTime()
    {
        return Sounds[SoundNum].length;
    }

    // フェード時間設定
    public void SetFadeTime(float _InTime,float _OutTime)
    {
        FadeInTime = _InTime;
        FadeOutTime = _OutTime;
    }

    // 最大音量変更
    public void SetMaxVol(float _vol)
    {
        // ボリュームの最大値超過チェック
        if(_vol >= 1.0f)
        {
            _vol = 1.0f;
        }

        // ボリュームの反映
        MaxVol = _vol;

        // 現在のボリュームが最大値以上だった場合変更する
        if(MainAudioSource.volume >= MaxVol)
        {
            MainAudioSource.volume = MaxVol;
        }

        // 通常再生状態でMaxVolが現在のボリューム値を上回ったら変更する
        if(_bgm_state == BGM_STATE.NOW_PLAY)
        {
            if(MainAudioSource.volume < MaxVol)
            {
                MainAudioSource.volume = MaxVol;
            }
        }
    }

}

次に作るのは動きの確認を行う「SoundTest」です。こちらも新規スクリプトの作成を行いコピペをしてください!

■SoundTest

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

public class SoundTest : MonoBehaviour
{
    [SerializeField]
    private int testNum = 0;

    [SerializeField]
    private SoundMNG _sound;

    // Update is called once per frame
    void Update()
    {
        // 動きのテスト
        switch (testNum)
        {
            // 通常再生と時間指定再生
            case 0:
                if (Input.GetKeyDown(KeyCode.Q))
                {
                    // 第一引数に曲の番号指定(設定した順番)
                    _sound.StartSoundNum(0);
                }

                if (Input.GetKeyDown(KeyCode.W))
                {
                    // 第二引数に秒単位で再生時間開始時間を設定可能
                    _sound.StartSoundNum(1,5.0f);
                }

                break;

            // 終了判定とランダム再生
            case 1:
                if (Input.GetKeyDown(KeyCode.E))
                {
                    // -1で全曲からランダム再生 時間指定が最大を超えると最初からスタート
                    _sound.StartSoundNum(-1,999.0f);
                }

                // 終了ステータスの取得と判定
                if (_sound.GetNowBgmState() == SoundMNG.BGM_STATE.END)
                {
                    _sound.StartSoundNum(-1);
                }
                break;

            //フェード時間の設定とボリューム変更
            case 2:
                if (Input.GetKeyDown(KeyCode.R))
                {
                    // 第一引数フェードインタイム 第二引数フェードアウトタイム
                    _sound.SetFadeTime(10.0f,10.0f);
                    // ボリューム 0.0f 〜 1.0f
                    _sound.SetMaxVol(0.5f);
                    _sound.StartSoundNum(-1);
                }
                if (Input.GetKeyDown(KeyCode.T))
                {
                    _sound.SetFadeTime(1.0f, 1.0f);
                    _sound.SetMaxVol(1.0f);
                    _sound.StartSoundNum(-1);
                }
                break;
        }

        // 一時停止と解除
        if(_sound.GetNowBgmState() != SoundMNG.BGM_STATE.PAUSE)
        {
            if (Input.GetKeyDown(KeyCode.Space))
            {
                //一時停止
                _sound.SoundPause();
            }
        }
        else
        {
            if (Input.GetKeyDown(KeyCode.Space))
            {
                // 一時停止解除
                _sound.SoundUnPause();
            }
        }

        // 停止処理
        if (Input.GetKeyDown(KeyCode.Z))
        {
            _sound.StopSoundNow();
        }

        // フェードアウト停止処理(フェードアウト時間が0の場合は通常停止になる)
        if (Input.GetKeyDown(KeyCode.X))
        {
            _sound.StopSoundFadeOut();
        }

        // テスト番号の切り替え(エンターキー)
        if (Input.GetKeyDown(KeyCode.Return))
        {
            testNum++;
            if(testNum >= 3)
            {
                testNum = 0;
            }
            Debug.Log("現在のテスト番号   :" + testNum);
        }

        // ステータスの取得
        if (Input.GetKeyDown(KeyCode.S))
        {
            Debug.Log("現在のステータス   :" + _sound.GetNowBgmState());
            Debug.Log("再生中の曲番号    :" + _sound.GetSoundNum());
            Debug.Log("現在の再生時間    :" + _sound.GetAudioSourceTime());
            Debug.Log("現在の曲の最大再生時間:" + _sound.GetNowPlaySoundMaxTime());
        }
    }
}

サンプルを動かしてみる

コピーが完了したら、スクリプトをそれぞれ空のオブジェクトにアタッチしてインスペクターの設定を行います。以下のように設定してください。

■SoundMNG

SoundMNGの設定

・Main Audio Source
「サンプルプロジェクトの作成」で作ったAudio Sourceを設定します。

・Sounds
Size:曲数に合わせてサイズを変更します。今回は2曲で試すので2を設定しています。
Element:Aoudio Clip(Soundファイル)を設定します。

・Fade In Time / Fade Out Time
それぞれフェードインの時間とフェードアウトの時間を設定します。必要のない場合は0を設定しておきます。

■SoundTest

SoundTestの設定

・Test Num
内部的な管理番号なので0のままでOKです。

・Sound
SoundMNGを設定した空のゲームオブジェクトを設定して、SoundMNGのインスタンスを取得します。これによりSoundTest内部からSoundMNGの各種関数にアクセスします。

あとは実行をして動きを確認してみましょう! SoundTestに各アクションを設定しています。

【TestNumによる動作の違い】

TestNum:0

キーボードQキー:曲0番目の再生
キーボードWキー:曲1番目を5秒の位置から再生

TestNum:1

キーボードEキー:ランダム再生(時間超過エラー設定)
TestNum1の設定では曲が終了するとすぐにランダムで別の曲が流れます。

TestNum:2

キーボードRキー:
フェードイン・フェードアウト時間を10秒に変更
ボリュームを0.5に変更
ランダムに曲を再生

キーボードTキー:
フェードイン・フェードアウト時間を1秒に変更
ボリュームを1.0に変更
ランダムに曲を再生

【共通操作】

キーボードスペースキー:曲の一時停止と解除

キーボードZキー:曲の即停止

キーボードXキー:曲のフェードアウト停止(フェードアウトタイムを設定時のみ)

キーボードエンターキー:TestNumを増やす
3以上になると0に戻ります

キーボードSキー:現在のステータスをデバッグログに表示

関数解説

必ずSoundMNGをオブジェクトにアタッチしたあと、曲再生を行うスクリプトに以下の宣言を行いSoundMNGのオブジェクトを設定してから使ってください!

[SerializeField]
private SoundMNG _sound; //名前はお好きに!

任意の曲の指定再生

_sound.StartSound(int _num);

_num番目の曲(0〜Max)を再生します。-1や存在しない番号が指定されると現在ある曲からランダムに曲が再生されます。また、曲の再生と終了はFadeInTimeに指定した時間とFadeOutTimeに指定した時間でフェード処理を行います。
※フェード処理が不必要な場合はFadeInTime/FadeOutTimeに0を指定します。

曲の再生位置の指定

_sound.StartSound(int _num, float _time);

_timeに指定した時間(秒)の位置から曲を再生します。_timeが曲の時間をオーバーしていた場合は最初からの再生になります。
※位置指定再生はフェードイン処理には対応していません。フェードアウト処理のみが有効です。

曲の一時停止処理と解除

_sound.SoundPause();
曲を一時停止します。

_sound_SoundUnPause();
一時停止を解除して元の状態に戻ります。

曲の停止

_sound.StopSoundNow();
流れている曲をすぐに停止します。

_sound.StopSoundFadeOut();
流れている曲をフェードアウトで停止します。普通に曲が終わる時のフェードアウト中はこの処理は有効にならないようになっています。戻り値がフェードアウト処理に移行した場合は「true」になるので、判定を行う場合はお使いください。

その他ステータス関連

_sound.GetNowBgmState();
現在のAudioSourceのステータスを取得します。「SoundMNG.BGM_STATE.ステータス」と比較して使います。

_sound.GetSoundNum();
現在再生中の曲の番号を取得します。※曲の再生前に取得すると、前回再生した数値が残っている場合があるのでご注意ください。

_sound.GetAudioSourceTime();
現在再生中の曲が何秒まで再生されたかを確認できます。

_sound.GetNowPlaySoundMaxTime();
現在再生中の曲が最大何秒まで続くかを確認できます。

_sound.SetFadeTime(float _InTime, float _OutTime);
フェードインの時間とフェードアウトの時間をスクリプトから変更します。

_sound.SetMaxVol(float _vol);
最大ボリュームをスクリプトから変更します。

まとめ

曲の停止判定はSoundTestで書いている以下の部分により判定が可能です。これにより曲が終了したら別シーンに移動などの処理も可能になっています。

if (_sound.GetNowBgmState() == SoundMNG.BGM_STATE.END)
{
//処理の実行
}

今回は一時停止中との判定を分けるために終了ステータスを設定して判断しています。サンプルを作るまで少し時間は必要ですが、ぜひ一度お試しください!