Problem with update speed of builtIndex

Hi, this is my first post in here. So first of all: It it awesome that a community like this exists and actually is active!
I am looking forward to becoming good at this and contributing my first game (probably next year :wink: ):

So here is my question:

I recently started with Unity. Here is my problem:
I have a game consisting of 3 scenes : Start Menu, Game, Game Over Menu.
Here is what I want to do:
When the Start Menu is opened, play Track01.
If the Game starts, play Track02.
If the Game over Menu starts, keep playing Track02.
When the Start Menu is entered, play Track01 again.

This is how I thought I would achieve it:

  • Create one instance (Singleton) of a music player. Whenever a scene is loaded, check which scene currently is loaded, and resume/load track accordingly.
  • Problem: When a new scene is loaded, I ask for the builtIndex, which has not been updated on the time of my query yet. Therefore, it still has the index of the previous scene

My Code:

using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;
 
public class MusicPlayer : MonoBehaviour
{
    private static MusicPlayer instance;
    public static MusicPlayer Instance
    {
        get
        {
            return instance;
        }
    }
    [SerializeField] AudioClip[] soundTracks;
    private AudioSource audioSource;
 
    // Use this for initialization
    void Awake()
    {
        if (instance != null && instance != this)
        {          
            Destroy(gameObject);
        }
        else
        {
            instance = this;
            DontDestroyOnLoad(gameObject);
        }
    }
 
    private void Start()
    {      
        audioSource = GetComponent<AudioSource>();
        ChooseTrackToPlay();
    }
 
    public void ChooseTrackToPlay()
    {
        int index = SceneManager.GetActiveScene().buildIndex;
        Debug.Log("current index: " + index);
        if (index < soundTracks.Length)
        {
            if (audioSource.isPlaying)
            {
                Debug.Log("Detected Audio Source playing");
                audioSource.Stop();
            }
            audioSource.clip = soundTracks[index];
            audioSource.Play();
        }
    }
 
}

and

using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;
 
public class Level : MonoBehaviour {
 
    [SerializeField] float delayInSeconds = 3f;
 
 
    public void LoadStartMenu()
    {
        SceneManager.LoadScene(0);
        MusicPlayer.Instance.ChooseTrackToPlay();
    }
    public void LoadGame()
    {
        SceneManager.LoadScene(1);
        GameSession.Instance.ResetGame();
        Debug.Log("Loaded Scene: " + SceneManager.GetActiveScene().buildIndex);
        MusicPlayer.Instance.ChooseTrackToPlay();
    }
    public void LoadGameOver()
    {
        StartCoroutine(WaitAndLoad());
        MusicPlayer.Instance.ChooseTrackToPlay();
    }
 
    private IEnumerator WaitAndLoad()
    {
        yield return new WaitForSeconds(delayInSeconds);
        SceneManager.LoadScene(2);
    }
 
    public void QuitGame()
    {
        Application.Quit();
    }
 
}

Hi,

Some thoughts on this for you…

You say that the build index is still the value from the previous level because of the time it takes to load. If you look at this code though;

    public void LoadGame()
    {
        SceneManager.LoadScene(1);
        GameSession.Instance.ResetGame();
        Debug.Log("Loaded Scene: " + SceneManager.GetActiveScene().buildIndex);
        MusicPlayer.Instance.ChooseTrackToPlay();
    }

That code is executing in the current scene, hence why you get the build index you don’t want.

Another thought is that in all of these scenarios, you already know the build index you want to pass to the ChooseTrackToPlay method, because you’ve just used it to specify the scene to load. If you are going to use the hard coded values once, why not use them twice and just pass them to a method that plays that track?

From what I can see you really only have two occasions to change the music being played. When you’re on the main menu, and when you’re playing the game, because those are the only scene transitions you care about, e.g. you cannot get to the “Game Over” scene from the “Start Menu” scene.

You could consider using an enum which may help with readability and you could still pass the track numbers through, for example;

public enum MusicTracks { Menu = 1, Game = 2 }; 

and then you could have;

public void LoadGame()
{
    GameSession.Instance.ResetGame();
    MusicPlayer.Instance.PlayTrack((int)MusicTracks.Game);
    SceneManager.LoadScene(1);
}

and perhaps something like this in your MusicPlayer;

    public void PlayTrack(int index)
    {
        if (index < soundTracks.Length)
        {
            if (audioSource.isPlaying)
            {
                Debug.Log("Detected Audio Source playing");
                audioSource.Stop();
            }
            audioSource.clip = soundTracks[index];
            audioSource.Play();
        }
    }

Hope this helps :slight_smile:

Thank you, that worked!

This would have been another solution to do something after the scene has been loaded:
Assign a function to a delagte which listens to SceneManager.sceneLoaded

as here:

https://answers.unity.com/questions/1174255/since-onlevelwasloaded-is-deprecated-in-540b15-wha.html

1 Like

Hi,

Glad to hear that worked.

Yes, delegates can be extremely useful and you could use them to broadcast to interested parties that something has just occurred, scene loaded, player died, game over etc.

Privacy & Terms