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() 方法覆盖它。

Any type of Resource or Node can be exported. The property editor shows a user-friendly assignment dialog for these types. This can be used instead of GD.Load and GetNode. See Nodes and Resources.

[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; }

带缓动提示的浮点数

Display a visual representation of the ease function when editing.

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

Export with suffix hint

Display a unit hint suffix for exported variables. Works with numeric types, such as floats or vectors:

[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; }

In the above example, \u00b2 is used to write the "squared" character (²).

颜色

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

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

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

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

节点

Since Godot 4.0, nodes can be directly exported without having to use NodePaths.

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

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

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

Custom node classes can also be exported directly. The filtering behavior depends on whether the custom class is a global class.

如有需要,仍可以像 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; }

The drop-down menu will be limited to AnimationNode and all its derived classes. Custom resource classes can also be used, see 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 MyEnum { 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";

Exporting inspector buttons with [ExportToolButton]

If you want to create a clickable button in the inspector, you can use the [ExportToolButton] attribute. This exports a Callable property or field as a clickable button. Since this runs in the editor, usage of the [Tool] attribute is required. When the button is pressed, the callable is called:

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

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

You can also set an icon for the button with a second argument. If specified, an icon will be fetched via GetThemeIcon(), from the "EditorIcons" theme type.

[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> 允许指定字典的键和值的元素的类型。

备注

目前在 Godot 编辑器中不支持类型化字典,因此检查器将不会限制可以分配的类型,可能导致运行时异常。

[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# arrays can exported as long as the element type is a Variant-compatible type.

[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),
];

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

When changing an exported variable's value from a script in 工具模式, the value in the inspector won't be updated automatically. To update it, call NotifyPropertyListChanged() after setting the exported variable's value.

高级导出

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

Before reading further, you should get familiar with the way properties are handled and how they can be customized with _Set(), _Get(), and _GetPropertyList() methods as described in 从对象访问数据或逻辑.

参见

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

警告

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