حلقه موسیقی پس زمینه با مقدمه

صفحه به روز شده :
تاریخ ایجاد صفحه :

محیط تایید

ویندوز
  • ویندوز 11
ویرایشگر یونیتی
  • دانلود: 2021.3.3f1
بسته سیستم ورودی
  • 1.3.0

پیشنیازهای این نکته

تنظیمات زیر از قبل به عنوان یک فرض برای توصیف این نکته ساخته شده است.

درباره مواد همراه با نمونه

BGM از سایت زیر قرض گرفته شده است.

درباره فایل صوتی حلقه با مقدمه

این بار، تنها تابع استاندارد Unity یک حلقه را با یک مقدمه بازی می کند، اما این به عنوان یک تابع استاندارد پشتیبانی نمی شود. در وهله اول، مشخصات حلقه با مقدمه به عنوان یک فایل صوتی ثابت نیست، بنابراین روش ایجاد بسته به چارچوب بازی متفاوت است.

این بار، ما دو فایل صوتی را برای بخش "مقدمه" و بخش "حلقه" اماده می کنیم، یک بار بخش مقدمه را پخش می کنیم و بخش حلقه را بارها و بارها پخش می کنیم. بنابراین، لطفا دو فایل فوق را به عنوان فایل های صوتی اماده کنید.

برخی از سایت های توزیع این را در نظر می گیرد و فایل های صوتی را به طور جداگانه از قبل توزیع می کنند. اگر نه، باید خودتان ان را با یک ابزار ویرایش صدا بسازید.

اگر می خواهید فایل را به دو قسمت تقسیم کنید، فرمت زیر فایل صوتی را توصیه می کنیم.

  • اگ ووربیس (.ogg)
  • پنل های ال سی (.wav)

برای اطلاعات بیشتر، مستندات رسمی Unity را ببینید.

درباره پخش موسیقی پس زمینه حلقه با مقدمه

یک UI ایجاد کنید که به شما اجازه می دهد موسیقی پس زمینه را به عنوان نمونه پخش، مکث و متوقف کنید. این همان طرح به عنوان یک نمونه پخش موسیقی پس زمینه عادی است.

دو فایل صوتی را به پروژه خود اضافه کنید، به دو بخش تقسیم کنید، یک مقدمه و یک حلقه.

این بار، ما برنامه خودمان را برای پخش BGM حلقه با مقدمه ایجاد خواهیم کرد. یک اسکریپت ایجاد کنید و ان را به نام IntroLoopAudio بگذارید.

فیلمنامه به این شکل است: من با اشاره به کد در سایت زیر، اما من اضافه شده بسیاری از کد به دلیل ان را به خوبی با WebGL کار نمی کند و من می خواستم کنترل کمی بیشتر.

[مرجع] 【وحدت】 پیاده سازی مقدمه + حلقه بازی - 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;
  }
}

از انجا که کد طولانی است، من جزئیات را حذف می کنم، اما مقدمه و حلقه AudioClip را از قبل تنظیم می کنم، هنگامی که شروع به بازی می کنید، ابتدا مقدمه را بازی کنید. برای حلقه ها، انها را برای بازی زمانی که مقدمه به پایان می رسد، برنامه ریزی کنید. هنگامی که حلقه شروع به بازی می کند، ان را بارها و بارها بازی کنید.

در اصل، loop برای حلقه ها، برای تنظیم true ویژگی کافی بود، اما در WebGL به درستی کار نمی کرد. AudioSource من دو حلقه را اماده می کنم و انها را به طور متناوب برای بازی تغییر می دهم. اگر مجبور نیستید WebGL را در نظر بگیرید، می توانید کد خود را به نصف کاهش دهید.

پس از ایجاد یک اسکریپت، ان را به یک شی متصل می کنید. به طور معمول، ممکن است بهتر باشد یک شی خالی ایجاد کنید و ان را متصل کنید، اما مشکل ساز است، بنابراین ان را به EventSystem متصل کنید.

موارد مقدمه و حلقه وجود دارد، بنابراین فایل صوتی را به ترتیب رها کنید و تنظیم کنید.

در این مرحله، هیچ پردازش خاصی لازم نیست. من می خواهم ان را پردازش زمانی که من با کلیک بر روی دکمه، بنابراین من ایجاد یک اسکریپت (ButtonEvent) برای دکمه.

فیلمنامه به این شکل است:

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 از بازرس و اضافه کردن اقدامات برای هر دکمه.

این اسکریپت به EventSystem متصل شده است. از انجا که شما نیاز به تنظیم Intro Loop Audio دارید، EventSystem را تنظیم کنید که دارای Intro Loop Audio است.

اکنون روش ها را به رویدادهای کلیک سه دکمه اختصاص دهید.

هنگامی که همه چیز تنظیم شد، بازی را اجرا کنید و سعی کنید ان را بازی کنید. هنگامی که پخش به پایان می رسد، می توانید ببینید که از وسط اهنگ حلقه می شود و پخش می شود. البته، این فرض می کند که داده های صوتی به طور مرتب تقسیم می شوند و حلقه ها به طور مرتب متصل می شوند.

نمایش مدت زمان جاری

به عنوان یک جایزه از اینجا،IntroLoopAudio ما یک ملک اضافه کرده ایم که به ما اجازه می دهد زمان پخش فعلی را دریافت کنیم، بنابراین بیایید ان را نمایش دهیم.

ابتدا متن را برای نمایش زمان قرار دهید.

یک اسکریپت ایجاد کنید.

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

یک اسکریپت را به متن پیوست کنید و EventSystem را با IntroLoopAudio تنظیم کنید.

ان را اجرا کنید و ببینید ایا زمان پخش فعلی نمایش داده می شود. همچنین، هنگام حلقه زدن، سعی کنید بررسی کنید که ایا زمان به نقطه حلقه باز می گردد.