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;
}
所以我有一个基本的单例,可以跨场景处理音频。
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;
}