AudioClip keeps playing even when using Stop()

Hi.
I’m learning Unity/C# for some months now and i’m having a little problem with my first game.
I have a BGM which i want to turn on and off in the options with buttons. This gets saved in PlayerPrefs.
First time starting works well, BGM plays, and i can turn it on and off normally.
But when i leave it off, stop the game, restart it, the BGM starts, although the code shouldn’t start it.

I have an empty GameObject with an AudioSource (PlayOnAwake is unchecked) with the BGM. This is “MainTheme” in the code. It also has a script attached with DontDestroyOnLoad (don’t know if this can interfere).

The following script is attached to my GameManager object.
To test it i placed many Debug.Logs which showed me that it should work correctly.
(i deleted the stuff for sfx for readability because it’s basically the same)

public class BGMAndSFX : MonoBehaviour
{
    public MainTheme mainTheme;
    public UnityEngine.UI.Button bgm;
    public UnityEngine.UI.Button sfx;

    private AudioSource audioSource;
    private bool bgmOnOff;
    private bool sfxOnOff;

    private void Awake()
    {
        if (!PlayerPrefs.HasKey("bgmOnOff"))
        {
            PlayerPrefs.SetString("bgmOnOff", "true");
        }
    }

    void Start()
    {
        audioSource = mainTheme.GetComponent<AudioSource>();

        if (PlayerPrefs.GetString("bgmOnOff") == "true")
        {
            BGMControl(true);
        }
        else if (PlayerPrefs.GetString("bgmOnOff") == "false")
        {
            BGMControl(false);
        }

        bgm.onClick.AddListener(delegate { BGMControl(!bgmOnOff); });
    }

    public void BGMControl(bool state)
    {
        bgmOnOff = state;
        if (!bgmOnOff)
        {
            bgm.GetComponent<Image>().color = new Color32(50, 50, 50, 255);
            bgm.GetComponentInChildren<Text>().text = "Musik: Aus";
            PlayerPrefs.SetString("bgmOnOff", "false");
            audioSource.Stop();
        }
        else if (bgmOnOff)
        {
            bgm.GetComponent<Image>().color = new Color32(105, 105, 105, 255);
            bgm.GetComponentInChildren<Text>().text = "Musik: An";
            PlayerPrefs.SetString("bgmOnOff", "true");
            audioSource.Play();
        }
    }
}

I pretty sure it’s (again) a simple solution, but i have no clue right now.
Thanks for your help!

bgmOnOff is a super confusing variable name, change it, haha
are you sure you didn’t reverse the if statement logic on the end?
try doing this:

        if (bgmOnOff)
        {
            bgm.GetComponent<Image>().color = new Color32(50, 50, 50, 255);
            bgm.GetComponentInChildren<Text>().text = "Musik: Aus";
            PlayerPrefs.SetString("bgmOnOff", "false");
            audioSource.Stop();
        }
        else if (!bgmOnOff)
        {
            bgm.GetComponent<Image>().color = new Color32(105, 105, 105, 255);
            bgm.GetComponentInChildren<Text>().text = "Musik: An";
            PlayerPrefs.SetString("bgmOnOff", "true");
            audioSource.Play();
        }

too tired to follow the logic myself.

one other point, you don’t need to do the else like you are doing, if you’re evaluating a bool and it’s not one state it has to be the other (unless you use a nullable “bool?” type, but that’s another story)

bool b = false;
if(b) { //same as "b == true"
   //do true stuff
} else { //will only run if "b != true" or "b == false", don't need an actual check here
   //do false stuff
}

Hi unity_EOJKufFJLxaWfw!

I do not see PlayerPrefs.Save() being called in this. I don’t think the editor calls it automatically (I could be wrong!)

Maybe if you called PlayerPrefs.Save() on the last line of BGMControl ?

I would do something like this:

using UnityEngine;
using UnityEngine.UI;

public class BGMAndSFX : MonoBehaviour
{
    [SerializeField]
    private MainTheme mainTheme;
    [SerializeField]
    private Button bgm;
    [SerializeField]
    private Button sfx;

    [SerializeField]
    private Color musicONColor = new Color(105, 105, 105, 255);
    [SerializeField]
    private Color musicOffColor = new Color(50, 50, 50, 255);
    [SerializeField]
    private string musicONText = "Musik: An";
    [SerializeField]
    private string musicOffText = "Musik: Aus";
   

    private Image bgmImage;
    private Text bgmText;
    private AudioSource bgmAudioSource;
   
    private bool shouldPlayBackgroundMusic = false;
    private bool shouldPlaySFX = false;

    private const string MUSIC_PREFS_KEY = "bgmOnOff";
    private const int ON = 1;
    private const int OFF = 0;
   
    private void Awake()
    {
        if (!PlayerPrefs.HasKey(MUSIC_PREFS_KEY)) PlayerPrefs.SetInt(MUSIC_PREFS_KEY, ON);
        shouldPlayBackgroundMusic = PlayerPrefs.GetInt(MUSIC_PREFS_KEY, ON) == ON;
        bgmAudioSource = mainTheme.GetComponent<AudioSource>();
        bgmImage = bgm.GetComponent<Image>();
        bgmText = bgm.GetComponentInChildren<Text>();
       
        UpdateBackgroundMusic();
    }

    private void OnEnable() => bgm.onClick.AddListener(ToggleMusic);
    private void OnDisable()
    {
        bgm.onClick.RemoveListener(ToggleMusic);
        PlayerPrefs.Save();
    }

    public void ToggleMusic()
    {
        shouldPlayBackgroundMusic = !shouldPlayBackgroundMusic;
        UpdateBackgroundMusic();
    }

    private void UpdateBackgroundMusic()
    {
        PlayerPrefs.SetInt(MUSIC_PREFS_KEY, shouldPlayBackgroundMusic ? ON : OFF);
        bgmImage.color = shouldPlayBackgroundMusic ? musicONColor : musicOffColor;
        bgmText.text = shouldPlayBackgroundMusic ? musicONText : musicOffText;
        if (shouldPlayBackgroundMusic)
        {
            bgmAudioSource.Play();
            return;
        }
        bgmAudioSource.Stop();
    }
}

Disclaimer: I haven’t run it, since there are stuff I don’t have and I was lazy. But in theory it should work.

Yeah, maybe not the best name, but for now it’s good :smile:
The logic should be correct. If the variable is true, text says “Music: On” and bgm starts playing, the opposite with false.
If you click the button, BGMControl is called with the reversed state.
Everything should be fine, but still, if the PlayerPref is false at the beginning, the bgm starts playing, although when you go into options, the button is dark and says “Music: Off”.
So somehow the bgm still gets started…maybe somehow outside this script, but the only way to do this would be PlayOnAwake in the inspector, but it is unchecked.

Yeah, you’re right, didn’t think of that :slight_smile:

It does save automatically. I tested it with a Debug.Log.
At first startup the Pref gets set to “true”. Ingame i then press the button, it gets set to “false”.
Then i stop the game and restart it and the value in the Pref is still “false”, but somehow the bgm still starts.

Omg, i just found the problem. If i hadn’t deleted the Method SFXControl you guys would probably have found it earlier :smile:

The problem was, i first created BGMControl, and since SFXControl is basically the same, i copy-pasted it…which still had the audioSource.Play and Stop in it. :roll_eyes:

I figured it out because out of frustration i pressed the sfx button ingame and the music also stopped. I wondered and then saw in the code the stupid thing i did :smile:

Thanks for your help, though, guys!

Thanks for your answer. I already solved the problem (see the post above), but at least your code gave me some insight on how to optimize and slim down my code :wink:

1 Like

Good to know that! Thanks!

1 Like