Loop bakgrundsmusik med intro

Sidan uppdaterad :
Datum för skapande av sida :

Verifiering miljö

Windows
  • Fönster 11
Unity-redaktör
  • 2021.3.3F1
Paket för inmatningssystem
  • 1.3.0

Förutsättningar för det här tipset

Följande inställningar har gjorts i förväg som en förutsättning för beskrivningen av detta tips.

Om materialet som ingår i provet

BGM lånas från följande webbplats.

Om ljudfilen i slingan med intro

Den här gången spelar endast standardfunktionen Unity upp en loop med ett intro, men detta stöds inte som en standardfunktion. För det första är specifikationerna för slingan med intro som en ljudfil inte fixade, så skapningsmetoden skiljer sig beroende på spelramen.

Den här gången kommer vi att förbereda två ljudfiler för "intro" -delen och "loop" -delen, spela introdelen en gång och spela loopdelen upprepade gånger. Förbered därför ovanstående två filer som ljudfiler.

Vissa distributionswebbplatser överväger detta och distribuerar ljudfiler separat i förväg. Om inte, måste du göra det själv med ett röstredigeringsverktyg.

Om du vill dela upp filen i två rekommenderar vi följande format för ljudfilen.

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

Mer information finns i den officiella Unity-dokumentationen.

Om att spela loopande bakgrundsmusik med intro

Skapa ett användargränssnitt som gör att du kan spela upp, pausa och stoppa bakgrundsmusik som ett exempel. Den har samma layout som ett vanligt bakgrundsmusikuppspelningsprov.

Lägg till två ljudfiler i ditt projekt, uppdelade i två delar, ett intro och en slinga.

Den här gången kommer vi att skapa vårt eget program för att spela looping BGM med introt. Skapa ett skript och lämna det med namnet IntroLoopAudio .

Skriptet ser ut så här: Jag hänvisar till koden på följande webbplats, men jag lade till mycket kod eftersom det inte fungerade bra med WebGL och jag ville ha lite mer kontroll.

[Referens] 【Unity】Implementera 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;
  }
}

Eftersom koden är lång kommer jag att utelämna detaljerna, men jag ställer in intro och loop AudioClip i förväg, När du börjar spela spelar du introt först. För loopar schemalägger du dem så att de spelas upp när introduktionen avslutas. När slingan börjar spela, spela den upprepade gånger.

Ursprungligen, för loopar, räckte det att ställa in egenskapen på true , loop men det fungerade inte korrekt i WebGL. AudioSource Jag förbereder två slingor och byter dem växelvis för att spela. Om du inte behöver tänka på WebGL kan du halvera din kod.

När du har skapat ett skript kopplar du det till ett objekt. Normalt kan det vara bättre att skapa ett tomt objekt och bifoga det, men det är besvärligt, så bifoga det till EventSystem.

Det finns intro- och loop-objekt, så släpp respektive ställ in ljudfilen.

Vid denna tidpunkt krävs ingen speciell behandling. Jag vill bearbeta det när jag klickar på knappen, så jag skapar ett skript (ButtonEvent) för knappen.

Skriptet ser ut så här:

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 från granskaren och lägg till åtgärder för varje knapp.

Skriptet är kopplat till EventSystem. Eftersom du behöver ställa in Intro Loop Audio ställer du in EventSystem som har Intro Loop Audio.

Tilldela nu metoder till klickhändelserna för de tre knapparna.

När allt är inställt, kör spelet och försök att spela det. När uppspelningen slutar till slutet kan du se att den slingrar från mitten av låten och spelas. Naturligtvis förutsätter detta att ljuddata är snyggt uppdelade och slingorna är snyggt anslutna.

Visa aktuell varaktighet

Som en bonus härifrånIntroLoopAudio har vi lagt till en egenskap som gör att vi kan få den aktuella uppspelningstiden i , så låt oss visa den.

Placera först texten för att visa tiden.

Skapa ett skript.

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

Bifoga ett skript till texten och ställ in EventSystem med IntroLoopAudio.

Kör den och se om den aktuella uppspelningstiden visas. När du loopar, försök också kontrollera om tiden återgår till looppunkten.