Attention: Here be dragons

This is the latest (unstable) version of this documentation, which may document features not available in or compatible with released stable versions of Godot.

C# 导出属性

在 Godot 中可以导出类成员。这意味着它们的值会与它们所附加的资源(例如场景)一起保存。它们也可以在属性编辑器中进行编辑。导出使用特性 [Export] 来完成。

using Godot;

public partial class ExportExample : Node3D
{
    [Export]
    public int Number { get; set; } = 5;
}

在这个例子中,数值 5 将被保存,在构建当前项目后,它将在属性编辑器中可见。

导出成员变量的基本好处之一,便是让这些变量在编辑器中可见可改,这样一来,美术师和游戏设计师就可以修改这些会影响程序运行方式的值。为此,Godot 提供了一种特殊的导出语法。

导出只适用于Variant 兼容类型

备注

GDScript 中也能够导出属性,相关信息见 GDScript 导出属性

基本用法

导出适用于字段和属性。它们可以具有任何访问修饰符。

[Export]
private int _number;

[Export]
public int Number { get; set; }

导出的成员可以指定默认值;否则,将使用类型的 默认值

类似于 Numberint 默认为 0Text 默认为 null,因为 string 是引用类型。

[Export]
public int Number { get; set; }

[Export]
public string Text { get; set; }

可以为字段和属性指定默认值。

[Export]
private string _greeting = "Hello World";

[Export]
public string Greeting { get; set; } = "Hello World";

包含后备字段的属性使用后备字段的默认值。

private int _number = 2;

[Export]
public int NumberWithBackingField
{
    get => _number;
    set => _number = value;
}

备注

属性的 get 实际上并不会被执行以确定默认值。相反,Godot 会分析 C# 源代码。这在大多数情况下都很好,例如本页上的示例。但是,有些属性对于分析器理解来说太复杂。

例如,以下属性尝试使用数学运算以在属性编辑器中将默认值显示为 5,但是不起作用:

[Export]
public int NumberWithBackingField
{
    get => _number + 3;
    set => _number = value - 3;
}

private int _number = 2;

分析器无法理解该代码,因此会回退到 int 的默认值 0。但是,在运行场景或检查带有附有工具脚本的节点时,_number 将为 2,而 NumberWithBackingField 将返回 5。这种差异可能会导致令人困惑的行为。为避免这种情况,请不要使用复杂属性。或者,如果可以明确指定默认值,则可以使用 _PropertyCanRevert()_PropertyGetRevert() 方法覆盖它。

任何类型的 ResourceNode 都可以被导出。属性编辑器为这些类型显示一个用户友好的赋值对话框。这可以用于替代 GD.LoadGetNode。请参阅节点和资源

[Export]
public PackedScene PackedScene { get; set; }

[Export]
public RigidBody2D RigidBody2D { get; set; }

导出分组

可以使用 [ExportGroup] 特性将导出的属性分组显示在检视器中。在此特性之后的每个导出的属性都将添加到该组中。开始一个新组,或使用 [ExportGroup("")] 进行分组结束。

[ExportGroup("My Properties")]
[Export]
public int Number { get; set; } = 3;

该特性的第二个参数可用于仅将具有指定前缀的属性分组。

分组可以嵌套,请使用 [ExportSubgroup] 在分组中创建子分组。

[ExportSubgroup("Extra Properties")]
[Export]
public string Text { get; set; } = "";
[Export]
public bool Flag { get; set; } = false;

你也可以修改你的主分类的名称,或者在属性列表中使用 [ExportCategory] 特性创建额外的分类。

[ExportCategory("Main Category")]
[Export]
public int Number { get; set; } = 3;
[Export]
public string Text { get; set; } = "";

[ExportCategory("Extra Category")]
[Export]
public bool Flag { get; set; } = false;

备注

属性列表是根据类继承来组织的,而新的分类会打破这种预期。使用它们时要小心,尤其是在创建公共项目时。

字符串用作文件路径

属性提示可以用来导出字符串作为路径

字符串可以用作文件路径。

[Export(PropertyHint.File)]
public string GameFile { get; set; }

字符串也可以用作文件目录路径。

[Export(PropertyHint.Dir)]
public string GameDirectory { get; set; }

字符串在用作是文件路径时,可以在提示项中提供自定义过滤器。

[Export(PropertyHint.File, "*.txt,")]
public string GameFile { get; set; }

也可以使用全局文件系统中的路径,仅工具模式下的脚本可以如此使用。

字符串还可以用作全局文件系统中某个 PNG 文件的路径。

[Export(PropertyHint.GlobalFile, "*.png")]
public string ToolImage { get; set; }

字符串也能用作全局文件系统中某个目录的路径。

[Export(PropertyHint.GlobalDir)]
public string ToolDir { get; set; }

多行文本注释会让编辑器使用大文本输入框来输入文本,而非那种小小的单行输入框。

[Export(PropertyHint.MultilineText)]
public string Text { get; set; }

编辑器内限制值的输入范围

使用范围属性提示允许你在编辑器中限制可以输入的值。

允许 0 到 20 之间的整数。

[Export(PropertyHint.Range, "0,20,")]
public int Number { get; set; }

允许 -10 到 20 之间的整数。

[Export(PropertyHint.Range, "-10,20,")]
public int Number { get; set; }

允许 -10 到 20 之间的数,调整步长为 0.2 。

[Export(PropertyHint.Range, "-10,20,0.2")]
public float Number { get; set; }

如果添加了 "or_greater" 和/或 "or_less" 的提示,在调整值时通过键入而非使用滑块,可以超过或低于限制。

[Export(PropertyHint.Range, "0,100,1,or_greater,or_less")]
public int Number { get; set; }

带缓动提示的浮点数

在编辑器里提供 ease 函数的视觉呈现。

[Export(PropertyHint.ExpEasing)]
public float TransitionSpeed { get; set; }

导出时附带后缀提示

为导出的变量显示单位提示后缀。适用于数值类型,如浮点数或向量:

[Export(PropertyHint.None, "suffix:m/s\u00b2")]
public float Gravity { get; set; } = 9.8f;
[Export(PropertyHint.None, "suffix:m/s")]
public Vector3 Velocity { get; set; }

在上面的示例中,\u00b2 用于书写"平方"字符(²)。

颜色

使用红、绿、蓝、Alpha 值指定普通颜色。

[Export]
public Color Color { get; set; }

使用红、绿、蓝值指定颜色(此时Alpha 始终为 1)。

[Export(PropertyHint.ColorNoAlpha)]
public Color Color { get; set; }

节点

节点(Nodes)也可以被直接导出,完全不需要借助 NodePath(节点路径)。

[Export]
public Node Node { get; set; }

还可以直接导出特定类型的节点。在检查器中按“赋值”后显示的节点列表会过滤为指定的类型,并且只有正确的节点才能被赋值。

[Export]
public Sprite2D Sprite2D { get; set; }

自定义节点类也可以被直接导出。过滤行为取决于自定义类是否为全局类

如有需要,仍可以像 Godot 3.x 中那样导出 NodePath:

[Export]
public NodePath NodePath { get; set; }

public override void _Ready()
{
    var node = GetNode(NodePath);
}

资源

[Export]
public Resource Resource { get; set; }

在检查器里,可以将资源文件从文件系统面板中拖放到导出变量所对应的槽位中。

展开检查器下拉菜单可能导致一个极长的可能创建的类别列表。因此,如果你指定从 Resource 派生的类型的话:

[Export]
public AnimationNode AnimationNode { get; set; }

那么下拉菜单将只限于 AnimationNode 及其所有继承的类。也可以使用自定义的资源类,参见 C# 全局类

必须注意:即使在在编辑器模式中未运行脚本,导出的属性仍可编辑。这一点可以与 使用工具模式的脚本 配合使用。

导出位标记

具有 [Flags] 特性的枚举类型的成员可以被导出,它们的值被限制为枚举类型的成员。编辑器将在检查器中创建一个小部件,允许选择枚举成员中的零个、一个或多个。该值将被存储为整数。

标志枚举使用 2 的幂作为枚举成员的值。使用逻辑或(|)组合多个标志的成员也是可能的。

[Flags]
public enum SpellElements
{
    Fire = 1 << 1,
    Water = 1 << 2,
    Earth = 1 << 3,
    Wind = 1 << 4,

    FireAndWater = Fire | Water,
}

[Export]
public SpellElements MySpellElements { get; set; }

用作位标志的整数可以在一个属性中存储多个 true/ false(布尔)值。通过使用 Flags 属性提示,可以从编辑器中设置任何给定的标志。

[Export(PropertyHint.Flags, "Fire,Water,Earth,Wind")]
public int SpellElements { get; set; } = 0;

你必须为每个标志提供一个字符串描述。在这个例子中,Fire 的值是 1,Water 的值是 2,Earth 的值是 4,Wind 对应的值是 8。通常,应相应地定义常量(例如 private const int ElementWind = 8 等等)。

你可以使用冒号添加显式的值:

[Export(PropertyHint.Flags, "Self:4,Allies:8,Foes:16")]
public int SpellTargets { get; set; } = 0;

只有2的幂次方值才是有效的位标记选项。允许的最低值是1,因为0表示没有选中任何内容。你还可以添加一些其他标记的组合作为选项:

[Export(PropertyHint.Flags, "Self:4,Allies:8,Self and Allies:12,Foes:16")]
public int SpellTargets { get; set; } = 0;

也可以为项目设置中定义的物理层和渲染层提供导出提示。

[Export(PropertyHint.Layers2DPhysics)]
public uint Layers2DPhysics { get; set; }
[Export(PropertyHint.Layers2DRender)]
public uint Layers2DRender { get; set; }
[Export(PropertyHint.Layers3DPhysics)]
public uint Layers3DPhysics { get; set; }
[Export(PropertyHint.Layers3DRender)]
public uint Layers3DRender { get; set; }

使用位标记需要对位操作有一定的了解,若对此有疑问,请使用布尔变量代替位标记使用。

导出枚举

类型是枚举的成员可以导出,它们的值只能是枚举类型的成员之一。编辑器会在检查器中创建一个小部件,把以下内容列举为“Thing 1”、“Thing 2”、“Another Thing”。这个值会以整数的形式存储。

public enum MyEnum
{
    Thing1,
    Thing2,
    AnotherThing = -1,
}

[Export]
public MyEnum MyEnumCurrent { get; set; }

整数和字符串类型的成员也可以使用 [Export] 特性和 PropertyHint.Enum 提示来限制为特定的值列表。编辑器会在检查器中创建一个小部件,把以下内容列举为 Warrior、Magician、Thief。这个值会以整数的形式存储,对应于所选选项的索引(即 012 )。

[Export(PropertyHint.Enum, "Warrior,Magician,Thief")]
public int CharacterClass { get; set; }

你可以使用冒号添加显式的值:

[Export(PropertyHint.Enum, "Slow:30,Average:60,Very Fast:200")]
public int CharacterSpeed { get; set; }

如果类型是 string ,值将以字符串形式存储。

[Export(PropertyHint.Enum, "Rebecca,Mary,Leah")]
public string CharacterName { get; set; }

如果你想设置初始值,你必须明确指定它:

[Export(PropertyHint.Enum, "Rebecca,Mary,Leah")]
public string CharacterName { get; set; } = "Rebecca";

使用 [ExportToolButton] 导出检查器按钮

如果你想创建一个可以点击的按钮,可以使用 [ExportToolButton] 属性。它会将一个 Callable(可调用对象)属性或字段导出为一个可点击的按钮。由于这需要在编辑器环境下运行,所以必须加上 [Tool] 属性。当按钮被按下时,绑定的 Callable 就会被调用。

[Tool]
public partial class MyNode : Node
{
    [ExportToolButton("Click me!")]
    public Callable ClickMeButton => Callable.From(ClickMe);

    public void ClickMe()
    {
        GD.Print("Hello world!");
    }
}

你也可以通过第二个参数为按钮设置图标。如果指定了该参数,将会通过 GetThemeIcon()"EditorIcons" 主题类型中获取图标。

[ExportToolButton("Click me!", Icon = "CharacterBody2D")]
public Callable ClickMeButton => Callable.From(ClickMe);

导出集合

C# Variant 文档所述,只有特定的 C# 数组和 Godot.Collections 命名空间中定义的集合类型是 Variant 兼容的,因此,只有这些类型才能被导出。

导出 Godot 数组

[Export]
public Godot.Collections.Array Array { get; set; }

使用泛型 Godot.Collections.Array<T> 可以指定数组元素的类型,这将用作编辑器的提示。检查器会将元素限制为指定的类型。

[Export]
public Godot.Collections.Array<string> Array { get; set; }

Godot 数组的默认值为 null。可以指定不同的默认值:

[Export]
public Godot.Collections.Array<string> CharacterNames { get; set; } =
[
    "Rebecca",
    "Mary",
    "Leah",
];

如果导出的数组指定了从 Resource 继承的类型,则可以一次性从文件系统面板中拖放多个文件到检查器中来设置该数组的值。

[Export]
public Godot.Collections.Array<Texture> Textures { get; set; }

[Export]
public Godot.Collections.Array<PackedScene> Scenes { get; set; }

导出 Godot 字典

[Export]
public Godot.Collections.Dictionary Dictionary { get; set; }

使用泛型 Godot.Collections.Dictionary<TKey, TValue> 允许指定字典的键和值的元素的类型。

[Export]
public Godot.Collections.Dictionary<string, int> Dictionary { get; set; }

Godot 字典的默认值是 null。可以指定不同的默认值:

[Export]
public Godot.Collections.Dictionary<string, int> CharacterLives { get; set; } = new Godot.Collections.Dictionary<string, int>
{
    ["Rebecca"] = 10,
    ["Mary"] = 42,
    ["Leah"] = 0,
};

导出 C# 数组

只要 C# 数组的元素类型是 Variant 兼容类型,就可以导出。

[Export]
public Vector3[] Vectors { get; set; }

[Export]
public NodePath[] NodePaths { get; set; }

C# 数组的默认值是 null。可以指定不同的默认值:

[Export]
public Vector3[] Vectors { get; set; } =
[
    new Vector3(1, 2, 3),
    new Vector3(3, 2, 1),
];

从工具脚本中设置导出变量

工具模式 下的脚本中的一个导出变量的值改变时,该值在检查器中不会自动更新。要更新它,请在设置该导出变量的值之后调用 NotifyPropertyListChanged()

高级导出

为了避免非必要的复杂设计,并非所有类型的导出都在语言层面上提供。下面将说明一些能用底层 API 实现的,且较为常见的导出方法。

在继续阅读之前,你应该先熟悉一下属性的处理方式,以及如何通过 _Set()_Get()_GetPropertyList() 方法来进行自定义。这些内容在 从对象访问数据或逻辑 中都有详细说明。

参见

要在 C++ 中用上述方法绑定属性,请参阅 使用 _set/_get/_get_property_list 绑定属性

警告

脚本必须在 tool 模式运行,才能使上述方法在编辑器内运行。