Лупинг фонова музика с интро

Страницата се актуализира :
Дата на създаване на страница :

Среда за проверка

Уиндоус
  • Прозорци 11
Редактор на единство
  • 2021.3.3f1
Пакет на входната система
  • 1.3.0

Предпоставки за този съвет

Следните настройки са направени предварително като предпоставка за описанието на този съвет.

За материала, включен в пробата

BGM е заимстван от следния сайт.

За аудио файла на цикъла с интро

Този път само стандартната функция Unity ще играе цикъл с интро, но това не се поддържа като стандартна функция. На първо място, спецификациите на цикъла с интро като аудио файл не са фиксирани, така че методът на създаване се различава в зависимост от рамката на играта.

Този път ще подготвим два аудио файла за частта "интро" и частта "цикъл", ще играем интро частта веднъж и ще играем многократно частта на цикъла. Затова, моля, подгответе горните два файла като аудио файлове.

Някои сайтове за разпространение обмислят това и разпространяват аудио файлове отделно предварително. Ако не, трябва да го направите сами с инструмент за редактиране на глас.

Ако искате да разделите файла на две, препоръчваме следния формат на аудиофайла.

  • ОгВорбис (.ogg)
  • WAV (.wav)

За повече информация вижте официалната документация на Unity.

За възпроизвеждането на повтаряща се фонова музика с интро

Създайте потребителски интерфейс, който ви позволява да възпроизвеждате, поставяте на пауза и спирате фоновата музика като образец. Той има същото оформление като нормален образец за възпроизвеждане на фонова музика.

Добавете два аудио файла към проекта си, разделени на две части, въведение и цикъл.

Този път ще създадем собствена програма, за да играем лупинга BGM с интрото. Създайте скрипт и го оставете с име IntroLoopAudio .

Сценарият изглежда така: Имам предвид кода на следния сайт, но добавих много код, защото не работеше добре с WebGL и исках малко повече контрол.

[Справка] 【Единство】Имплементиране на интро + лупинг игра - 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 , Когато започнете да играете, първо пуснете интрото. За цикли, планирайте ги да играят, когато интрото приключи. След като цикълът започне да се възпроизвежда, играйте го многократно.

Първоначално, за цикли, беше достатъчно да зададете свойството true на , loop но не работеше правилно в 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.

Стартирайте го и вижте дали се показва текущото време на възпроизвеждане. Също така, когато циклирате, опитайте се да проверите дали времето се връща в точката на цикъла.