Unity Audio Singleton 跨场景

Unity Audio Singleton Across Scenes

所以我有一个基本的单例,可以跨场景处理音频。

    private void Awake() {
        if (Instance == null) {
            Instance = this;
        } else {
            Destroy(gameObject);
            return;
        }

        DontDestroyOnLoad(gameObject);
    }

假设这个音频单例放在 Scene1 中,如果我切换到 Scene2 它似乎可以工作。唯一的问题是,如果我从 Scene2 开始,音频将不再有效。我猜这是因为单例仅在 Scene1 中创建,因此 Scene2 中没有单例引用。我已经尝试将我的单身人士制作成预制件,这样我就可以在我的每个场景中都有它们,这解决了我在 Scene2 中没有现有单身人士的问题,但如果我切换场景,它就会停止工作。

有没有办法让我的音频单例即使不从 Scene1 开始也能正常工作?我想它不一定是单身人士,但这就是我到目前为止所拥有的。我是 Unity 的新手,所以我一直在查找基本的 Unity 教程。

编辑:我想从Scene2开始的原因是因为我想测试特定的场景。

您可能会使用惰性实例化,例如

[RequireComponent(typeof(AudioSource))]
public class CentralAudio : MonoBehaviour
{
    [SerializeField] private AudioSource _source;

    public AudioSource Source => _source;

    private static CentralAudio _instance;
    
    public static CentralAudio Instance
    {
        get
        {
            // instance already exists -> return it right away
            if(_instance) return _instance;

            // look in the scene for one
            _instance = FindObjectOfType<CentralAudio>();

            // found one -> return it
            if(_instance) return _instance;

            // otherwise create a new one from scratch
            _instance = new GameObject(nameof(CentralAudio), typeof(AudioSource)).AddComponent<CentralAudio>();
        }
    }
    
    private void Awake()
    {
        // ensure singleton
        if(_instance && _instance != this)
        {
            Destroy(gameObject);
            return;
        }

        _instance = this;

        // lazy initialization of AudioSource component
        if(!_source) 
        {
            if(!TryGetComponent<AudioSource>(out _source))
            {
                _source = gameObject.AddComponent<AudioSource>();
            }
        }

        DontDestroyOnLoad(gameObject);
    }
}

现在您可以使用例如

CentralAudio.Instance.Source.PlayOneShot(someAudioClip);

并且第一次会按需创建实例。

您可以使用下面我的单例模板。声明 class 时使用“public class MyClass : Singleton<MyClass>”。您需要做的是拥有一个像 Bootstrap 或 Startup 这样的场景,这是您放置单例游戏对象的地方。他们应该使用一些与 derHugo 发布的非常相似的标准单例代码,除了如果找不到组件我不会实例化它——它应该在日志中显示错误。通常我的管理器单例有有用的属性和数组,这些属性和数组在检查器中设置,所以只创建一个这样的组件会失去所有的功能。 拥有 Bootstrap 或 Startup 场景后,将其移至构建场景列表中加载顺序的顶部。您应该在 Startup 场景中还有另一个单例游戏对象,然后加载 Scene1 或 Scene2 或您需要的任何内容。我制作了一个名为 GameManager 的单例,它有一个状态机并确定要加载的场景并知道我们在哪里。 通常它会在一个 GameUI 场景或多个 UI 场景中加载,您可以加载它们。这样你就可以将游戏和 UI 分解成多个场景进行组织。在拥有多个场景的团队中工作时,协作对于协作也很重要,因为它们往往不容易合并。很多时候人们想要一个 Intro 场景,所以 GameManager 将有不同的状态并在它们之间移动以加载不同的场景。不过不要调用单例 SceneManager,Unity 已经有一个 class 以这种方式命名。

using UnityEngine;

public class Singleton<T> : MonoBehaviour where T: MonoBehaviour
{
    protected virtual void Awake()
    {
        if (instance != null)
        {
            Debug.LogError($"Duplicate Singleton: {name} of type {typeof(T)}, destroying self");
            GameObject.DestroyImmediate(gameObject);
        }
        else
            instance = gameObject.GetComponent<T>();
    }

    static bool doneOnce;

    /// <summary>
    /// Returns the instance of this singleton.
    /// </summary>
    public static T Instance
    {
        [System.Diagnostics.DebuggerStepThrough]
        get
        {
            if (instance == null)
            {
                instance = (T)GameObject.FindObjectOfType(typeof(T)); // not really any point to doing this (it will fail), Awake would have happened but it's possible your code got here before Awake

                if (instance == null && !doneOnce)
                {
                    doneOnce = true;
                    Debug.LogError($"!!! An instance of type {typeof(T)} is needed in the scene, but there is none !!!");
                }
            }

            return instance;
        }
    }

    private static T instance;
}