使用MonoBehavior构造函数的Unity3D Singleton。

问题描述 投票:0回答:4

我有几个 MonoBehavior 子类,需要成为一个 Singleton 然而,分配一个 Instance 财产 Awake() 是太晚了一些类和结果的比赛条件,所以我想知道是有什么发言反对分配的 Instance 在一个私人的c-tor中,像这样。

public class Foo: MonoBehaviour
{
    public static Foo Instance { get; private set; }

    private Foo()
    {
        Instance = this;
    }
}

或者这种方法有什么负面的副作用需要我注意?

c# unity3d singleton
4个回答
1
投票

你所使用的技术允许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;
    }
}

这是双重检查的锁定模式。 如果代码的访问量不大,或者性能不是问题,你可以使用常规的锁定。


2
投票

我同意Lorek的回答,但有一个问题。

你不应该使用MonoBehavior的构造函数,因为它本身就有不受欢迎的行为。因为它不会是一个特定的GameObject的一部分。所以你将不得不把它添加到 启动, 醒来开始 方法;或者创建一个新的类来包含你想要共享的逻辑。(新类不应该由MonoBehavior类扩展)

然后像Lorek上面描述的那样创建一个单体。

你也可以改变脚本执行顺序,以确保你的MonoBehavior需要作为一个 "单人 "在所有其他脚本之前被执行,然而,这将是必要的,这个MonoBehavior已经附着在场景中现有的GameObject上,而不是通过代码自动添加。

http:/docs.unity3d.comManualclass-criptExecution.html)。


1
投票

谢谢大家的意见!我最终想到了下面的方法。我最终想出了下面的方法,因为在我的情况下,这些单子是通过编辑器(从菜单)创建的,而单子应该是容器游戏对象上的组件。

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

当然它也不是完美的,因为它只依赖于对象名称。但它对我来说是可行的。


0
投票

如果你需要一个全局的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++;
© www.soinside.com 2019 - 2024. All rights reserved.