Zapętl muzykę w tle z intro

Strona zaktualizowana :
Data utworzenia strony :

Środowisko weryfikacji

Windows
  • Okna 11
Edytor Unity
  • 2021.3.3f1
Pakiet systemu wejściowego
  • 1.3.0

Wymagania wstępne dotyczące tej porady

Poniższe ustawienia zostały wcześniej wprowadzone jako przesłanka do opisu tej wskazówki.

O materiale dołączonym do próbki

BGM jest zapożyczony z następującej strony.

O pliku audio pętli z intro

Tym razem tylko standardowa funkcja Unity będzie odtwarzać pętlę z intro, ale nie jest to obsługiwane jako standardowa funkcja. Po pierwsze, specyfikacje pętli z intro jako plikiem audio nie są stałe, więc metoda tworzenia różni się w zależności od struktury gry.

Tym razem przygotujemy dwa pliki audio dla części "intro" i części "pętli", zagramy część intro raz i zagramy partię pętli wielokrotnie. Dlatego proszę przygotować powyższe dwa pliki jako pliki audio.

Niektóre witryny dystrybucyjne biorą to pod uwagę i dystrybuują pliki audio osobno z wyprzedzeniem. Jeśli nie, musisz zrobić to sam za pomocą narzędzia do edycji głosu.

Jeśli chcesz podzielić plik na dwie części, zalecamy następujący format pliku audio.

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

Aby uzyskać więcej informacji, zapoznaj się z oficjalną dokumentacją Unity.

Informacje o odtwarzaniu zapętlonej muzyki w tle z intro

Utwórz interfejs użytkownika, który umożliwia odtwarzanie, wstrzymywanie i zatrzymywanie muzyki w tle jako próbki. Ma taki sam układ jak normalna próbka odtwarzania muzyki w tle.

Dodaj dwa pliki audio do projektu, podzielone na dwie części, intro i pętlę.

Tym razem stworzymy własny program do odtwarzania zapętlonego BGM z intro. Utwórz skrypt i pozostaw go o nazwie IntroLoopAudio .

Skrypt wygląda tak: Odnoszę się do kodu na następującej stronie, ale dodałem dużo kodu, ponieważ nie działał dobrze z WebGL i chciałem trochę więcej kontroli.

[Odniesienie] 【Jedność】 Implementacja Intro + Loop Play - 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;
  }
}

Ponieważ kod jest długi, pominę szczegóły, ale wcześniej ustawiam intro i pętlę AudioClip , Kiedy zaczniesz grać, najpierw odtwórz intro. W przypadku pętli zaplanuj ich odtwarzanie po zakończeniu intro. Gdy pętla zacznie grać, odtwarzaj ją wielokrotnie.

Pierwotnie, dla pętli, wystarczyło ustawić true właściwość na , loop ale nie działała poprawnie w WebGL. AudioSource Przygotowuję dwie pętle i przełączam je naprzemiennie do gry. Jeśli nie musisz brać pod uwagę WebGL, możesz zmniejszyć swój kod o połowę.

Po utworzeniu skryptu należy dołączyć go do obiektu. Zwykle może być lepiej utworzyć pusty obiekt i dołączyć go, ale jest to kłopotliwe, więc dołącz go do EventSystem.

Istnieją elementy wprowadzające i pętli, więc upuść i ustaw plik audio odpowiednio.

W tym momencie nie jest wymagane żadne specjalne przetwarzanie. Chcę go przetworzyć po kliknięciu przycisku, więc tworzę skrypt (ButtonEvent) dla przycisku.

Skrypt wygląda tak:

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 od inspektora i dodaj akcje dla każdego przycisku.

Skrypt jest dołączony do EventSystem. Ponieważ musisz ustawić Intro Loop Audio, ustaw EventSystem, który ma Intro Loop Audio.

Teraz przypisz metody do zdarzeń kliknięcia trzech przycisków.

Gdy wszystko jest skonfigurowane, uruchom grę i spróbuj w nią zagrać. Po zakończeniu odtwarzania do końca widać, że zapętla się od środka utworu i jest odtwarzany. Oczywiście zakłada to, że dane audio są starannie podzielone, a pętle są starannie połączone.

Wyświetlanie bieżącego czasu trwania

Jako bonus stąd dodaliśmy właściwość, która pozwala nam uzyskać bieżący czas odtwarzania w ,IntroLoopAudio więc wyświetlmy go.

Najpierw umieść tekst, aby wyświetlić godzinę.

Utwórz skrypt.

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}";
  }
}

Dołącz skrypt do tekstu i ustaw EventSystem za pomocą IntroLoopAudio.

Uruchom go i sprawdź, czy wyświetlany jest bieżący czas odtwarzania. Ponadto, podczas pętli, spróbuj sprawdzić, czy czas powraca do punktu pętli.