Musique de fond en boucle avec intro

Page mise à jour :
Date de création de la page :

Environnement de vérification

Windows
  • Fenêtres 11
Éditeur Unity
  • 2021.3.3f1
Package système d’entrée
  • 1.3.0

Conditions préalables à cette astuce

Les paramètres suivants ont été définis à l’avance comme prémisse pour la description de cette astuce.

À propos du matériel inclus avec l’échantillon

BGM est emprunté au site suivant.

A propos du fichier audio de la boucle avec intro

Cette fois, seule la fonction Unity standard jouera une boucle avec une intro, mais cela n’est pas pris en charge en tant que fonction standard. En premier lieu, les spécifications de la boucle avec intro en tant que fichier audio ne sont pas fixes, de sorte que la méthode de création diffère en fonction du cadre du jeu.

Cette fois, nous allons préparer deux fichiers audio pour la partie « intro » et la partie « boucle », jouer la partie intro une fois et lire la partie boucle à plusieurs reprises. Par conséquent, veuillez préparer les deux fichiers ci-dessus en tant que fichiers audio.

Certains sites de distribution en tiennent compte et distribuent les fichiers audio séparément à l’avance. Sinon, vous devez le faire vous-même avec un outil d’édition vocale.

Si vous souhaitez diviser le fichier en deux, nous vous recommandons le format suivant du fichier audio.

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

Pour plus d’informations, consultez la documentation officielle de Unity.

À propos de la lecture de musique de fond en boucle avec l’intro

Créez une interface utilisateur qui vous permet de lire, de mettre en pause et d’arrêter la musique de fond en tant qu’échantillon. Il a la même disposition qu’un échantillon de lecture de musique de fond normal.

Ajoutez deux fichiers audio à votre projet, divisés en deux parties, une intro et une boucle.

Cette fois, nous allons créer notre propre programme pour jouer le BGM en boucle avec l’intro. Créez un script et laissez-le nommé IntroLoopAudio .

Le script ressemble à ceci : Je fais référence au code sur le site suivant, mais j’ai ajouté beaucoup de code parce que cela ne fonctionnait pas bien avec WebGL et je voulais un peu plus de contrôle.

[Référence] 【Unity】 Implémenter 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;
  }
}

Comme le code est long, je vais omettre les détails, mais j’ai défini l’intro et la boucle AudioClip à l’avance, Lorsque vous commencez à jouer, jouez d’abord l’intro. Pour les boucles, planifiez-les pour qu’elles soient jouées à la fin de l’intro. Une fois que la boucle commence à jouer, jouez-la à plusieurs reprises.

À l’origine, pour les boucles, il suffisait de définir la true propriété sur , loop mais cela ne fonctionnait pas correctement dans WebGL. AudioSource Je prépare deux boucles et les change alternativement pour jouer. Si vous n’avez pas besoin de considérer WebGL, vous pouvez réduire votre code de moitié.

Après avoir créé un script, vous l’attachez à un objet. Normalement, il peut être préférable de créer un objet vide et de l’attacher, mais c’est gênant, alors attachez-le à EventSystem.

Il y a des éléments d’introduction et de boucle, alors déposez et définissez le fichier audio respectivement.

À ce stade, aucun traitement spécial n’est requis. Je veux le traiter lorsque je clique sur le bouton, donc je crée un script (ButtonEvent) pour le bouton.

Le script ressemble à ceci :

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 de l’inspecteur et ajoutez des actions pour chaque bouton.

Le script est attaché à EventSystem. Puisque vous devez définir Intro Loop Audio, définissez le EventSystem qui a Intro Loop Audio.

Attribuez maintenant des méthodes aux événements de clic des trois boutons.

Une fois que tout est configuré, lancez le jeu et essayez d’y jouer. Lorsque la lecture se termine jusqu’à la fin, vous pouvez voir qu’elle tourne en boucle à partir du milieu de la chanson et joue. Bien sûr, cela suppose que les données audio sont soigneusement divisées et que les boucles sont bien connectées.

Afficher la durée actuelle

En prime, nous avons ajouté une propriété qui nous permet d’obtenir le temps de lecture actuel dans ,IntroLoopAudio alors affichons-le.

Tout d’abord, placez le texte pour afficher l’heure.

Créez 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}";
  }
}

Joignez un script au texte et définissez le EventSystem avec IntroLoopAudio.

Exécutez-le et voyez si la durée de lecture actuelle est affichée. De plus, lors de la boucle, essayez de vérifier si l’heure revient au point de boucle.