Unity - 持久化检查员添加的引用

Unity - persistence with inspector-added references

我有一个持久的game object,我基本上用它来初始化所有东西。我称之为 Persistence 的脚本有一些 public 引用,我只是通过检查器拖到它们上面。 我正在努力让它像我在网上找到的那样持久:

public static Persistence instance;
void Awake()
{
    if (instance != null)
    {
        DestroyImmediate(gameObject);
    }
    else
    {
        DontDestroyOnLoad(gameObject);
        instance = this;
    }
}

问题是,当我从主菜单加载关卡时,没问题。当我从该级别加载主菜单时,它显示

MissingReferenceException: The object of type 'Persistence' has been destroyed but you are still trying to access it.

所以我决定让它在加载主菜单时创建其他实例,但这会弄乱依赖此数据的游戏级别上的所有脚本。

我的问题是,如果我有检查员添加的引用,如何在 Unity 中正确实现这种 ~singleton 持久模式?

注意 我只从我的 class 中的 Awake 函数初始化,而不是从其他任何地方初始化。从字面上看,我的 InitializeMe 脚本是从 Persistence class 一个接一个地调用的。

我应该做些什么不同的事情来完成这项工作?从不同的非持久 gameobject 初始化?忘了拖到检查器?任何使这项工作的建议都值得赞赏。

我让它工作了。所以在 Unity 中使用持久性之前,人们应该知道(我应该知道)的事情:

You don't drag references to a persistent GO via the inspector as they are gone the instant another scene is loaded except for additive loading. Also the persistent GO may hold data relevant to multiple scenes, hold functionality that is built for general purpose, as reusable as it goes, but it never initializes anything itself or interfere with non-persistent GOs other than being called - used as a tool - or providing the data.

虽然这是我的奥秘智慧,所以任何更熟练的人阅读这篇文章,如果我错了,请在其他人记住之前纠正我。

所以准确地说,我制作了一个非持久性主控 INITIALIZER 来做这件事。我将所有场景 GO 放入一个集合 一次 并且没有再次 GameObject.Find 。我使用 LINQ 查询来方便地从该集合中过滤我的结果。另一件事是我的 InitializeMe 抽象 class (或者更准确地说,它的后代)。

public abstract class InitializeMe : MonoBehaviour
{
    public int orderNumber;
    public abstract void INIT(INITIALIZER init);
}

此处 orderNumber 用于确定初始化的顺序,如果一个对象依赖于另一个对象。还值得一提的是,这样做可以产生一种 非常 可预测的设置方式,因为它是一个接一个地完成的。请注意,这不会造成瓶颈,因为 Unity 的脚本 API 仅在主线程中执行 - 将代码分成多个 AwakeStart 方法不会对我的表现更好截至 2017 年的最佳知识。INITIALIZER 中的 LINQ 查询使用它。 INITIALIZER 中的好处是 - 再一次 - 它拥有自己 一切 ,包括对持久对象和保存数据的引用,因此通过引用自身到 InitializeMe 方法,他们可以做他们需要的一切——因为 DataBank 提供了具有持久数据的通用工具,而 INITIALIZER 提供了易失性数据(希望我用对了,我的意思是只与主菜单相关)数据具有一些仅用于主菜单的附加功能。

现在,持久性不是由持久性 class 本身确保的,而是由 Awake 中的 INITIALIZER 确保的,如下所示:

//find by tag or rename upon instantiation as by doing so will result in a "(Clone)" added to its name.
var d = GameObject.FindGameObjectWithTag("DataBank");
if (d == null)
{
    //instantiating it
    DATA = GameObject.Instantiate<DataBank>(DATA_blueprint);
    //intializing the databank itself. The parameters are needed for me,
    // but serve as a good example - all of those are dragged from the inspector to the _INITIALIZER_ but will remain in the whole game as long as databank exists.
    DATA.LoadDataFromINIT(_baseColor, _baseColor2, _outlineColor,
           new MMtoolBundle(DATA));
    //to make it persistent.
    DontDestroyOnLoad(DATA);
}else
{
    //in this case the main menu is loaded from another scene.
    //so we only find that loaded object and get its reference, no initialization as it was already setup.
    this.DATA = d.GetComponent<DataBank>();
}

其中DATA是我的执着DataBank。请注意,默认情况下,我在场景中没有 NO DataBank 对象。 DATA_blueprint是inspector拖拽的DataBank的prefab(因为INITIALIZER不是持久化的)。它也可以通过 AssetDatabase 加载,但这更方便一些。

值得一提的是,虽然 DataBank 本身是一个 MonoBehaviour,所以它可以出现在 Unity 场景中,但它的 none 个成员是 MonoBehaviour,所以有可能去驾驭传承的力量。如果有人希望在非单声道工具包中启动 coroutine,则必须从 DataBank 本身的引用开始。

这个方法一个明显的缺点就是我的持久化GO的存在依赖于INITIALIZER,所以必须在第一个场景。但是同样,由于大多数游戏都是从主菜单开始的,所以这应该不是什么大问题(谈论单人游戏)。

YET AGAIN 我会推荐更熟练的人来纠正我,以防我误导其他人,但这个解决方案对我有用 - 在我所有的过程中得到了相同且唯一持久的 GO从未创造过的场景。