Ciklinė foninė muzika su įvadu

Puslapis atnaujintas :
Puslapio sukūrimo data :

Tikrinimo aplinka

Windows
  • Langai 11
"Unity" redaktorius
  • 2021.3.3F1
Įvesties sistemos paketas
  • 1.3.0

Būtinos šio patarimo sąlygos

Šie nustatymai buvo atlikti iš anksto kaip šio patarimo aprašymo prielaida.

Apie medžiagą, įtrauktą į pavyzdį

BGM yra pasiskolintas iš šios svetainės.

Apie ciklo garso failą su įvadu

Šį kartą tik standartinė "Unity" funkcija paleis kilpą su įvadu, tačiau tai nepalaikoma kaip standartinė funkcija. Visų pirma, kilpos su įvadu kaip garso failu specifikacijos nėra fiksuotos, todėl kūrimo metodas skiriasi priklausomai nuo žaidimo sistemos.

Šį kartą paruošime du garso failus "įvadinei" ir "kilpos" dalims, vieną kartą paleisime įvadinę dalį ir pakartotinai paleisime kilpos dalį. Todėl paruoškite aukščiau nurodytus du failus kaip garso failus.

Kai kurios platinimo svetainės tai atsižvelgia ir iš anksto platina garso failus atskirai. Jei ne, turite tai padaryti patys naudodami balso redagavimo įrankį.

Jei norite padalyti failą į dvi dalis, rekomenduojame tokį garso failo formatą.

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

Daugiau informacijos rasite oficialioje "Unity" dokumentacijoje.

Apie foninės muzikos grojimą su įvadu

Sukurkite vartotojo sąsają, leidžiančią leisti, pristabdyti ir sustabdyti foninę muziką kaip pavyzdį. Jo išdėstymas yra toks pat kaip įprasto foninės muzikos atkūrimo pavyzdžio.

Pridėkite du garso failus prie savo projekto, padalinkite į dvi dalis, įvadą ir ciklą.

Šį kartą mes sukursime savo programą, kad galėtume žaisti kilpinį BGM su įvadu. Sukurkite scenarijų ir palikite jį pavadintą IntroLoopAudio .

Scenarijus atrodo taip: Aš kalbu apie kodą šioje svetainėje, bet pridėjau daug kodo, nes jis neveikė gerai su "WebGL" ir norėjau šiek tiek daugiau kontrolės.

[Nuoroda] 【Vienybė】Įdiekite įvadą + kilpos žaidimą - 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;
  }
}

Kadangi kodas yra ilgas, praleisiu detales, bet iš anksto nustatiau įvadą ir kilpą AudioClip , Kai pradėsite žaisti, pirmiausia paleiskite įvadą. Kilpoms suplanuokite, kad jos būtų žaidžiamos, kai baigsis įžanga. Kai kilpa pradės groti, paleiskite ją pakartotinai.

Iš pradžių loop kilpoms pakako nustatyti true nuosavybę į , tačiau "WebGL" ji tinkamai neveikė. AudioSource Paruošiu dvi kilpas ir pakaitomis jas perjungiu, kad galėčiau žaisti. Jei jums nereikia atsižvelgti į "WebGL", galite perpus sumažinti kodą.

Sukūrę scenarijų, pridedate jį prie objekto. Paprastai gali būti geriau sukurti tuščią objektą ir jį pridėti, tačiau tai vargina, todėl pridėkite jį prie "EventSystem".

Yra įvadinių ir kilpinių elementų, todėl numeskite ir nustatykite garso failą atitinkamai.

Šiuo metu specialaus apdorojimo nereikia. Noriu jį apdoroti, kai spusteliu mygtuką, todėl sukuriu mygtuko scenarijų (ButtonEvent).

Scenarijus atrodo taip:

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 iš inspektoriaus ir pridėkite veiksmus kiekvienam mygtukui.

Scenarijus pridėtas prie "EventSystem". Kadangi jums reikia nustatyti "Intro Loop Audio", nustatykite "EventSystem", kurioje yra "Intro Loop Audio".

Dabar priskirkite metodus trijų mygtukų paspaudimo įvykiams.

Kai viskas bus nustatyta, paleiskite žaidimą ir pabandykite jį žaisti. Kai atkūrimas baigiasi iki galo, matote, kad jis kartojasi nuo dainos vidurio ir groja. Žinoma, tai reiškia, kad garso duomenys yra tvarkingai suskirstyti ir kilpos yra tvarkingai sujungtos.

Rodyti dabartinę trukmę

Kaip premiją iš čia pridėjome nuosavybę, leidžiančią gauti dabartinį atkūrimo laiką ,IntroLoopAudio todėl parodykime jį.

Pirmiausia įdėkite tekstą, kad būtų rodomas laikas.

Sukurkite scenarijų.

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

Pridėkite scenarijų prie teksto ir nustatykite "EventSystem" naudodami "IntroLoopAudio".

Paleiskite jį ir pažiūrėkite, ar rodomas dabartinis atkūrimo laikas. Be to, kai naudojate kilpą, pabandykite patikrinti, ar laikas grįžta į kilpos tašką.