循环背景音乐与介绍
验证环境
- 窗户
-
- 视窗 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
,然后为每个按钮添加操作。
该脚本将附加到事件系统。 由于您需要设置介绍循环音频,因此请设置具有介绍循环音频的事件系统。
现在,将方法分配给三个按钮的单击事件。
设置好所有内容后,运行游戏并尝试玩它。 当播放结束到结束时,您可以看到它从歌曲中间循环播放。 当然,这是假设音频数据划分整齐,循环连接整齐。
显示当前持续时间
作为此处的奖励,我们添加了一个属性,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}";
}
}
将脚本附加到文本并使用IntroLoopAudio设置事件系统。
运行它并查看是否显示当前播放时间。 此外,循环时,尝试检查时间是否返回到循环点。