Grace Michelle
Grace Michelle

Reputation: 223

Unity 5.3 with C# - Toggle Sound On/Off

I'm working with Unity 5.3 with C# as code editor

These are what I have :

I have 2 scenes in my project : home and options. I have bg object on both scenes. Both bg objects have Audio Source component, which contains same background music that play on awake. I don't use any codes for these background musics, I only click the Add Component button from Unity and add Audio Source.

This is what I want :

Options scene can toggle the background music on/off for all scenes. Therefore, there are btnOn and btnOff in Options scene.

This is my code in Audio Manager.cs :

using UnityEngine;
using UnityEngine.UI;
using System.Collections;

public class AudioManager : MonoBehaviour {
    public Button btnOn;
    public Button btnOff;

    // Use this for initialization
    void Start () {
        btnOn = GetComponent<Button>();
        btnOff = GetComponent<Button>();

        btnOn.onClick.AddListener(() => PlayAudio());
        btnOff.onClick.AddListener(() => StopAudio());
    }

    void PlayAudio()
    {
        AudioSource.volume = 0.5f;
    }

    void StopAudio()
    {
        AudioSource.volume = 0f;
    }
}

This is the problem :

I have this error : An object reference is required to access non-static member UnityEngine.AudioSource.volume. Maybe, this is because I don't write public AudioSource audioSource in my code. But, if I write this, I have to add another audio in Get Component box, and I will have double Audio Source in one scene. What should I do? Thanks

Upvotes: 0

Views: 1391

Answers (2)

Yemachu
Yemachu

Reputation: 144

The error is raised because you need to assign the property on an individual object; the volume is not shared between sources. For this reason you will either need a field to assign in the inspector, or get a reference with GetComponent.

While using different scenes for handling options is not wrong, it is a tad clumsy; the current scene has to be unloaded (destroying all object not marked as DontDestroyOnLoad and information associated with them), after which the options are loaded, to then load the previous scene. While unloading the music most likely stops playing, which, after loading, starts at the beginning again. Not mention any settings on these objects are lost (volume, change of track, etc.).

The afore mentioned DontDestroyOnLoad can help since you make your changes on the same object, but you will have to deal with duplicates each time a scene is loaded where such an object exists... you can use the function OnLevelWasLoaded (documentation is a tad lacking at the moment) as a moment to determine which objects to destroy.

Another point is that you currently have public Button fields. This allows the for the assignment of them via the inspector, but that is rather moot as you overwrite them in Start (new value being the first button component assigned to the same object). I would make these fields private, or at least make sure they are not assigned. But I'm getting slightly of topic on how to keep settings persistent between scenes.

Here is some code to give you an idea, but be warned it is untested as I currently have no access to an environment to test it. This solution uses an object which is persistent between scenes. Keep in mind that any connections established to this object via the editor are lost after another scene has loaded.

public class AudioManager : MonoBehaviour
{
  private AudioSource source;

  // Called regardless of whether the object is enabled or not.
  // Should not be called in a new scene.
  private void Awake()
  {
    // Protect this object from being destroyed so its volume
    // is maintained between scenes.
    DontDestroyOnLoad(this.gameObject);
    source = GetComponent<AudioSource>();
  }    

  public void DestroyPossibleDuplicates()
  {
    AudioManager[] managers = FindObjectsOfType(typeof(AudioManager)) as AudioManager[];
    foreach (AudioManager manager in managers)
    {
      // Use something to determine whether a manager is a duplicate.
      // It must not be this, but have something in common; a name perhaps?
      if ((manager != this) && (manager.gameObject.name == this.gameObject.name))
      {
        // Destroy the duplicates so their sound won't interfere.
        Destroy(manager.gameObject);
      }
    }
  }

  private void BindToInterface()
  {
    Slider[] sliders = FindObjectsOfType(typeof(Slider)) as Slider[];
    foreach (Slider slider in sliders)
    {
      // Determine whether the specified slider should have effect on this object.
      // If the slider's name contains this object's name assume it should.
      if (slider.gameObject.name.indexOf(this.gameObject.name)!=-1)
      {
        slider.onValueChanged.addListener((float value)=>
        {
          // In this case a slider is used for more control over the volume.
          // Different elements require other logic to function.
          source.volume = value;
        });
      }
    }
  }

  // If my memory serves correct this method is only called on objects 
  // that were in the scene before it started loading.
  // Just to be safe, don't have it do anything depending on this difference.
  private void OnLevelWasLoaded()
  {
    DestroyPossibleDuplicates();
    BindToInterface();
  }     
}

Upvotes: 1

Whatever
Whatever

Reputation: 291

As you have a bg object in both scenes and as your AudioManager isn't marked as [DontDestroyOnLoad] (then I assume you also have one AudioManager in both scenes), it's Start() function will fire each time you load a scene.

So you can declare a private AudioSource and get a reference of it by finding your bg object in your scene and calling GetComponent on it :

using UnityEngine;
using UnityEngine.UI;
using System.Collections;

public class AudioManager : MonoBehaviour
{
    public Button btnOn;
    public Button btnOff;

    private AudioSource audioSource;

    // Use this for initialization
    void Start()
    {
        btnOn = GetComponent<Button>();
        btnOff = GetComponent<Button>();

        btnOn.onClick.AddListener(() => PlayAudio());
        btnOff.onClick.AddListener(() => StopAudio());

        audioSource = GameObject.Find("bg").GetComponent<AudioSource>();
    }

    void PlayAudio()
    {
        audioSource.volume = 0.5f;
    }

    void StopAudio()
    {
        audioSource.volume = 0f;
    }
}

Upvotes: 2

Related Questions