Open1

Unity ScriptableObject使用で、mp3イントロ付きループ再生してみました

Cz_mirrorCz_mirror

Unityでイントロ付きmp3をループ再生してみました。色々調べてみましたが標準ではできないため、ScriptableObjectでSoundclipとイントロスキップ箇所を定義した音源用ScriptableObjectを作り、その音源ScriptableObjectを使ってイントロ付きループを実現してみました。
以下のようなファイルで構成しております。

  • 音源用ScriptableObject (音源用AudioClipとスキップしたい時間帯を定義)
  • BGMループ再生クラス(シーン別BGM制御クラスから受け取った音源用ScriptableObjectを再生する)
  • BGMループ制御用インターフェース(シーン別BGM制御クラスで使用、BGMループ再生クラスをオブジェクトで定義する)
  • シーン別BGM制御クラス(シーンから呼び出したい音源用ScriptableObjectを指定して、BGMループ再生クラスに受け渡しをする)

まずは音源用ScriptableObjectを作成します。

using UnityEngine;

namespace BGM
{
    /// <summary>
    /// 音源ループScriptableObject
    /// </summary>
    [CreateAssetMenu(fileName = "LoopSound", menuName = "ScriptableObjects/LoopSound", order = 1)]
    public class LoopSound :ScriptableObject
    {
        /// <summary>
        /// 音源ファイル
        /// </summary>
        public AudioClip clip;

        /// <summary>
        /// イントロ部分のスキップ時間帯(秒)
        /// </summary>
        public float skipTime;
    }
}

作成した音源用ScriptableObjectにAudioClipとイントロスキップしたい時間帯を定義します。

次にBGMループ再生クラスを作成します。こちらは実際に音源用ScriptableObjectを受け取り再生するためのClassになります。

using UnityEngine;

namespace BGM
{
    /// <summary>
    /// BGMループ再生クラス
    /// </summary>
    public class LoopSoundPlayer : MonoBehaviour
    {
        [SerializeField] private AudioSource audioSource;

        /// <summary>
        /// 再生中の音源
        /// </summary>
        private LoopSound loopSound;

        /// <summary>
        /// ループ開始地点のサンプル数
        /// </summary>
        private int loopStartSamples;

        /// <summary>
        /// ループ終了地点のサンプル数
        /// </summary>
        private int loopEndSamples;

        /// <summary>
        /// ループ時間に含まれるサンプル数
        /// </summary>
        private int loopLengthSamples;

        private void Update()
        {
            // ループ終了地点に達したら、ループ開始地点に戻す
            if (audioSource.timeSamples >= loopEndSamples)
            {
                audioSource.timeSamples -= loopLengthSamples;
                audioSource.Play();
            }
        }

        // 外部から音源ScriptableObjectを切り替えるメソッド
        public void ChangeLoopSound(LoopSound newLoopSound)
        {
            // 新しい音源ScriptableObjectを代入する
            loopSound = newLoopSound;

            // AudioSourceにAudioClipを設定する
            audioSource.clip = loopSound.clip;

            // AudioClipとskipTimeからサンプル数を計算する
            loopStartSamples = (int)(loopSound.clip.frequency * loopSound.skipTime);
            loopEndSamples = loopSound.clip.samples;
            loopLengthSamples = loopEndSamples - loopStartSamples;

            // AudioSourceを再生する
            audioSource.Play();
        }
    }
}

次にBGMループ制御用インターフェースを実装します。こちらはシーン別BGM制御クラスをプロパティにもたせます。

namespace BGM
{
    /// <summary>
    /// BGMループ制御用インターフェース
    /// </summary>
    public interface ILoopSoundPlayer
    {
        LoopSoundPlayer LoopSoundPlayer { get; set; }
    }
}

最後にBGMループ制御用インターフェースを実装したシーン別BGM制御クラスを実装します。
こちらはゲーム毎に異なるクラスをそれぞれ定義する想定です。今回はタイトルのみのパターンで実装をしています。こちらはゲームの場面毎に呼び出したいループ音源を定義してカスタマイズすることで、他のシーンなどでも活用できる様になります。

using UnityEngine;

namespace BGM
{
    /// <summary>
    /// タイトル画面BGM制御クラス
    /// </summary>
    public class TitleBGMSwitcher : MonoBehaviour, ILoopSoundPlayer
    {
        /// <summary>
        /// タイトルBGM
        /// </summary>
        [SerializeField] LoopSound titleBGM;

        [SerializeField] private LoopSoundPlayer _loopSoundPlayer;

        public LoopSoundPlayer LoopSoundPlayer
        {
            get => _loopSoundPlayer;
            set => _loopSoundPlayer = value;
        }

        private void Start()
        {
            LoopSoundPlayer.ChangeLoopSound(titleBGM);
        }
    }
}

実装後任意の要素にAudio Sourceコンポーネントと共にBGMループ再生クラスとシーン別BGM制御クラスを付与して、音源用ScriptableObject、BGMループ再生クラス、Audio Sourceコンポーネントをそれぞれアタッチすればループ付き音源を再生できる様になります。