ゲーム制作   Unity ソフトウエア 

Unity ソフトウエアでゲーム制作#1モグラたたき編(29.ゲームにBGMをつける)

キャッチ画像モグラたたきBGMをつける

本投稿は2024年9月時点の内容になります。アップデートにより変更となる場合があります。また環境によって違いがあると思いますのであくまで参考として、ご了承ください。

様々な書籍、ブログや動画を参考にさせていただきました。多すぎて一つ一つはお紹介できませんが感謝です。

初心者の自分がUnity ソフトウエアでゲームを作ってみました。とりあえずシンプルなものということでモグラたたきに挑戦です。ゲーム作ってみるかという感じになったときに、いいタイミングで某ゲームのイベントシナリオ内ミニゲームにモグラたたきが実装されていたのでUIとかエフェクトとか、諸々の仕様をぱくって参考にして作ってみましたよ。様々なHowToの中の選択肢のひとつとして、同じ初心者さんの参考になればよいです。

\ チェック /

今回はゲームにBGMをつけていきます。

今回はゲームにBGMをつけていきたいと思います。

前回の「効果音をつける」を踏まえて、BGMをつけていきます。

\ ゲームに効果音をつけるをチェック /

本記事のポイント

  • BGMをつけることができる
  • シングルトンパターンをつかって、シーンを跨いでも途切れることなくBGMを流す方法

AudioClip 音素材の準備

今回はユニティちゃん公式の「キャラクターソング・アルバム Vol.1『UNITE IN THE SKY』」を使わせていただきました。

ScriptableObjectでAudioClipを管理する

今回も効率よくBGMを使えるようにScriptableObjectを使ってデータベース化していきます。

ScriptableObjectの使い方はこちらも参考にしてみて下さい。

以下スクリプトです。

class BGMSettingDB
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

[CreateAssetMenu]
public class BGMSettingDB : ScriptableObject
{
    public List<BGMSettingData>BGMSettingList=new List<BGMSettingData>();

    public BGMSettingData FindBGMSettingDataBYName(string name)
    {
        BGMSettingData bgmSettingData = BGMSettingList.Find(BGMSettingData => BGMSettingData.Name == name);
        if(bgmSettingData == null)
        {
            Debug.Log("指定された名前がステータスリストに見当たりません");
        }
        return bgmSettingData;
    }
}

[System.Serializable]
public class BGMSettingData
{
    public string Name;
    public AudioClip BGM;
    public float StartTime;
    public float EndTime;
    [Range(0f,1f)]
    public float Volume;
    public bool IsLoop;
}

スクリプトざっくり解説

  • (6-20)ScriptableObjectのデータベース化の処理
  • (6)「作成」メニューに表示
  • (11-19)データベースに登録したオーディオクリップを含むBGMSettingDataを名前で取得するメソッド
  • (25-31)BGMSettingDataの項目
  • (27,28,31)今回はBGMなのである開始時間と終了時間でループできる仕様にします。そのパラメータです。

オーディオクリップデータベースの作成

  1. プロジェクト上で右クリック>作成>「BGMSettingDB」作成をクリック 任意の名前で作成します。
  2. 今回はひとまずメイン用、ポーズ時用、ゲームオーバー時用のBGMを用意しました。

カウントダウンで音を出したいので、例としてCountDownオブジェクトに付けてみます。

BGMManagerを作成

BGMをあつかうBGMManagerを準備します。

BGMManageの作成

  1. ヒエラルキー上に任意の名前で空のオブジェクトを作成します。今回は「BGMManager」としました。
  2. BGMManagerにAudioSourceコンポーネントを追加します。
  3. スクリプト「BGMManager」を作成してアタッチします。

スクリプトからBGMを再生する

以下スクリプトです。

class BGMManager
using System.Collections;
using System.Collections.Generic;
using UnityEditor.AssetImporters;
using UnityEngine;

public class BGMManager : MonoBehaviour
{
    [SerializeField] AudioSource audioSource;
    [SerializeField] BGMSettingDB bgmSettingDB;
    //現在のBGMの名前
    string currentBGMName;
    //BGMSettingDataの設定を取得するためのフィールド
    float startTime;
    float endTime;
    bool isLoop;
   //ひとつ前のBGMの名前と一時停止した時間
    string previousBGMName;
    float previousBGMPauseTime;
    //再生フラグ
    bool isPlay;
    
    //初期化 再生フラグをfalse   
    public void InitBGMManager()
    {
        isPlay = false;
        GameManager.Instance.SetBGMManager(this);
    }
    //BGMのセット
    public void SetBGM(string name)
    {
        //BGMSettingDataの取得
        BGMSettingData settingData = bgmSettingDB.FindBGMSettingDataBYName(name);
        float startTime = settingData.StartTime;
        float endTime = settingData.EndTime;
        string newBGMName=settingData.Name;
        AudioClip newBGM=settingData.BGM;
        
        //再生していたオーディオクリップがあった場合は名前を覚えておく
        if (audioSource.clip != null) 
        {
            previousBGMName = currentBGMName;
        }
        //現在のBGMの名前を新たにセットしたBGMの名前に変更  
        currentBGMName = newBGMName;
        
        //BGMSettingDataの開始時間を取得
        if (0 <= startTime && startTime <= newBGM.length)
        {
            this.startTime = startTime;
        }
        else
        {
            this.startTime=0;
            Debug.Log("開始時間は0に設定されました。");
        }

        //BGMSettingDataの終了時間を取得
        if (0 <= endTime && endTime <= newBGM.length)
        {
            this.endTime = endTime;

        }
        else
        {
            this.endTime = newBGM.length;
            Debug.Log("終了時間はクリップの最終時間に設定されました。");
        }
        
        //BGMSettingDataのループ設定を取得
        this.isLoop = settingData.IsLoop;
        //BGMSettingDataボリュームを取得
        audioSource.volume = settingData.Volume;
        //AudioSourceコンポーネントにクリップをセット
        audioSource.clip=newBGM;
        //終了時間より開始時間が大きいときは
        //先頭に戻れるようにAudioSourceコンポーネントのループをOnに
        //それ以外の時はOffにする
        if (endTime < startTime) 
        { 
            audioSource.loop=true;
        }
        else
        {
            audioSource.loop=false;
        }
    }
    
    //BGM再生のメソッド
    public void PlayBGM(string name)
    {
        SetBGM(name);
        audioSource.time = startTime;
        audioSource.Play();
        isPlay = true;
    }
    
    //一時停止のメソッド
    public void PauseBGM()
    {
        previousBGMPauseTime = audioSource.time;
        StopBGM();
        PlayBGM("pause");
    }
    //再開のメソッド(一時停止前のBGMを再生)
    public void UnPauseBGM()
    {
        StopBGM();
        PlayBGM(previousBGMName);
        audioSource.time=previousBGMPauseTime;
    }
    //停止のメソッド
    public void StopBGM() 
    { 
        audioSource.Stop();
        isPlay=false;
    }
    //再生の処理更新(ループ設定のチェック)
    public void UpdateAudioPlayTime()
    {
        if (isPlay) 
        {
            CheckAudioPlayTime();
        }
    }

    //終了時間を迎えた際のループの具体的な処理   
    public void CheckAudioPlayTime()
    {
        //再生時間が終了時間を過ぎたら再生の更新
        if ((startTime<=endTime && endTime <= audioSource.time)
            || (endTime <= audioSource.time && audioSource.time < startTime))
        {
            switch (isLoop)
            {
                //ループ設定がtrueなら開始時間に戻って再生
                case true:
                    audioSource.time = startTime;
                break;
                //ループ設定がfalseなら停止
                case false:
                    audioSource.Stop();
                break;

            }
        }
    }

    // Update is called once per frame
    void Update()
    {
        UpdateAudioPlayTime();

    }

    private void OnEnable()
    {
        InitBGMManager();
    }
}

スクリプトざっくり解説

  • (11)現在のBGMの名前
  • (13-15)BGMSettingDataの設定を取得するためのフィールド
  • (17,18)ひとつ前のBGMの名前と一時停止した時間
  • (20)再生フラグ
  • (26)GameManagerに自身を渡す
  • (29-86)BGMの設定
  • (32-36)BGMSettingDataの取得
  • (39-42)再生していたオーディオクリップがあった場合は名前を覚えておく
  • (43)現在のBGMの名前を新たにセットしたBGMの名前に変更
  • (47-55)BGMSettingDataの開始時間を取得
  • (57-67)BGMSettingDataの終了時間を取得
  • (70,72)BGMSettingDataのループ設定とボリュームを取得
  • (74)AudioSourceコンポーネントにクリップをセット
  • (78-85)設定で終了時間より開始時間の方が大きいときは、先頭に戻れるようにAudioSourceコンポーネントのループをOnに、それ以外の時はOffにする
  • (88-95)BGM再生のメソッド
  • (99-103)一時停止のメソッド
  • (105-110)再開のメソッド(一時停止前のBGMを再生)
  • (112-116)停止のメソッド
  • (118-124)再生の処理更新(ループ設定のチェック)
  • (127-146)終了時間を迎えた際のループの具体的な処理
  • (130-145)再生時間が終了時間を過ぎたら再生の更新

いろいろ盛り込んだので少し長いですね。見づらくてすみません。

つづいてGameManagerを変更します。

class GameManager
public class GameManager : Singleton<GameManager> 

{
    public GameState CurrentGameState {  get; set; }
    public float PlayTime {  get; private set; }
    BGMManager BGMManager;
    EnemyManager enemyManager;
    EffectManager effectManager;

    //省略
    
    public void SetBGMManager(BGMManager BGMManager)
    {
        this.BGMManager = BGMManager;
    }

    //省略

    private void Gamestart()
    {
        PlayTime = 0;
        BGMManager.PlayBGM("main");
    }

    //省略
    
    private void Pause()
    {
        PauseUIController.OpenUI();
        BGMManager.PauseBGM();
    }

    IEnumerator Resume()
    {
        PauseUIController.CloseUI();
        BGMManager.UnPauseBGM();
        yield return new WaitForSecondsRealtime(0.8f);
        UpdateCurrentState(GameState.active);
    }

    private void Retry()
    {   
        BGMManager.StopBGM();
        SceneManager.LoadScene(SceneManager.GetActiveScene().name);
        UpdateCurrentState(GameState.gamestart);
    }

    private void GameOver()
    {
        enemyManager.RemoveAllEnemies();
        effectManager.RemoveAllEffects();
        GameOverUIController.OpenUI();
        BGMManager.PlayBGM("gameover");
    }
    //省略
}

スクリプトざっくり解説

  • BGMManagerのメソッドを使って、各状況に応じてBGMを再生、一時停止、停止します。

ゲーム再生するとBGMが流れたと思います。しかしリトライでLoadSceneするとBGMが流れなくなってしまいました。

どうやらDontDestroyOnLoadすればいいらしいので、以前やったシングルトンパターンを使ってみます。

今回の問題点

  • LoadSceneするとBGMが流れなくなってしまいました

解決方法

  • BGMManagerをDontDestroyOnLoadすればいいのでシングルトンパターンを使う
  • よりシンプルな場合はAudioSourceコンポーネントの「ゲーム開始時に再生」をオンにしてもいい

BGMManagerにジェネリックシングルトンを適用する

BGMManagerをシングルトンパターンにするのは簡単です。ゲームマネージャーのときに作ったジェネリックシングルトンを適用していきます。シングルトンパターンについてはこちらをどうぞ。

\ シングルトンパターンの解説の実装例 /

以下スクリプトの変更点です。

class BGMManager
public class BGMManager : Singleton<BGMManager>
{
    //省略
    public void InitBGMManager()
    {
        isPlay = false;
    }
    //省略
}

スクリプトざっくり解説

  • MonoBehaviourクラスの代わりにジェネリックシングルトンSingletonを継承して、不必要になったGameManager.Instance.SetBGMManager(this)を削除。
  • ※ジェネリックシングルトンクラスを実装している場合に限ります。内容はシングルトンパターンで確認してください。

つづいてGameManagerです。

class GameManager
public class GameManager : Singleton<GameManager> 

{
    public GameState CurrentGameState {  get; set; }
    public float PlayTime {  get; private set; }
    
    //省略

    private void Gamestart()
    {
        PlayTime = 0;
        BGMManager.Instance.PlayBGM("main");
    }

    //省略
    
    private void Pause()
    {
        PauseUIController.OpenUI();
        BGMManager.Instance.PauseBGM();
    }

    IEnumerator Resume()
    {
        PauseUIController.CloseUI();
        BGMManager.Instance.UnPauseBGM();
        yield return new WaitForSecondsRealtime(0.8f);
        UpdateCurrentState(GameState.active);
    }

    private void Retry()
    {   
        BGMManager.Instance.StopBGM();
        SceneManager.LoadScene(SceneManager.GetActiveScene().name);
        UpdateCurrentState(GameState.gamestart);
    }

    private void GameOver()
    {
        enemyManager.RemoveAllEnemies();
        effectManager.RemoveAllEffects();
        GameOverUIController.OpenUI();
        BGMManager.Instance.PlayBGM("gameover");
    }

    //省略
}

スクリプトざっくり解説

  • BGMManagerをシングルトンにしたので、メソッドはBGMManager.Instance.で呼び出します。

ゲーム再生してみます。今度はリトライ後も問題ないと思います。

まとめ

まとめ

  • BGMをスクリプトから鳴らすときはAudioSource.Playを使うといい
  • AudioSource.timeを使うと区間を決めた再生や、そのループ再生などができる
  • ScriptableObjectを使ってAudioClipを管理すると便利
  • シーンまたぎ(LoadScene)でBGMが途切れてしまう場合はシングルトンパターンを使ってDontDestroyOnLoadすればいい
ユニティちゃん公式ホームページへ
ユニティちゃん公式ホームページ
ユニティちゃんライセンス
ユニティちゃんの画像、素材、ライセンスロゴはユニティちゃんライセンス条項を元に使用しています

\ Unityのスクリプトを書くのに役立ちます /

もっと早く教えてほしかった!Unity C#入門

MARU

マケイヌ的おすすめ度

わかりやすい度

目指せ脱初心者

〇おすすめポイント

ボリューム大でC#学習にもよく使う関数のチェックにもOK

×よくないポイント

始めたばかりの人にはちょっと難しい

\ 初学に使った書籍です /

動画×解説でかんたん理解! Unityゲームプログラミング超入門

大角 茂之/大角 美緒

マケイヌ的おすすめ度

わかりやすい度

提供素材がよくてテンション上がる度

〇おすすめポイント

素材が良くてモチベがあがります。

×よくないポイント

動画解説前提なので図版が小さいのが玉に瑕

おすすめ記事

 

プロフィール

マケイヌ

人生のメインストリームから外れた40代の♂。

90年代オルタナにはまり、文字通りメインストリームから逸脱。 その後もたびたび人生から逃亡。

心が動いた作品の紹介や 自分のちいさな経験、HowToを発信できればと日々模索中。

1年後までにイラストと写真のポートフォリオをつくりたい。

記:2019年12月

▼プロフィールはこちら

Follow me

アーカイヴ