Циклічна фонова музика зі вступом

Сторінка оновлюється :
Дата створення сторінки :

Середовище перевірки

Вікна
  • вікна 11
Редактор єдності
  • 2021.3.3f1
Пакет системи введення
  • 1.3.0

Передумови для цієї поради

Наступні настройки були зроблені заздалегідь як передумова для опису цієї поради.

Про матеріал, що входить до складу зразка

BGM запозичена з наступного сайту.

Про аудіофайл циклу зі вступом

На цей раз тільки стандартна функція Unity буде відтворювати цикл зі вступом, але це не підтримується як стандартна функція. По-перше, специфікації циклу зі вступом як аудіофайлом не виправлені, тому метод створення відрізняється в залежності від ігрових рамок.

Цього разу ми підготуємо два аудіофайли для частини «вступ» та частини «цикл», один раз зіграємо вступну частину та повторимо циклічну частину. Тому, будь ласка, підготуйте вищезазначені два файли як аудіофайли.

Деякі дистрибутивні сайти враховують це і заздалегідь поширюють аудіофайли окремо. Якщо ні, вам потрібно зробити це самостійно за допомогою інструменту редагування голосу.

Якщо ви хочете розділити файл надвоє, ми рекомендуємо наступний формат аудіофайлу.

  • OggVorbis (.ogg)
  • WAV (.wav)

Для отримання додаткової інформації дивіться офіційну документацію Unity.

Про відтворення циклічної фонової музики зі вступом

Створіть інтерфейс користувача, який дозволяє відтворювати, призупиняти та зупиняти фонову музику як зразок. Він має той самий макет, що й звичайний зразок відтворення фонової музики.

Додайте два аудіофайли до свого проекту, розділіть на дві частини, вступ і цикл.

Цього разу ми створимо власну програму для відтворення циклічної BGM зі вступом. Створіть сценарій і залиште його ім'я IntroLoopAudio .

Сценарій виглядає так: Я маю на увазі код на наступному сайті, але я додав багато коду, оскільки він погано працював з WebGL, і я хотів трохи більше контролю.

[Довідка] 【Unity】Реалізуйте вступ + цикл відтворення - 7080 + 1

using UnityEngine;

/// <summary>
/// イントロ付きループ BGM を制御するクラスです。
/// </summary>
/// <remarks>
/// WebGL では PlayScheduled で再生するとループしないのでその対応を入れている。
/// WebGL では2つの AudioSource を交互に再生。
/// </remarks>
public class IntroLoopAudio : MonoBehaviour
{
  /// <summary>BGM のイントロ部分の音声データ。</summary>
  [SerializeField] private AudioClip AudioClipIntro;

  /// <summary>BGM のループ部分の音声データ。</summary>
  [SerializeField] private AudioClip AudioClipLoop;

  /// <summary>BGM のイントロ部分の AudioSource。</summary>
  private AudioSource _introAudioSource;

  /// <summary>BGM のループ部分の AudioSource。</summary>
  private AudioSource[] _loopAudioSources = new AudioSource[2];

  /// <summary>一時停止中かどうか。</summary>
  private bool _isPause;

  /// <summary>現在の再生するループ部分のインデックス。</summary>
  private int _nowPlayIndex = 0;

  /// <summary>ループ部分に使用する AudioSource の数。</summary>
  private int _loopSourceCount = 0;

  /// <summary>再生中であるかどうか。一時停止、非アクティブの場合は false を返す。</summary>
  private bool IsPlaying
    => (_introAudioSource.isPlaying || _introAudioSource.time > 0)
      || (_loopAudioSources[0].isPlaying || _loopAudioSources[0].time > 0)
      || (_loopAudioSources[1] != null && (_loopAudioSources[1].isPlaying || _loopAudioSources[1].time > 0));

  /// <summary>現在アクティブで再生しているループ側の AudioSource。</summary>
  private AudioSource LoopAudioSourceActive
    => _loopAudioSources[1] != null && _loopAudioSources[1].time > 0 ? _loopAudioSources[1] : _loopAudioSources[0];

  /// <summary>現在の再生時間 (s)。</summary>
  public float time
    => _introAudioSource == null ? 0
      : _introAudioSource.time > 0 ? _introAudioSource.time
      : LoopAudioSourceActive.time > 0 ? AudioClipIntro.length + LoopAudioSourceActive.time
      : 0;


  void Start()
  {
    _loopSourceCount = 2;   // WebGL でなければ 1 でもよい

    // AudioSource を自身に追加
    _introAudioSource = gameObject.AddComponent<AudioSource>();
    _loopAudioSources[0] = gameObject.AddComponent<AudioSource>();
    if (_loopSourceCount >= 2)
    {
      _loopAudioSources[1] = gameObject.AddComponent<AudioSource>();
    }

    _introAudioSource.clip = AudioClipIntro;
    _introAudioSource.loop = false;
    _introAudioSource.playOnAwake = false;

    _loopAudioSources[0].clip = AudioClipLoop;
    _loopAudioSources[0].loop = _loopSourceCount == 1;
    _loopAudioSources[0].playOnAwake = false;
    if (_loopAudioSources[1] != null)
    {
      _loopAudioSources[1].clip = AudioClipLoop;
      _loopAudioSources[1].loop = false;
      _loopAudioSources[1].playOnAwake = false;
    }
  }

  void Update()
  {
    // WebGL のためのループ切り替え処理
    if (_loopSourceCount >= 2)
    {
      // 終了する1秒前から次の再生のスケジュールを登録する
      if (_nowPlayIndex == 0 && _loopAudioSources[0].time >= AudioClipLoop.length - 1)
      {
        _loopAudioSources[1].PlayScheduled(AudioSettings.dspTime + (AudioClipLoop.length - _loopAudioSources[0].time));
        _nowPlayIndex = 1;
      }
      else if (_nowPlayIndex == 1 && _loopAudioSources[1].time >= AudioClipLoop.length - 1)
      {
        _loopAudioSources[0].PlayScheduled(AudioSettings.dspTime + (AudioClipLoop.length - _loopAudioSources[1].time));
        _nowPlayIndex = 0;
      }
    }
  }

  public void Play()
  {
    // クリップが設定されていない場合は何もしない
    if (_introAudioSource == null || _loopAudioSources == null) return;

    // Pause 中は isPlaying は false
    // 標準機能だけでは一時停止中か判別不可能
    if (_isPause)
    {
      _introAudioSource.UnPause();
      if (_introAudioSource.isPlaying)
      {
        // イントロ中ならループ開始時間を残り時間で再設定
        _loopAudioSources[0].Stop();
        _loopAudioSources[0].PlayScheduled(AudioSettings.dspTime + AudioClipIntro.length - _introAudioSource.time);
      }
      else
      {
        if (_loopSourceCount >= 2)
        {
          // WebGL の場合は切り替え処理を実行
          if (_loopAudioSources[0].time > 0)
          {
            _loopAudioSources[0].UnPause();
            if (_loopAudioSources[0].time >= AudioClipLoop.length - 1)
            {
              _loopAudioSources[1].Stop();
              _loopAudioSources[1].PlayScheduled(AudioSettings.dspTime + (AudioClipLoop.length - _loopAudioSources[0].time));
              _nowPlayIndex = 1;
            }
          }
          else
          {
            _loopAudioSources[1].UnPause();
            if (_loopAudioSources[1].time >= AudioClipLoop.length - 1)
            {
              _loopAudioSources[0].Stop();
              _loopAudioSources[0].PlayScheduled(AudioSettings.dspTime + (AudioClipLoop.length - _loopAudioSources[0].time));
              _nowPlayIndex = 0;
            }
          }
        }
        else
        {
          // WebGL 以外は UnPause するだけ
          _loopAudioSources[0].UnPause();
        }
      }
    }
    else if (IsPlaying == false)
    {
      // 最初から再生
      Stop();
      _introAudioSource.Play();

      // イントロの時間が経過した後に再生できるようにする
      // 設定する時間はゲーム刑か時間での設定となる
      _loopAudioSources[0].PlayScheduled(AudioSettings.dspTime + AudioClipIntro.length);
    }

    _isPause = false;
  }

  /// <summary>BGM を一時停止します。</summary>
  public void Pause()
  {
    if (_introAudioSource == null || _loopAudioSources == null) return;

    _introAudioSource.Pause();
    _loopAudioSources[0].Pause();
    if (_loopAudioSources[1] != null) _loopAudioSources[1].Pause();

    _isPause = true;
  }

  /// <summary>BGM を停止します。</summary>
  public void Stop()
  {
    if (_introAudioSource == null || _loopAudioSources == null) return;

    _introAudioSource.Stop();
    _loopAudioSources[0].Stop();
    if (_loopAudioSources[1] != null) _loopAudioSources[1].Stop();

    _isPause = false;
  }
}

Оскільки код довгий, деталі опустю, але заздалегідь встановлюю вступ і цикл AudioClip , Коли ви почнете грати, спочатку зіграйте вступ. Для циклів заплануйте їх відтворення, коли вступ закінчиться. Як тільки цикл почне відтворюватися, відтворіть його кілька разів.

Спочатку loop для циклів було достатньо встановити властивість true на , але вона не працювала належним чином у WebGL. AudioSource Я готую два цикли і перемикаю їх по черзі для гри. Якщо вам не потрібно розглядати WebGL, ви можете скоротити свій код навпіл.

Після створення сценарію ви прикріплюєте його до об'єкта. Зазвичай може бути краще створити порожній об'єкт і прикріпити його, але це клопітно, тому приєднайте його до EventSystem.

Є вступні та циклічні елементи, тому скиньте та встановіть аудіофайл відповідно.

До цього моменту ніякої спеціальної обробки не потрібно. Я хочу обробити його після натискання кнопки, тому я створюю сценарій (ButtonEvent) для кнопки.

Сценарій виглядає так:

using UnityEngine;

public class ButtonEvent : MonoBehaviour
{
  [SerializeField] private IntroLoopAudio IntroLoopAudio;

  public void OnClickPlay()
  {
    IntroLoopAudio.Play();
  }
  public void OnClickPause()
  {
    IntroLoopAudio.Pause();
  }
  public void OnClickStop()
  {
    IntroLoopAudio.Stop();
  }
}

IntroLoopAudio з інспектора і додайте дії для кожної кнопки.

Скрипт додається до EventSystem. Оскільки вам потрібно встановити Intro Loop Audio, встановіть EventSystem, яка має Intro Loop Audio.

Тепер призначте методи подіям клацання трьох кнопок.

Як тільки все налаштовано, запустіть гру і спробуйте в неї пограти. Коли відтворення закінчується до кінця, ви можете побачити, що воно циклічно зациклюється з середини пісні і відтворюється. Звичайно, це передбачає, що аудіодані акуратно розділені, а шлейфи акуратно з'єднані.

Відображення поточної тривалості

Як бонус звідси ми додали властивість, яка дозволяє нам отримати поточний час відтворення в ,IntroLoopAudio тому давайте відобразимо його.

Спочатку помістіть текст, щоб відобразити час.

Створіть сценарій.

using UnityEngine;
using UnityEngine;
using UnityEngine.UI;

public class TextEvent : MonoBehaviour
{
  [SerializeField] private IntroLoopAudio IntroLoopAudio;

  private Text _text;

  // Start is called before the first frame update
  void Start()
  {
    _text = GetComponent<Text>();
  }

  // Update is called once per frame
  void Update()
  {
    _text.text = $"AudioPlayTime : {IntroLoopAudio.time}";
  }
}

Прикріпіть скрипт до тексту і встановіть EventSystem за допомогою IntroLoopAudio.

Запустіть його і подивіться, чи відображається поточний час відтворення. Також при циклі спробуйте перевірити, чи повертається час до точки циклу.