Alright, I’ve got a weird one for ya.
Goal:
- Use a collider to activate new background music
- When music is finished, play another song
- When that song is finished, play another song
Here is what is currently happening:
- Collider activates new background music
- When that music is finished, another song is played
- Then another song starts playing before the last song is finished, thereby having two songs playing at the same time.
Here is the current collider/ song activator script:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class SignPostMusic : MonoBehaviour
{
public string textToDisplay;
public int signPostSound;
public AudioSource song;
private bool canStartNewSong;
// Start is called before the first frame update
void Start()
{
}
// Update is called once per frame
void Update()
{
}
private void OnTriggerEnter2D(Collider2D other)
{
if (other.tag == "Player")
{
canStartNewSong = true;
AudioManager.instance.PlaySFX(signPostSound);
HubUIController.instance.messageText.text = textToDisplay;
HubUIController.instance.DisplayMessage();
AudioManager.instance.hubMusic.Stop();
AudioManager.instance.StopAllAudio();
song.Play();
if (canStartNewSong == true)
{
AudioManager.instance.Invoke("PlayRandomHubSong", song.clip.length);
canStartNewSong = false;
}
}
}
}
I’m not going to include the entire AudioManager.instance script, but here is the pertinent section:
void PlayRandomHubSong()
{
hubMusic = hubMusicSoundtrack[Random.Range(0, hubMusicSoundtrack.Length)];
hubMusic.Play();
hubBGSFX.Play();
Invoke("PlayRandomHubSong", hubMusic.clip.length);
Debug.Log("play song " + hubMusic + "for " + hubMusic.clip.length);
}
Right now the void PlayRandomHubSong() works just fine, and continues to play a random song at the right time. It’s only when using this collider/ activator to trigger a new song that the timing gets thrown off.
Any suggestions?
Don’t use Invoke. When you stop the current audio from playing in the collision, your Invoke is still scheduled, and there’s no way to cancel it. Instead, use a coroutine. When you start a coroutine you can keep a reference to the coroutine around so that you can cancel the coroutine later:
. Coroutine nextScheduledSong;
IEnumerator PlayRandomHubSong(float withDelay = 0)
{
if (withDelay > 0) {
yield return new WaitForSeconds(withDelay);
}
hubMusic = hubMusicSoundtrack[Random.Range(0, hubMusicSoundtrack.Length)];
hubMusic.Play();
hubBGSFX.Play();
nextScheduledSong = StartCoroutine(PlayRandomHubSong( hubMusic.clip.length));
Debug.Log("play song " + hubMusic + "for " + hubMusic.clip.length);
}
Then add this to AudioManager.StopAllAudio:
if (nextScheduledSong != null) StopCoroutine(nextScheduledSong);
And to start the hub music immediately, see my next post.
Since you have these things on two different objects, I would add this method to the AudioManager as well:
public void BeginPlayingHubMusic(float withDelay = 0) {
nextScheduledSong = StartCoroutine(PlayRandomHubSong(withDelay));
}
And replace line 38 with:
AudioManager.instance.BeginPlayingHubMusic(song.clip.length);
I’m assuming “song” in this context is some other song not related to the hub music? There’s probably more work you can do to continue to centralize all of this in AudioManager as well, but this should work for now.
Instead of scheduling play of the song in the future, I’d consider just checking if IsPlaying is false. If IsPlaying == false, then the song is done playing, so play the next song.
1 Like
This seemed like the simplest way to accomplish this, but for some reason it is not quite working.
Whenever the collider music is activated, it is also playing the hubMusic.
Updated collider script:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class SignPostMusic : MonoBehaviour
{
public string textToDisplay;
public int signPostSound;
public AudioSource song;
// Start is called before the first frame update
void Start()
{
AudioManager.instance.creditPlaying = false;
song.Stop();
}
// Update is called once per frame
void Update()
{
if (song.isPlaying)
{
Debug.Log("now playing " + song);
AudioManager.instance.creditPlaying = true;
}
if(!song.isPlaying)
{
AudioManager.instance.creditPlaying = false;
}
}
private void OnTriggerEnter2D(Collider2D other)
{
if (other.tag == "Player")
{
AudioManager.instance.PlaySFX(signPostSound);
HubUIController.instance.messageText.text = textToDisplay;
HubUIController.instance.DisplayMessage();
AudioManager.instance.hubMusic.Stop();
AudioManager.instance.StopAllAudio();
song.Play();
}
}
}
Updated AudioManager script:
void Update()
{
if (!creditPlaying && !hubMusic.isPlaying)
{
PlayRandomHubSong();
Debug.Log("creditPlaying " + creditPlaying);
}
if (creditPlaying && hubMusic.isPlaying)
{
Debug.Log("creditPlaying " + creditPlaying);
hubMusic.Stop();
}
if (!hubBGSFX.isPlaying && !bgSFXPlaying)
{
bgSFXPlaying = true;
PlayHubBGSFX();
}
}
void PlayRandomHubSong()
{
hubMusic = hubMusicSoundtrack[Random.Range(0, hubMusicSoundtrack.Length)];
hubMusic.Play();
//hubBGSFX.Play();
//Invoke("PlayRandomHubSong", hubMusic.clip.length);
//Invoke("PlayHubSFX", hubBGSFX.clip.length);
Debug.Log("play song " + hubMusic + "for " + hubMusic.clip.length);
}
I feel like I’m freaking out. What could this issue be?