How to play a sound when a prefab button that changes scene is clicked

Hi, my current situation is:

  • There is a prefab button
  • The button has attached to it an Audio Source and the field AudioClip has the correct sound (if I check the Play On Awake option you can hear it when the button is created inside a scene)
  • I tried to set runtime the OnClick function with a listener, here I tried to play the sound attached to the button and, after that, to load a new scene

I found out that the loading of a new scene probably forces the audio to stop to play and so my current code is:

 //Inside a method that instantiate the button b
b.GetComponent<Button>().onClick.AddListener((() => {
                AudioSource btnAudio = b.GetComponent<AudioSource>();
                btnAudio.PlayOneShot(btnAudio.clip);
                StartCoroutine(Wait(btnAudio.clip.length));
                StartCoroutine(ChangeScene());
            }));

//This method is inside the same class
System.Collections.IEnumerator Wait(float minTime)
{
    yield return new WaitForSeconds(minTime);
}

//This method is inside the same class
System.Collections.IEnumerator ChangeScene()
{
    while (LocalizationManager.instance.GetIsReady() == false)
    {
        yield return null;
    }

    SceneManager.LoadScene("newScene");
}

Right now this code doesn’t produce any sound but the newScene is loaded correctly.
Any suggestion?

PS: I tried to log the clip.name and it is the correct name of the audio source that i placed inside Resources/Audio

You need to make a new script (such as MusicController) to keep the music running in all scenes.
MusicController will be a singleton class which will have the Button which will switch music on or off.
In this class you can make functions to check if music is currently playing or not and so on.

Greetings my friend!

The thing is you start coroutines one after another inside the lambda expression (line 5,6). The Wait coroutine won’t delay execution of ChangeScene coroutine. You can create a single couroutine that has Wait funcitonality in it and loads next scene like that one:


  private IEnumerator WaitAndLoadScene(float clipLength)
    {
        yield return new WaitForSeconds(clipLength);
        while(LocalizationManager.instance.GetIsReady() == false)
        {
            yield return new WaitForEndOfFrame();
        }
        SceneManager.LoadScene("newScene");
    }

You can simplify this using a single coroutine where you load your next scene asynchronously, play the audioclip, and wait for both the scene to load and the audioclip to finish playing before allowing the scene to load. It’ll look something like this:

private IEnumerator ChangeScene()
{
    float duration = btnAudio.clip.length;
    btnAudio.PlayOneShot(btnAudio.clip);
    
    //load the scene asynchrounously, it's important you set allowsceneactivation to false
    //in order to wait for the audioclip to finish playing
    AsyncOperation sceneLoading = SceneManager.LoadSceneAsync("newScene");
    sceneLoading.allowSceneActivation = false;
    
    //wait for the audioclip to end
    yield return new WaitForSeconds(duration);
    //wait for the scene to finish loading (it will always stop at 0.9 when allowSceneActivation is false
    while (sceneLoading.progress < 0.9f) yield return null;
    //allow the scene to load
    sceneLoading.allowSceneActivation = true;
}

One thing I have noticed is that there will still be a noticeable lag when called LoadSceneAsync in the editor, but when the application is built this lag is gone. Hope this helped!