Música de fondo en bucle con intro

Actualización de la página :
Fecha de creación de la página :

Entorno de verificación

Windows
  • Ventanas 11
Unity Editor
  • 2021.3.3F1
Paquete del sistema de entrada
  • 1.3.0

Requisitos previos para esta sugerencia

Los siguientes ajustes se han realizado de antemano como premisa para la descripción de este consejo.

Sobre el material incluido con la muestra

BGM se toma prestado del siguiente sitio.

Acerca del archivo de audio del bucle con intro

Esta vez, solo la función estándar de Unity reproducirá un bucle con una introducción, pero esto no es compatible como una función estándar. En primer lugar, las especificaciones del bucle con intro como archivo de audio no son fijas, por lo que el método de creación difiere según el marco del juego.

Esta vez, prepararemos dos archivos de audio para la parte de "introducción" y la parte de "bucle", reproduciremos la parte de introducción una vez y reproduciremos la parte de bucle repetidamente. Por lo tanto, prepare los dos archivos anteriores como archivos de audio.

Algunos sitios de distribución consideran esto y distribuyen archivos de audio por separado por adelantado. Si no, debe hacerlo usted mismo con una herramienta de edición de voz.

Si desea dividir el archivo en dos, le recomendamos el siguiente formato del archivo de audio.

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

Para obtener más información, consulta la documentación oficial de Unity.

Acerca de la reproducción de música de fondo en bucle con intro

Cree una interfaz de usuario que le permita reproducir, pausar y detener música de fondo como muestra. Tiene el mismo diseño que una muestra de reproducción de música de fondo normal.

Agregue dos archivos de audio a su proyecto, divididos en dos partes, una introducción y un bucle.

Esta vez, crearemos nuestro propio programa para reproducir el BGM en bucle con la introducción. Cree un script y déjelo llamado IntroLoopAudio .

El script tiene este aspecto: Me refiero al código en el siguiente sitio, pero agregué mucho código porque no funcionaba bien con WebGL y quería un poco más de control.

[Referencia] 【Unidad】 Implementar 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;
  }
}

Dado que el código es largo, omitiré los detalles, pero establezco la introducción y el bucle AudioClip de antemano, Cuando empieces a jugar, reproduce primero la introducción. Para los bucles, prográmelos para que se reproduzcan cuando finalice la introducción. Una vez que el bucle comience a reproducirse, reprodúzcalo repetidamente.

Originalmente, para los bucles, bastaba con establecer la true propiedad en , loop pero no funcionaba correctamente en WebGL. AudioSource Preparo dos bucles y los cambio alternativamente para reproducir. Si no tiene que considerar WebGL, puede cortar su código por la mitad.

Después de crear un script, adjunte a un objeto. Normalmente, puede ser mejor crear un objeto vacío y adjuntarlo, pero es problemático, así que conéctelo a EventSystem.

Hay elementos de introducción y bucle, así que suelte y configure el archivo de audio respectivamente.

En este punto, no se requiere ningún procesamiento especial. Quiero procesarlo cuando hago clic en el botón, así que creo un script (ButtonEvent) para el botón.

El script tiene este aspecto:

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 desde el inspector y agregue acciones para cada botón.

El script se adjunta a EventSystem. Como necesita configurar Intro Loop Audio, establezca el EventSystem que tiene Intro Loop Audio.

Ahora asigne métodos a los eventos de clic de los tres botones.

Una vez que todo esté configurado, ejecute el juego e intente jugarlo. Cuando la reproducción termina hasta el final, puedes ver que se repite desde la mitad de la canción y se reproduce. Por supuesto, esto supone que los datos de audio están perfectamente divididos y los bucles están conectados ordenadamente.

Mostrar la duración actual

Como beneficio adicional de aquí, hemos agregado una propiedad que nos permite obtener el tiempo de reproducción actual en ,IntroLoopAudio así que vamos a mostrarlo.

Primero, coloque el texto para mostrar la hora.

Cree un script.

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

Adjunte un script al texto y establezca EventSystem con IntroLoopAudio.

Ejecútelo y vea si se muestra el tiempo de reproducción actual. Además, al hacer un bucle, intente verificar si el tiempo vuelve al punto de bucle.