如何在Unity中创建通用的单例类?

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

我是 Unity 的初学者。

我在学习时遇到一个问题。

我参考了下面的文档。

using UnityEngine;

public class Singleton<T> : MonoBehaviour where T : MonoBehaviour
{
    private static T _instance;

    private static object _lock = new object();

    public static T Instance
    {
        get
        {
            if (applicationIsQuitting) {
                Debug.LogWarning("[Singleton] Instance '"+ typeof(T) +
                    "' already destroyed on application quit." +
                    " Won't create again - returning null.");
                return null;
            }

            lock(_lock)
            {
                if (_instance == null)
                {
                    _instance = (T) FindObjectOfType(typeof(T));

                    if ( FindObjectsOfType(typeof(T)).Length > 1 )
                    {
                        Debug.LogError("[Singleton] Something went really wrong " +
                            " - there should never be more than 1 singleton!" +
                            " Reopening the scene might fix it.");
                        return _instance;
                    }

                    if (_instance == null)
                    {
                        GameObject singleton = new GameObject();
                        _instance = singleton.AddComponent<T>();
                        singleton.name = "(singleton) "+ typeof(T).ToString();

                        DontDestroyOnLoad(singleton);

                        Debug.Log("[Singleton] An instance of " + typeof(T) + 
                            " is needed in the scene, so '" + singleton +
                            "' was created with DontDestroyOnLoad.");
                    } else {
                        Debug.Log("[Singleton] Using instance already created: " +
                            _instance.gameObject.name);
                    }
                }

                return _instance;
            }
        }
    }

    private static bool applicationIsQuitting = false;

    public void OnDestroy () {
        applicationIsQuitting = true;
    }
}

如果实例为空,为什么要将 GameObject 添加到 AddComponent ?

为什么使用 FindObject 函数?

以及为什么在 Unity 中使用 Singleton?

我不知道Singleton总体流程..

请给我代码审查..

作为初学者,我了解的不多。我需要你的帮助。

请给我你的想法。

c# generics unity-game-engine singleton
2个回答
4
投票

如果实例为空,为什么要将 GameObject 添加到 AddComponent ?

如果您要创建的脚本实例为空

if (_instance == null)
:

1.创建新的游戏对象

GameObject singleton = new GameObject();

2。创建该脚本的新实例并将其附加到上面创建的游戏对象。在 Unity 中,组件必须附加到 GameObject。

AddComponent
函数用于将组件附加到游戏对象。

_instance = singleton.AddComponent<T>();

为什么要使用 FindObject 函数?

如果您要创建的脚本实例为空

if (_instance == null)
,请检查该脚本实例是否已存在于场景中。
FindObjectOfType
函数仅用于查找此类脚本。假设我们有一个名为
SceneLoader
的脚本,并且将
SceneLoader
传递给
Singleton
类,它将检查
SceneLoader
的实例是否已存在于场景中并返回该实例。如果不存在则返回
null

为什么在Unity中使用Singleton?

当您只想在场景中拥有一个某种脚本类型的实例时,可以使用它。另外,使用

DontDestroyOnLoad
意味着即使加载下一个场景,该实例仍然存在。它不会像其他脚本一样被破坏。

请给我代码审查

您可以在 codereview 网站上请求代码改进。如果您是 Unity 新手,您可以在他们的网站上找到 Unity 项目教程,让您轻松入门这里


0
投票

我认为我有一个优雅的解决方案来解决这个问题(通用单例):

public static class Singleton
{
    /************************************************************/
    #region Functions

    public static T Get<T>(
        bool findObjectOfType = false, 
        bool dontDestroyOnLoad = false, 
        bool unparentGameObject = false) where T : MonoBehaviour
    {
        if (findObjectOfType && SingletonInstance<T>.instance == null) 
        {
            TrySet(Object.FindObjectOfType<T>(), dontDestroyOnLoad);
            LogManager.Log($"called Get<{typeof(T)}>() before instance was set; calling FindObjectOfType<{typeof(T)}>");
        }
        return SingletonInstance<T>.instance;
    }

    public static bool TrySet<T>(
        T instance, 
        bool dontDestroyOnLoad = false, 
        bool unparentGameObject = false) where T : MonoBehaviour
    {
        // NOTE: method does not need to be called; BUT if called, FindObjectOfType() is avoided during lazy init
        if (SingletonInstance<T>.instance != null)
        {
            LogManager.Log($"{instance.name} called Set<{typeof(T)}>() when singleton already exists");
            if (!IsSingleton(instance)) 
            {
                LogManager.Log($"there are two different singleton instances, calling DestroyImmediate for {instance.name}");
                Object.DestroyImmediate(instance.gameObject);
            }
            return false;
        }
        else if (instance != null)
        {
            SingletonInstance<T>.instance = instance;
            if (unparentGameObject) instance.transform.SetParent(null);
            if (dontDestroyOnLoad) Object.DontDestroyOnLoad(instance.gameObject);
            return true;
        }
        else
        {
            LogManager.LogError($"called Set<{typeof(T)}>() when given instance is null");
            return false;
        }
    }

    public static bool IsSingleton<T>(T instance) where T : MonoBehaviour
    {
        return ReferenceEquals(SingletonInstance<T>.instance, instance);
    }

    #endregion
    /************************************************************/
    #region Subclasses

    private static class SingletonInstance<T> where T : MonoBehaviour
    {
        public static T instance;
    }

    #endregion
    /************************************************************/
}

其中具有/是

singleton
class 看起来像:

    /************************************************************/
    #region Properties

    public static TestRunner Instance => Singleton.Get<TestRunner>(findObjectOfType: true);

    #endregion
    /************************************************************/
    #region Functions

    private void Awake() => Singleton.TrySet(this, dontDestroyOnLoad);

    #endregion
    /************************************************************/

你可以创建一个 MonoSingleton 类,如下所示:

public abstract class MonoSingleton<T> : MonoBehaviour where T : MonoBehaviour
{
    /************************************************************/
    #region Fields

    [Header("Singleton Settings")]
    [Tooltip("whether Object.DontDestroyOnLoad() is called on this")]
    [SerializeField] private bool dontDestroyOnLoad;
    [Tooltip("whether this GameObject unparents itself")]
    [SerializeField] private bool unparentGameObject;

    #endregion
    /************************************************************/
    #region Properties

    private T _Instance => this as T;
    public static T Instance => Singleton.Get<T>();

    #endregion
    /************************************************************/
    #region Functions

    protected void Awake() 
    {
        if (Singleton.TrySet(_Instance, dontDestroyOnLoad, unparentGameObject))
        {
            MonoSingleton_Awake();
        }
    }
    
    protected void OnDestroy()
    {
        if (Singleton.IsSingleton(_Instance))
        {
            MonoSingleton_OnDestroy();
        }
    }

    protected void OnEnable() 
    {
        if (Singleton.IsSingleton(_Instance))
        {
            MonoSingleton_OnEnable();
        }
    }
    
    protected void OnDisable() 
    {
        if (Singleton.IsSingleton(_Instance))
        {
            MonoSingleton_OnDisable();
        }
    }
    
    protected virtual void MonoSingleton_Awake() {}
    
    protected virtual void MonoSingleton_OnEnable() {}

    protected virtual void MonoSingleton_OnDisable() {}

    protected virtual void MonoSingleton_OnDestroy() {}

    #endregion
    /************************************************************/
}
© www.soinside.com 2019 - 2024. All rights reserved.