如何解决Unity:域重载后静态引用消失编辑器重载
public class SingletonTest : MonoBehavIoUr
{
public static SingletonTest Singleton = null;
public int Value = 42;
void Start() {
Singleton = this;
}
}
这起初运行良好,但我的问题是,当我编辑脚本然后单击返回 Unity 编辑器/IDE 时,我会得到一整串 NullReferenceException
来表示 SingletonTest.Singleton.Value
在其他班级。
我用谷歌搜索了一下,这个编辑器重新加载似乎触发了一个名为 Domain Reloading (see also) 的过程。 显然域重新加载也会重置所有静态字段,这解释了我的错误消息。我尝试了该页面上建议的一些解决方法,但都没有奏效:
using UnityEngine;
public class SingletonTest : MonoBehavIoUr
{
public static SingletonTest Singleton = null;
public int Value = 42;
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.SubsystemRegistration)]
static void StaticStart()
{
var arr = FindobjectsOfType<SingletonTest>();
Debug.Log($"Len arr: {arr.Length}"); // This is 0! :(
if (arr.Length > 0)
Singleton = arr[0];
}
void Start()
{
Singleton = this;
}
void OnAfterDeserialize()
{
Singleton = this;
}
}
我可以通过将 Singleton = this
放在 Update()
函数中来使其工作,但该解决方案很难看,并且在之后的第一帧仍然给我一个 NullReferenceException
重新加载。
(请不要建议将 Value
字段设为静态,这只是一个简化示例,我确实需要/想要一个单例类)
解决方法
当 CLR 创建 SingletonTest
对象时,考虑使用私有构造函数来强制静态字段初始化为一个值。
尽管 Unity 通常不建议使用带有 MonoBehvior
的构造函数,因为它们应该是脚本(以及其他 Mono 和 Unity 怪癖)。我发现这非常适合我的单例用例(例如静态编辑器 Dictionary<T>
保存加载的元数据等...
public class SingletonTest : MonoBehaviour
{
public static SingletonTest Singleton { get; set; } = null;
public int Value = 42;
protected SingletonTest()
{
Singleton ??= this;
}
}
或者考虑避免给定字段/属性永远不会为空的假设。
例如:
void Awake()
{
// call method example ↓ (null-coalescing operator)
SingletonTest.Singleton?.Invoke("Something");
// property example
if(SingletonTest.Singleton != null)
{
Debug.Log($"{SingletonTest.Singleton.gameObject.Name}");
}
}
,
首先:到目前为止,您还没有实现任何单例。
顾名思义,“Singleton”的主要概念是确保存在一个单一实例。在您的代码中,如果另一个实例覆盖实例字段,则无法控制。
Singleton 只会被滥用以获取公共访问权限。
您拥有它的方式也很危险:任何其他脚本都可以简单地将字段设置为其他内容。
何时/如果我做这样的事情我通常会通过属性使用延迟初始化,例如
public class SingletonTest : MonoBehaviour
{
// Here you store the actual instance
private static SingletonTest _instance;
// Only allow read access for others
public static SingletonTest Instance
{
get
{
// If instance is assigned and exists return it right away
if(_instance) return _instance;
// Otherwise try to find one in the scene
_instance = FindObjectOfType<SingletonTest>();
// Found one? Nice return it
// this would only happen if this getter is called before the
// Awake had its chance to set the instance
if(_instance) return _instance;
// Otherwise create an instance now
// this will only happen if you forgot to put one into your scene
// it is not active/enabled
// or it was destroyed for some reason
_instance = new GameObject("SingletonTest").AddComponent<SingletonTest>();
return _instance;
}
}
public int Value = 42;
private void Awake()
{
// If another instance exists already then destroy this one
// This is the actual Singleton Pattern
if(_instance && _instance != this)
{
Destroy(gameObject);
return;
}
_instance = this;
// Optional: also make sure this instance survives any scene changes
DontDestroyOnLoad(gameObject);
}
}
然后如果你在编辑器中也需要这样的东西,而不是在播放模式/运行时你可以使用 [InitializeOnLoadMethod]
(NOT [RuntimeInitializeOnLoadMethodAttribute]
顾名思义是用于运行时/PlayMode)。
当然你可以同时拥有
#if UNITY_EDITOR
[UnityEditor.InitializeOnLoadMethod]
#endif
[RuntimeInitializeOnLoadMethodAttribute]
private static void Init()
{
Debug.Log("Initialized",Instance);
}
对 Instance
属性的简单访问已经确保它已初始化。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。