Lặp lại nhạc nền với phần giới thiệu

Trang Cập Nhật :
Ngày tạo trang :

Môi trường xác minh

Windows
  • cửa sổ 11
Biên tập viên Unity
  • 2021.3.3F1
Gói hệ thống đầu vào
  • 1.3.0

Điều kiện tiên quyết cho mẹo này

Các cài đặt sau đây đã được thực hiện trước làm tiền đề cho mô tả về mẹo này.

Về vật liệu đi kèm với mẫu

BGM được mượn từ trang web sau.

Giới thiệu về tệp âm thanh của vòng lặp với phần giới thiệu

Lần này, chỉ có hàm Unity tiêu chuẩn sẽ phát một vòng lặp với phần giới thiệu, nhưng điều này không được hỗ trợ như một hàm tiêu chuẩn. Ngay từ đầu, các thông số kỹ thuật của vòng lặp với phần giới thiệu dưới dạng tệp âm thanh không cố định, vì vậy phương pháp tạo khác nhau tùy thuộc vào khung trò chơi.

Lần này, chúng tôi sẽ chuẩn bị hai tệp âm thanh cho phần "giới thiệu" và phần "vòng lặp", phát phần giới thiệu một lần và phát phần vòng lặp nhiều lần. Do đó, vui lòng chuẩn bị hai tệp trên dưới dạng tệp âm thanh.

Một số trang web phân phối xem xét điều này và phân phối các tệp âm thanh riêng biệt trước. Nếu không, bạn cần tự làm bằng công cụ chỉnh sửa giọng nói.

Nếu bạn muốn chia tệp thành hai, chúng tôi khuyên bạn nên sử dụng định dạng sau của tệp âm thanh.

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

Để biết thêm thông tin, hãy xem tài liệu chính thức của Unity.

Giới thiệu về phát nhạc nền lặp lại với phần giới thiệu

Tạo giao diện người dùng cho phép bạn phát, tạm dừng và dừng nhạc nền dưới dạng mẫu. Nó có bố cục giống như một mẫu phát lại nhạc nền thông thường.

Thêm hai tệp âm thanh vào dự án của bạn, được chia thành hai phần, phần giới thiệu và vòng lặp.

Lần này, chúng tôi sẽ tạo chương trình của riêng mình để phát BGM lặp lại với phần giới thiệu. Tạo một tập lệnh và để nó được đặt tên IntroLoopAudio là .

Kịch bản trông như thế này: Tôi đang đề cập đến mã trên trang web sau, nhưng tôi đã thêm rất nhiều mã vì nó không hoạt động tốt với WebGL và tôi muốn kiểm soát nhiều hơn một chút.

[Tham khảo] 【Thống nhất】 Triển khai Giới thiệu + Chơi vòng lặp - 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;
  }
}

Vì mã dài, tôi sẽ bỏ qua các chi tiết, nhưng tôi đặt phần giới thiệu và vòng lặp AudioClip trước, Khi bạn bắt đầu chơi, hãy phát phần giới thiệu trước. Đối với các vòng lặp, hãy lên lịch cho chúng phát khi phần giới thiệu kết thúc. Khi vòng lặp bắt đầu phát, hãy phát nó nhiều lần.

Ban đầu, đối với các vòng lặp, nó là đủ để đặt true thuộc tính thành , loop nhưng nó không hoạt động đúng trong WebGL. AudioSource Tôi chuẩn bị hai vòng lặp và chuyển chúng luân phiên để chơi. Nếu bạn không phải xem xét WebGL, bạn có thể cắt mã của mình xuống một nửa.

Sau khi bạn tạo một tập lệnh, bạn đính kèm nó vào một đối tượng. Thông thường, có thể tốt hơn để tạo một đối tượng trống và đính kèm nó, nhưng nó rất rắc rối, vì vậy hãy gắn nó vào EventSystem.

Có các mục giới thiệu và vòng lặp, vì vậy hãy thả và đặt tệp âm thanh tương ứng.

Tại thời điểm này, không cần xử lý đặc biệt. Tôi muốn xử lý nó khi tôi nhấp vào nút, vì vậy tôi tạo một tập lệnh (ButtonEvent) cho nút.

Kịch bản trông như thế này:

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ừ trình kiểm tra và thêm hành động cho từng nút.

Tập lệnh được đính kèm với EventSystem. Vì bạn cần đặt Intro Loop Audio, hãy đặt EventSystem có Intro Loop Audio.

Bây giờ gán phương thức cho các sự kiện nhấp chuột của ba nút.

Khi mọi thứ đã được thiết lập, hãy chạy trò chơi và cố gắng chơi nó. Khi quá trình phát lại kết thúc đến cuối, bạn có thể thấy rằng nó lặp lại từ giữa bài hát và phát. Tất nhiên, điều này giả định rằng dữ liệu âm thanh được phân chia gọn gàng và các vòng lặp được kết nối gọn gàng.

Hiển thị thời lượng hiện tại

Như một phần thưởng từ đây, chúng tôi đã thêm một thuộc tính cho phép chúng tôi có được thời gian phát lại hiện tại trong ,IntroLoopAudio vì vậy hãy hiển thị nó.

Đầu tiên, đặt văn bản để hiển thị thời gian.

Tạo tập lệnh.

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

Đính kèm tập lệnh vào văn bản và đặt EventSystem với IntroLoopAudio.

Chạy nó và xem thời gian phát lại hiện tại có được hiển thị không. Ngoài ra, khi lặp lại, hãy cố gắng kiểm tra xem thời gian có quay trở lại điểm vòng lặp hay không.