带有C#的Unity 5.3 - 打开/关闭声音

时间:2016-12-15 07:29:32

标签: c# audio unity5

我正在使用Unity 5.3和C#作为代码编辑器

这些就是我所拥有的:

我的项目中有2个场景:homeoptions。我在两个场景都有bg个对象。这两个bg个对象都有Audio Source个组件,其中包含play on awake相同的背景音乐。我没有对这些背景音乐使用任何代码,我只点击Unity中的Add Component按钮并添加Audio Source

这就是我想要的:

Options场景可以为所有场景打开/关闭背景音乐。因此,btnOn场景中有btnOffOptions

这是我在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;
    }
}

这就是问题:

我有这个错误:An object reference is required to access non-static member UnityEngine.AudioSource.volume。也许,这是因为我没有在我的代码中写public AudioSource audioSource。但是,如果我写这个,我必须在Get Component框中添加另一个音频,我将在一个场景中加倍Audio Source。我该怎么办?谢谢

2 个答案:

答案 0 :(得分:2)

由于您在两个场景中都有bg个对象,并且AudioManager未被标记为[DontDestroyOnLoad](我假设您还有一个AudioManager两个场景),每次加载场景时它都会触发Start()功能。

因此,您可以声明private AudioSource并通过在场景中找到bg对象并在其上调用GetComponent来获取它的引用:

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;
    }
}

答案 1 :(得分:1)

引发错误是因为您需要在单个对象上分配属性;卷之间不共享卷。因此,您需要在检查器中指定一个字段,或者使用GetComponent获取参考。

虽然使用不同的场景来处理选项并没有错,但它有点笨拙;必须卸载当前场景(销毁所有未标记为DontDestroyOnLoad的对象和与之关联的信息),然后加载选项,然后加载前一场景。虽然卸载音乐很可能会停止播放,但在加载后,会再次从头开始播放。不提及这些对象的任何设置都会丢失(音量,音轨变化等)。

前面提到的DontDestroyOnLoad可以帮助您,因为您对同一个对象进行了更改,但是每次加载场景时您都必须处理重复项...这样的对象存在...您可以使用该函数OnLevelWasLoaded(文档目前缺乏一点)作为确定要销毁哪些对象的时刻。

另一点是您目前拥有公开的Button字段。这允许通过检查器分配它们,但是在Start中覆盖它们时相当没有意义(新值是分配给同一对象的第一个按钮组件)。我会将这些字段设为私有,或者至少确保它们未被分配。但我稍微谈谈如何在场景之间保持设置的持续性。

这里有一些代码可以给你一个想法,但要注意它是未经测试的,因为我目前无法访问测试它的环境。此解决方案使用在场景之间持久的对象。请记住,在加载另一个场景后,通过编辑器建立到此对象的任何连接都将丢失。

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();
  }     
}