Música de fundo em loop com introdução
Ambiente de verificação
- Windows
-
- Janelas 11
- Unity Editor
-
- 2021.3.3f1
- Pacote do sistema de entrada
-
- 1.3.0
Pré-requisitos para esta dica
As configurações a seguir foram feitas com antecedência como premissa para a descrição desta dica.
Sobre o material incluído na amostra
BGM é emprestado do seguinte site.
Sobre o arquivo de áudio do loop com introdução
Desta vez, apenas a função Unity padrão reproduzirá um loop com uma introdução, mas isso não é suportado como uma função padrão. Em primeiro lugar, as especificações do loop com intro como um arquivo de áudio não são fixas, então o método de criação difere dependendo da estrutura do jogo.
Desta vez, vamos preparar dois arquivos de áudio para a parte "intro" e a parte "loop", tocar a parte de introdução uma vez e reproduzir a parte de loop repetidamente. Portanto, prepare os dois arquivos acima como arquivos de áudio.
Alguns sites de distribuição consideram isso e distribuem arquivos de áudio separadamente com antecedência. Se não, você precisa fazer você mesmo com uma ferramenta de edição de voz.
Se você quiser dividir o arquivo em dois, recomendamos o seguinte formato do arquivo de áudio.
- OggVorbis (.ogg)
- WAV (.wav)
Para obter mais informações, consulte a documentação oficial do Unity.
Sobre a reprodução de música de fundo em loop com a introdução
Crie uma interface do usuário que permita reproduzir, pausar e parar a música de fundo como uma amostra. Ele tem o mesmo layout que uma amostra de reprodução de música de fundo normal.
Adicione dois arquivos de áudio ao seu projeto, divididos em duas partes, uma introdução e um loop.
Desta vez, vamos criar nosso próprio programa para reproduzir o BGM em looping com a introdução.
Crie um script e deixe-o nomeado IntroLoopAudio
.
O script tem a seguinte aparência: Estou me referindo ao código no seguinte site, mas adicionei muito código porque não funcionava bem com o WebGL e eu queria um pouco mais de controle.
[Referência] 【Unity】Implementar Intro + Loop Play - 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;
}
}
Como o código é longo, vou omitir os detalhes, mas defini a introdução e o loop AudioClip
com antecedência,
Quando começar a jogar, reproduza a introdução primeiro. Para loops, programe-os para serem reproduzidos quando a introdução terminar.
Quando o loop começar a tocar, reproduza-o repetidamente.
Originalmente, para loops, era suficiente definir a true
propriedade como , loop
mas ela não funcionava corretamente no WebGL.
AudioSource
Preparo dois loops e troco-os alternadamente para jogar.
Se você não precisa considerar o WebGL, você pode cortar seu código pela metade.
Depois de criar um script, anexe-o a um objeto. Normalmente, pode ser melhor criar um objeto vazio e anexá-lo, mas é problemático, então anexe-o ao EventSystem.
Há itens de introdução e loop, então solte e defina o arquivo de áudio, respectivamente.
Neste ponto, nenhum processamento especial é necessário.
Quero processá-lo quando clico no botão, então crio um script (ButtonEvent
) para o botão.
O script tem a seguinte aparência:
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
do inspetor e adicione ações para cada botão.
O script é anexado ao EventSystem. Como você precisa definir Intro Loop Audio, defina o EventSystem que tem Intro Loop Audio.
Agora atribua métodos aos eventos de clique dos três botões.
Depois que tudo estiver configurado, execute o jogo e tente jogá-lo. Quando a reprodução termina até o final, você pode ver que ele faz um loop do meio da música e toca. Claro, isso pressupõe que os dados de áudio estão bem divididos e os loops estão conectados ordenadamente.
Exibir a duração atual
Como um bônus daqui, adicionamos uma propriedade que nos permite obter o tempo de reprodução atual no ,IntroLoopAudio
então vamos exibi-lo.
Primeiro, coloque o texto para exibir a hora.
Crie um 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}";
}
}
Anexe um script ao texto e defina o EventSystem com IntroLoopAudio.
Execute-o e veja se o tempo de reprodução atual é exibido. Além disso, ao fazer um looping, tente verificar se a hora retorna ao ponto de loop.