ゲーム化!tomo_manaのブログ

ゲーム化!tomo-manaのブログ

Unityでゲームを作る方法について紹介しています

Unity学習#35 BGMの再生 (Unity 2020.3.26f1)

f:id:tomo_mana:20220215202021p:plain

Unityで音楽を再生したり止めたりする設定。

概念

f:id:tomo_mana:20220215203432p:plain
AudioSource、AudioClip、AudioListener

音楽再生機器に、距離の概念を持たせたもの。

●AudioSource : 音源。音楽の再生機器のイメージ。

●AudioClip : 日本語では録音された音声の一部。音楽やサウンド。CDなどのイメージ。

●AudioListener:音声を聞く人。デフォルトではMain Cameraに設置されている。


AudioListenerの使い方は、ここでは触れません。(BGMを流す程度であれば、AudioListenerMain Camera に固定でよさそうです。)

一般的な説明として、音楽の再生にはAudioSourceAudioClipとがあります。FPSなどに対応するためか、複数のAudioSourceAudioListenerを使用することで、かなり凝ったことができそうですが、ここでは基本的な操作だけを取り上げます。

主な使い方は、AudioClip(曲やサウンド)をAudioSourceにセットして、AudioSourcePlay()/Stop()します。

操作

初期化

音源(AudioSource)は、再生可能な一つのオーディオクリップ(AudioClip)を持ちます。オーディオクリップを切り替えるには、audioSource.clip にアクセスします。

[SerializeField]
AudioSource audioSource;
[SerializeField]
AudioClip audioClip;

// 音源に曲やサウンドをセットする
audioSource.clip = audioClip;

再生

AudioSourcePlay() を使用します。

// 再生処理
audioSource.Play();

停止

AudioSourceStop() を使用します。再生中かどうかは .isPlaying で確認できますが、一時停止でも false になるため、一時停止を使う場合は、独自の判別フラグを用意する必要があります。

if( audioSource.isPlaying ){
    audioSource.Stop();
}

一時停止/再開

AudioSourcePause()UnPause() を使用します。先述のようにAudioSourceには一時停止状態かどうかを知る方法がありません。そのため独自の判別フラグを用意する必要があります。

bool paused = false;

// 一時停止
if( audioSource.isPlaying ){
    if( !paused ){
        audioSource.Pause();
        paused = true;
    }
}

// 再開
if( paused ){
    audioSource.UnPause();
    paused = false;
}

ミュート

AudioSource.mute を使用します。trueでミュートします(Volumeが0になる)。

// ミュート
audioSource.mute = true;

// ミュート解除
audioSource.mute = false;

フェードイン/アウト

フェードイン・アウトは、少し実装が必要です。以下に実装例を示します。

enum AUDIO_STATE {
    WAITING,
    PLAYING,
    FADING_IN,
    FADING_OUT,
}
AUDIO_STATE state;

// ボリューム設定値
float audioVolume = -1;
// フェードアウト(3秒)
float timeRequested = 3.0f;
// 残時間
private float timeRemain;

void Fadeout()
{
    if( state == AUDIO_STATE.FADING_OUT )
    {
        if( !audioSource.mute ){
            audioSource.volume = (float)(audioVolume * (timeRemain / timeRequested));
//            audioSource.volume = (float)(audioVolume * (1.0f - timeRemain / timeRequested));  // フェードインの場合
        }
    }
}
bool Playing()
{
    if( state != AUDIO_STATE.WAITING ){
        return true;
    }
    return false;
}
void Update()
{
    if( Playing() ){
        if (timeRemain > 0){
            timeRemain -= Time.deltaTime;
        }
        if (timeRemain <= 0){
            timeRemain = 0;
            Timeout();
        }
    }
    Fadeout();
}

コード例

あまり凝ったことをしないのであれば、以下のようなコンポーネントを作ることで、複数のBGMを一つのコンポーネントにまとめることができます。

※このコンポーネントを使用する場合、AudioSourceを同じゲームオブジェクトに取り付けてください(コンポーネントの中でAudioSourceを自動取得します)。

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

public enum BGM_ID {
    BGM_ID_001,
    BGM_ID_002,
    BGM_MAX,  // このIDは再生時に指定しない
}

public class AudioController : MonoBehaviour
{
    // BGM_IDの個数と一致させること
    [Header("BGM設定")]
    [SerializeField]
    List<AudioClip> audioClip;
    
    AudioSource audioSource;
    
    enum AUDIO_STATE {
        WAITING,
        PLAYING,
        FADING_OUT,
    }
    AUDIO_STATE state = AUDIO_STATE.WAITING;
    
    // フェードアウト
    float TIME_FADING_OUT = 3.0f;
    
    // ボリューム
    float audioVolume = -1;
    
    // 停止
    public void Stop()
    {
        if( !ReferenceEquals( audioSource.clip, null) ){
            if( audioSource.isPlaying ){
                audioSource.Stop();
            }
        }
        state = AUDIO_STATE.WAITING;
        
        if( audioVolume != -1 ){
            audioSource.volume = audioVolume;
        }
        audioVolume = -1;
    }
    
    // 再生
    public void Play( BGM_ID id, float playTime )
    {
        Stop();
        audioSource.clip = audioClip[(int)id];
        audioSource.Play();
        
        timeRemain = 
        timeRequested = playTime;
        
        state = AUDIO_STATE.PLAYING;
    }
    
    bool paused = false;
    
    // 一時停止
    public void Pause()
    {
        if( audioSource.isPlaying ){
            if( paused == false ){
                audioSource.Pause();
                paused = true;
            }
        }
    }
    
    // 再開
    public void UnPause()
    {
        if( paused == true ){
            audioSource.UnPause();
            paused = false;
        }
    }
    
    // ミュート
    public void SetMute( bool onoff )
    {
        audioSource.mute = onoff;
    }
    
    private float timeRequested;
    private float timeRemain;
    
    void Timeout()
    {
        if( state == AUDIO_STATE.PLAYING ){
            state = AUDIO_STATE.FADING_OUT;
            
            if( !audioSource.mute ){
                audioVolume = audioSource.volume;
                
                timeRemain = 
                timeRequested = TIME_FADING_OUT;
            } else {
                state = AUDIO_STATE.WAITING;
            }
            
        } else
        if( state == AUDIO_STATE.FADING_OUT ){
        
            state = AUDIO_STATE.WAITING;
            
            StopBGM();
        }
    }
    
    void Fadeout()
    {
        if( state == AUDIO_STATE.FADING_OUT )
        {
            if( !audioSource.mute ){
                audioSource.volume = (float)(audioVolume * (timeRemain / timeRequested));
            }
        }
    }
    
    // Start is called before the first frame update
    void Start()
    {
        // 音源
        audioSource = gameObject.GetComponent<AudioSource>();
    }

    bool Playing()
    {
        if( state != AUDIO_STATE.WAITING ){
            return true;
        }
        return false;
    }

    // Update is called once per frame
    void Update()
    {
        if( Playing() ){
            if (timeRemain > 0){
                timeRemain -= Time.deltaTime;
            }
            if (timeRemain <= 0){
                timeRemain = 0;
                Timeout();
            }
        }
        Fadeout();
    }
}

呼び出し関数をstaticにできれば、より便利かもしれません。

参考

以下のサイトを参考にさせていただきました。
www.sejuku.net
qiita.com

(以上)