我有几个 MonoBehavior
子类,需要成为一个 Singleton
然而,分配一个 Instance
财产 Awake()
是太晚了一些类和结果的比赛条件,所以我想知道是有什么发言反对分配的 Instance
在一个私人的c-tor中,像这样。
public class Foo: MonoBehaviour
{
public static Foo Instance { get; private set; }
private Foo()
{
Instance = this;
}
}
或者这种方法有什么负面的副作用需要我注意?
你所使用的技术允许Instance属性被设置不止一次,即使只是被同一个类的其他成员设置。 这是不可以的。 我会做这样的事情。
private static readonly Foo instance = new Foo();
public static Foo Instance
{
get
{
return instance;
}
}
这是一个简单的方法来确定单人变量只被设置一次。 如果你想要懒惰的实例化,你可以做这样的事情。
private static Foo instance = null;
public static Foo Instance
{
get
{
if (null == instance)
instance = new Foo();
return instance;
}
}
但是使用这种技术,你仍然可以向你的实例变量写不止一次。 所以,你需要确保你总是引用属性而不是变量。 而且如果这个属性要从多个线程访问,你要通过使用这样的临界部分来防止竞赛条件。
private static readonly object singletonSection = new object();
private static Foo instance = null;
public static Foo Instance
{
get
{
if (null == instance)
{
lock(singletonSection)
{
if (null == instance)
instance = new Foo();
}
}
return instance;
}
}
这是双重检查的锁定模式。 如果代码的访问量不大,或者性能不是问题,你可以使用常规的锁定。
我同意Lorek的回答,但有一个问题。
你不应该使用MonoBehavior的构造函数,因为它本身就有不受欢迎的行为。因为它不会是一个特定的GameObject的一部分。所以你将不得不把它添加到 启动, 醒来 或 开始 方法;或者创建一个新的类来包含你想要共享的逻辑。(新类不应该由MonoBehavior类扩展)
然后像Lorek上面描述的那样创建一个单体。
你也可以改变脚本执行顺序,以确保你的MonoBehavior需要作为一个 "单人 "在所有其他脚本之前被执行,然而,这将是必要的,这个MonoBehavior已经附着在场景中现有的GameObject上,而不是通过代码自动添加。
谢谢大家的意见!我最终想到了下面的方法。我最终想出了下面的方法,因为在我的情况下,这些单子是通过编辑器(从菜单)创建的,而单子应该是容器游戏对象上的组件。
public static T GetInstance<T>(string containerName) where T : Component
{
/* Find container or create if it doesn't exist. */
var container = GameObject.Find(containerName);
if (container == null) container = new GameObject(containerName);
/* Get existing instance or create new one if not found. */
return container.GetComponent<T>() ?? container.AddComponent<T>();
}
当然它也不是完美的,因为它只依赖于对象名称。但它对我来说是可行的。
如果你需要一个全局的MonoBehaviour脚本,MonoSingleton类是很有用的,可以从任何地方访问。这里是MonoSingleton类。
using UnityEngine;
public class MonoSingleton<T> where T : MonoBehaviour
{
private static T _instance;
private static bool isFound;
private bool createMissingInstance;
static MonoSingleton()
{
isFound = false;
_instance = null;
}
public MonoSingleton(bool createNewInstanceIfNeeded = true)
{
this.createMissingInstance = createNewInstanceIfNeeded;
}
public T Instance
{
get
{
if (isFound && _instance)
{
return _instance;
}
else
{
UnityEngine.Object[] objects = GameObject.FindObjectsOfType(typeof(T));
if (objects.Length > 0)
{
if (objects.Length > 1)
Debug.LogWarning(objects.Length + " " + typeof(T).Name + "s were found! Make sure to have only one at a time!");
isFound = true;
_instance = (T) System.Convert.ChangeType(objects[0], typeof(T));
return _instance;
}
else
{
Debug.LogError(typeof(T).Name + " script cannot be found in the scene!!!");
if (createMissingInstance)
{
GameObject newInstance = new GameObject(typeof(T).Name);
isFound = true;
_instance = newInstance.AddComponent<T>();
Debug.Log(typeof(T).Name + " was added to the root of the scene");
return _instance;
}
else
{
isFound = false;
return null; // or default(T)
}
}
}
}
}
}
然后在静态类中定义MonoSingletons。比如说。
public static class Global
{
public static MonoSingleton<GameProgress> progress = new MonoSingleton<GameProgress>(true); //true means that new GameObject with script GameProgress will be created if it is missing from the scene
public static MonoSingleton<CharacterController> characterController = new MonoSingleton<CharacterController>(false); //will return null if there is no character controller present in the scene.
}
然后简单地从任何脚本中访问全局MonoBehaviors!
Global.progress.Instance.score++;