为什么 Unity 忽略非静态公共字段的初始化值

本文关键字:字段 初始化 静态 Unity 为什么 | 更新日期: 2023-09-27 18:37:10

我正在使用InvokeRepeating()来调用游戏中的方法。我在其中一个GameObject类的Start()方法中调用InvokeRepeating()。为了设置InvokeRepeating()repeatRate参数,我正在向它传递一个名为secondsBetweenBombDrops的公共字段。

Unity 忽略我在代码中为 secondsBetweenBombDrops 指定的值,而是在没有静态修饰符的情况下声明secondsBetweenBombDrops时使用一些默认值(即 1):

public float secondsBetweenBombDrops = 10f;
void Start() {
    InvokeRepeating("DropBomb", 1f, secondsBetweenBombDrops);
}

但是,一旦我将 static 修饰符添加到 secondsBetweenBombDrops ,代码的行为符合预期,并使用正确的值 10:

public static float secondsBetweenBombDrops = 10f;
void Start() {
    InvokeRepeating("DropBomb", 1f, secondsBetweenBombDrops);
}

为什么此字段要求 static 修饰符使用适当的值?

在 Unity 检查器中,脚本组件显示secondsBetweenBombDrops为 1。无论我是在游戏启动时实例化预制件还是在游戏运行时创建预制件实例,此默认值 1 都存在。

为什么 Unity 忽略非静态公共字段的初始化值

序列化的双刃剑

Unity 希望让每个人都能更轻松,包括编码知识有限的人(初学者、设计师)。

为了帮助他们,Unity 会在检查器中显示数据。这允许编码人员进行编码,设计人员通过调整值进行设计,而无需打开 MonoDevelop/IDE。

有两种

方法可以在检查器中显示值:

public int myVar = 10;
[SerializeField] private int myOtherVar = 0; // Can also be protected

第二个更好,因为它符合封装原则(变量是私有/受保护的,并通过方法或属性进行修改)。

在编辑器中显示变量时,脚本中给出的值仅在拖动脚本时使用。然后,Unity 会序列化这些值,不再关心任何脚本修改。例如,如果在事后脚本中将myVar设置为 20,则不会使用它,这可能会导致混淆。序列化写入场景文件中。

示例中的两行的工作方式完全相同。

可能的解决方案

可以通过按脚本组件设置轮上的重置来让 Unity 考虑脚本中的新值。这也将重置组件的所有其他变量,因此仅在预期时才执行此操作。

将变量设为私有并省略属性[SerializeField]将禁用序列化过程,因此 Unity 将不再在场景文件中查找要显示的值 - 相反,该值将由脚本在运行时创建。

将组件添加到 Unity 时,将创建与组件类型相同的新对象。显示的值是该对象的序列化值。因此,只能显示成员值,而不能显示静态变量,因为它们不可序列化。(这是一个 .NET 规范,并非严格特定于 Unity。由于 Unity 不序列化静态字段,这就是为什么添加 static 修饰符似乎可以解决问题的原因。

解释OP

在 OP 案例中,根据注释,您的公共字段在编辑器中显示值 1。您认为此值是默认值,而实际上它是您在最初声明该字段时最有可能为该字段提供的值。将脚本添加为组件后,您创建了值 10,并认为它有问题,因为它仍在使用值 1。您现在应该明白它按照设计工作得很好。

Unity 序列化了什么?

默认情况下,Unity 将序列化和显示值类型(int、float、enum 等)以及字符串、数组、List 和 MonoBehavior。(可以使用编辑器脚本修改它们的外观,但这是题外话。

以下:

public class NonMonoBehaviourClass{
   public int myVar;
}

默认情况下不序列化。这里又是 .NET 规范。默认情况下,Unity 将单体行为序列化为引擎要求的一部分(这会将内容保存到场景文件中)。如果您希望在编辑器中显示"经典"类,只需这样说:

[System.Serializable]
public class NonMonoBehaviourClass{
   public int myVar = 10;
}

显然,您无法将其添加到游戏对象中,因此您需要在 MonoBehavior 中使用:

public class MyScript:MonoBehaviour{
     public NonMonoBehaviourClass obj = new NonMonoBehaviourClass();
}

这将在检查器中显示对象,并允许修改 NonMonoBehaviourClass 实例中的 myVar 变量。同样,在将值序列化并存储到场景中后,将不考虑对脚本中myVar所做的任何更改。

有关在检查器中显示内容的额外提示

最后,接口也不会显示在检查器中,因为它们不包含任何变量 - 只包含方法和属性。在调试模式下,默认情况下不显示属性。您可以使用检查器右上角带有三行的按钮更改此模式。前两个设置是"正常/调试"。第一个是默认的,第二个也将显示私有变量。这对于监视其值很有用,但不能从编辑器中更改。

因此,如果您需要显示接口,则必须考虑抽象类,因为它提供类似的功能(多重继承除外),但可以是MonoBehavior。

引用:

http://docs.unity3d.com/ScriptReference/SerializeField.html

http://docs.unity3d.com/Manual/script-Serialization.html

https://www.youtube.com/watch?v=9gscwiS3xsU

https://www.youtube.com/watch?v=MmUT0ljrHNc