Giriş ile arka plan müziğini döngüye alın

Sayfa güncel :
Sayfa oluşturma tarihi :

Doğrulama ortamı

Windows
  • Pencereler 11
Birlik Editörü
  • 2021.3.3f1
Giriş Sistemi Paketi
  • 1.3.0

Bu ipucu için önkoşullar

Aşağıdaki ayarlar, bu ipucunun açıklaması için bir öncül olarak önceden yapılmıştır.

Numuneye dahil edilen malzeme hakkında

BGM aşağıdaki siteden ödünç alınmıştır.

Giriş ile döngünün ses dosyası hakkında

Bu kez, yalnızca standart Unity işlevi giriş içeren bir döngü oynatacaktır, ancak bu standart bir işlev olarak desteklenmemektedir. İlk olarak, bir ses dosyası olarak intro ile döngünün özellikleri sabit değildir, bu nedenle oluşturma yöntemi oyun çerçevesine bağlı olarak farklılık gösterir.

Bu sefer "intro" kısmı ve "loop" kısmı için iki ses dosyası hazırlayacağız, intro kısmını bir kez çalacağız ve loop kısmını tekrar tekrar çalacağız. Bu nedenle, lütfen yukarıdaki iki dosyayı ses dosyaları olarak hazırlayın.

Bazı dağıtım siteleri bunu dikkate alır ve ses dosyalarını önceden ayrı ayrı dağıtır. Değilse, bir ses düzenleme aracıyla kendiniz yapmanız gerekir.

Dosyayı ikiye bölmek istiyorsanız, ses dosyasının aşağıdaki biçimini öneririz.

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

Daha fazla bilgi için resmi Unity belgelerine bakın.

Giriş ile döngüsel arka plan müziği çalma hakkında

Arka plan müziğini örnek olarak çalmanıza, duraklatmanıza ve durdurmanıza olanak tanıyan bir kullanıcı arabirimi oluşturun. Normal bir arka plan müziği çalma örneğiyle aynı düzene sahiptir.

Projenize iki parçaya bölünmüş iki ses dosyası ekleyin, bir giriş ve bir döngü.

Bu kez, döngüsel BGM'yi intro ile oynatmak için kendi programımızı oluşturacağız. Bir komut dosyası oluşturun ve adını .IntroLoopAudio

Komut dosyası şöyle görünür: Aşağıdaki sitedeki koda atıfta bulunuyorum, ancak WebGL ile iyi çalışmadığı ve biraz daha fazla kontrol istediğim için çok fazla kod ekledim.

[Referans] 【Unity】Giriş + Döngü Oyunu Uygula - 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;
  }
}

Kod uzun olduğu için detayları atlayacağım ama intro ve loop'u AudioClip önceden ayarladım, Oynamaya başladığınızda, önce introyu oynatın. Döngüler için, giriş sona erdiğinde oynatılacak şekilde zamanlayın. Döngü çalmaya başladığında, tekrar tekrar çalın.

Başlangıçta, döngüler için, özelliği , olarak ayarlamak true yeterliydi, loop ancak WebGL'de düzgün çalışmadı. AudioSource İki döngü hazırlıyorum ve oynamak için dönüşümlü olarak değiştiriyorum. WebGL'yi göz önünde bulundurmanız gerekmiyorsa, kodunuzu yarıya indirebilirsiniz.

Bir komut dosyası oluşturduktan sonra, onu bir nesneye eklersiniz. Normalde, boş bir nesne oluşturmak ve eklemek daha iyi olabilir, ancak zahmetlidir, bu nedenle EventSystem'e ekleyin.

Giriş ve döngü öğeleri vardır, bu nedenle ses dosyasını sırasıyla bırakın ve ayarlayın.

Bu noktada, özel bir işleme gerek yoktur. Düğmeyi tıklattığımda işlemek istiyorum, bu yüzden düğme için bir komut dosyası (ButtonEvent) oluşturuyorum.

Komut dosyası şöyle görünü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 tıklayın ve her düğme için eylemler ekleyin.

Komut dosyası EventSystem'e eklenir. Giriş Döngü Sesi'ni ayarlamanız gerektiğinden, Giriş Döngü Sesi'ne sahip EventSystem'i ayarlayın.

Şimdi üç düğmenin tıklama olaylarına yöntemler atayın.

Her şey ayarlandıktan sonra, oyunu çalıştırın ve oynamaya çalışın. Çalma sona erdiğinde, şarkının ortasından döngüye girdiğini ve çalındığını görebilirsiniz. Tabii ki, bu ses verilerinin düzgün bir şekilde bölündüğünü ve döngülerin düzgün bir şekilde bağlandığını varsayar.

Geçerli süreyi görüntüleme

Buradan bir bonus olarak, mevcut oynatma süresini almamızı sağlayan bir özellik ekledik ,IntroLoopAudio bu yüzden görüntüleyelim.

İlk olarak, saati görüntülemek için metni yerleştirin.

Bir komut dosyası oluşturun.

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

Metne bir komut dosyası ekleyin ve EventSystem'i IntroLoopAudio ile ayarlayın.

Çalıştırın ve geçerli oynatma süresinin görüntülenip görüntülenmediğine bakın. Ayrıca, döngü yaparken, zamanın döngü noktasına geri dönüp dönmediğini kontrol etmeye çalışın.