מוזיקת רקע בלולאה עם מבוא

עודכן דף :
תאריך יצירת דף :

סביבת אימות

חלונות
  • חלונות 11
עורך Unity
  • 2021.3.3F1
חבילת מערכת קלט
  • 1.3.0

תנאים מוקדמים לטיפ זה

ההגדרות הבאות נקבעו מראש כהנחת יסוד לתיאור עצה זו.

על החומר הכלול במדגם

BGM מושאל מהאתר הבא.

על קובץ השמע של הלולאה עם מבוא

הפעם, רק פונקציית Unity הסטנדרטית תנגן לולאה עם מבוא, אך זו אינה נתמכת כפונקציה סטנדרטית. מלכתחילה, המפרטים של הלולאה עם מבוא כקובץ שמע אינם קבועים, ולכן שיטת היצירה שונה בהתאם למסגרת המשחק.

הפעם, נכין שני קבצי אודיו לחלק "ההקדמה" ולחלק "הלולאה", ננגן את חלק המבוא פעם אחת, וננגן את חלק הלולאה שוב ושוב. לכן, אנא הכינו את שני הקבצים הנ"ל כקבצי שמע.

אתרי הפצה מסוימים רואים זאת ומפיצים קבצי שמע בנפרד מראש. אם לא, אתה צריך לעשות את זה בעצמך עם כלי עריכה קולית.

אם ברצונך לפצל את הקובץ לשניים, אנו ממליצים על הפורמט הבא של קובץ השמע.

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

לקבלת מידע נוסף, עיין בתיעוד הרשמי של Unity.

אודות השמעת מוזיקת רקע בלולאה עם מבוא

צור ממשק משתמש המאפשר לך להשמיע, להשהות ולהפסיק מוזיקת רקע כדוגמה. יש לו פריסה זהה לזו של דגימת השמעת מוזיקת רקע רגילה.

הוסף שני קבצי שמע לפרויקט שלך, מפוצלים לשני חלקים, מבוא ולולאה.

הפעם, ניצור תוכנית משלנו כדי לשחק את 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 מראש, כשתתחיל לשחק, נגן תחילה את הפתיח. עבור לולאות, תזמן אותן להפעלה כאשר המבוא מסתיים. ברגע שהלולאה מתחילה להתנגן, הפעל אותה שוב ושוב.

במקור, עבור לולאות, זה היה מספיק כדי להגדיר את הנכס true ל- , loop אבל זה לא עבד כראוי ב- WebGL. AudioSource אני מכין שתי לולאות ומחליף אותן לסירוגין כדי לשחק. אם אתה לא צריך לשקול WebGL, אתה יכול לחתוך את הקוד שלך בחצי.

לאחר יצירת סקריפט, מצרפים אותו לאובייקט. בדרך כלל, ייתכן שעדיף ליצור אובייקט ריק ולצרף אותו, אבל זה בעייתי, אז חבר אותו ל- EventSystem.

ישנם פריטי מבוא ולולאה, אז שחרר והגדר את קובץ השמע בהתאמה.

בשלב זה, אין צורך בעיבוד מיוחד. ברצוני לעבד אותו בעת לחיצה על הלחצן, לכן אני יוצר קובץ Script (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. מכיוון שעליך להגדיר שמע מבוא לולאה, הגדר את מערכת האירועים הכוללת שמע מבוא לולאה.

כעת הקצה שיטות לאירועי הלחיצה של שלושת הכפתורים.

לאחר שהכל מוגדר, הפעל את המשחק ונסה לשחק בו. כאשר ההשמעה מסתיימת עד הסוף, אתה יכול לראות שזה לולאה מאמצע השיר ומתנגן. כמובן, זה בהנחה שנתוני השמע מחולקים בצורה מסודרת והלולאות מחוברות בצורה מסודרת.

הצגת משך הזמן הנוכחי

כבונוס מכאן, הוספנו מאפיין המאפשר לנו לקבל את זמן ההשמעה הנוכחי ב- ,IntroLoopAudio אז בואו נציג אותו.

תחילה, מקם את הטקסט כדי להציג את השעה.

צור קובץ 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}";
  }
}

צרף סקריפט לטקסט והגדר את EventSystem עם IntroLoopAudio.

הפעל אותו ובדוק אם זמן ההפעלה הנוכחי מוצג. כמו כן, בעת לולאה, נסה לבדוק אם הזמן חוזר לנקודת הלולאה.