允许 gameObject 在不使用 Singleton 的情况下访问巨大的数据字典?

Allow gameObject to access a gigantic Dictionary of data without using a Singleton?

我在 Unity 中有一个游戏对象,我希望能够访问巨大的数据字典中的随机条目。

我目前通过对包含字典和 getter:

的游戏对象使用单例模式来使其工作
public class VectorData : MonoBehaviour
{
    // create singleton
    public static VectorData i { get; private set; }
    private void Awake()
    {
        i = this;
    }

    // gigantic Dictionary containing Lists of Vector2's (real project will have hundreds of entries)
    Dictionary<string, List<Vector2>> dataDictionary = new Dictionary<string, List<Vector2>>()
    {
      {"dataSet1", new List<Vector2> {new Vector2(-1, 6), new Vector2(0, 6), new Vector2(1, 6)} },
      {"dataSet2", new List<Vector2> {new Vector2(-7, 6), new Vector2(-6, 6), new Vector2(-5, 6)} }
    };

    // getter
    public Dictionary<string, List<Vector2>> DataDictionary
    {
        get => dataDictionary;
    }

}

另一个 gameObject 将通过访问它来获取该数据:

public class CoolObject : MonoBehavior
{
    public List<Vector2> vectorInfo;

    private void Awake()
    {
       // get a List of Vectors from the VectorData singleton
        vectorInfo = VectorData.i.BattleDictionary["dataSet2"];
    }

} // end CoolObject Class

但由于只有 CoolObject 需要访问此词典,使用全局可访问的单例似乎有点过分了。

所以我的问题是,让对象访问像这样的巨大字典(或任何大型数据集)的最佳和最高效的方法是什么?

我考虑的事情:

我对 Unity 还是很陌生,所以我真的在努力养成良好的习惯和技巧。非常感谢这里的任何帮助!

一些想法。

您将变量设为静态,这与创建单例不同。单例模式涉及一个唯一的静态实例,但你所做的我称之为使变量静态。

您无需将变量设为静态即可从外部授予访问权限。 属性 和 public getter 可以。

在 c# 中将变量设置为静态意味着 属性 值在所有 VectorData 个实例(如果超过一个)中都相同。将其设为 class 属性 而不是实例 属性,因此是 class 模板本身的一部分。

关于第 1 点和第 2 点,class 中的代码量与数据结构的大小无关。逻辑或架构将根据可读性和逻辑功能编写,而不是数据结构大小使用。

要点3。检查 docs 中可编写脚本对象的使用情况。大量的原始数据是可以的,因为你在 class 变量中有它。

Point4 单例模式或使 属性 静态对我来说似乎有点过分了。要访问一个 class 属性,public get 是可以的,或者一个 public 变量。

最后,当传递 class 个实例时,您不必担心由于大量数据而导致的性能问题,因为 class 个实例是通过引用传递的,因此您不会制作任何副本你的大字典。

希望这是有道理的:)

我决定现在将我的评论转换为答案,以便能够更好地编写它并提供更多解释;)

参考你提到的几点

The object itself could obviously store the data, but this Dictionary is so gigantic that it will be unruly inside of any script that has any other code in it.

这有点基于意见。如果该数据属于某个 class 而其他 class 不应访问它,我不明白为什么它不应该存在于 class 中,即使那里有更多代码。如果您担心可读性,您仍然可以简单地将其包装在可折叠的 #region or use a partial class.

The same applies for creating a parent class to pass this information down (CoolObject already inherits)

好吧,这 可以 通过例如一个

public abstract class VectorDataMonoBehaviour : MonoBehaviour
{
    protected readonly Dictionary....
}

而不是让你的另一个 class 从中派生出来

public class CoolObject : MonoBehaviour 
{
    ... 
}

但是让我们面对现实吧..那是多么不可思议!每次你还想为特定的 class 使用另一种数据类型时,你会再次被搞砸 ;)

I know Scriptable Objects are often used for data, but from my understanding they are mostly used as a base to create new objects from?

是的,如果您只想存在这样的资产之一,那么使用 ScriptableObject 没有多大意义。 Except 如果您希望能够轻松地切换出具有不同值的数据而无需重新编译您的应用程序!在这种情况下,ScriptableObject 将是 要使用的东西!

Maybe I'm completely wrong and a Singleton is actually the best approach here?

在这种情况下,我会说 不! 相反。无论如何,Singleton 是很有争议的,但是 如果 你使用它,它会带来一些巨大的缺点:

  • 您需要依赖正在初始化的 Singleton 实例 => 在 Awake 被调用时 => 您始终必须确保这样的实例实际存在于您的场景中,处于活动状态并已启用,并且不会'即使改变场景也不会被破坏(或者你每次都必须重新初始化)。
  • 在任何尝试访问 Singleton 实例
  • 之前,
  • 你也会依赖于这种类型的 Awake 被调用

我是你的用例而不是考虑单例你可以简单地制作字典本身static which makes it a part of the type itself and not anymore bound to a certain instance of it. See also Static classes and static members

public class VectorData : MonoBehaviour
{    
    //gigantic Dictionary containing Lists of Vector2's (real project will have hundreds of entries)
    private static Dictionary<string, List<Vector2>> dataDictionary = new Dictionary<string, List<Vector2>>()
    {
      {"dataSet1", new List<Vector2> {new Vector2(-1, 6), new Vector2(0, 6), new Vector2(1, 6)} },
      {"dataSet2", new List<Vector2> {new Vector2(-7, 6), new Vector2(-6, 6), new Vector2(-5, 6)} }
    };

    // getter
    public static Dictionary<string, List<Vector2>> DataDictionary => dataDictionary;
}

这样您就不会依赖场景中存在的任何实例、活动实例和启用实例。您也不依赖于订单,并且已经为此实例调用了 that/if Awake

不,相反 dataDictionary 在您的应用程序的整个生命周期中已经存在。


但是,由于您的 class 现在似乎没有任何基于实例的内容,也没有与特定 GameObject 相关的内容,因此值得怀疑的是为什么它应该是 MonoBehaviour

如果 class 只有 static 个成员,则使整个 class static

public static class VectorData
{    
    // Note if your property is doing nothing else you could as well
    // simply make the field readonly itself
    public static readonly Dictionary<string, List<Vector2>> DataDictionary = new Dictionary<string, List<Vector2>>()
    {
      {"dataSet1", new List<Vector2> {new Vector2(-1, 6), new Vector2(0, 6), new Vector2(1, 6)} },
      {"dataSet2", new List<Vector2> {new Vector2(-7, 6), new Vector2(-6, 6), new Vector2(-5, 6)} }
    };
}

这样它就不能附加到任何GameObject。它只是“存在于”您的 Assets 文件夹中。


另一个提示,如果您真的只想对数据进行硬编码,然后任何人都无法修改它,您可能更愿意使用 IReadonlyDictionary and let it store IReadonlyList,例如

public static class VectorData
{    
    public static readonly IReadonlyDictionary<string, IReadonlyList<Vector2>> DataDictionary = new Dictionary<string, IReadonlyList<Vector2>>()
    {
      {"dataSet1", new List<Vector2> {new Vector2(-1, 6), new Vector2(0, 6), new Vector2(1, 6)} },
      {"dataSet2", new List<Vector2> {new Vector2(-7, 6), new Vector2(-6, 6), new Vector2(-5, 6)} }
    };
}

这是可能的,因为 Dictionary 实现了 IReadonlyDictionaryList 实现了 IReadonlyList

这样你就可以在不可变的数据提供者中转换它,没有人可以修改它的任何内容。