Reputation: 223
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
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
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