C# features

本页概述了C#和Godot的常用功能以及它们如何一起使用。

类型转换和强制转换

C#是一种静态类型语言。因此,您无法执行以下操作:

var mySprite = GetNode("MySprite");
mySprite.SetFrame(0);

方法 GetNode() 返回一个 Node 实例。在这种情况下,您必须将其显式转换为所需的派生类型, Sprite

为此,在C#中有多种选择。

强制转换和类型检查

如果返回的节点无法转换为Sprite,则抛出 InvalidCastException 。如果您非常确定它不会失败,您可以使用它而不是 as 运算符。

Sprite mySprite = (Sprite)GetNode("MySprite");
mySprite.SetFrame(0);

使用AS运算符

如果节点无法转换为 Sprite,则 as 运算符返回 null,因此它不能与值类型一起使用。

Sprite mySprite = GetNode("MySprite") as Sprite;
// Only call SetFrame() if mySprite is not null
mySprite?.SetFrame(0);

使用泛型方法

还提供了泛型方法以使该类型转换透明。

GetNode <T>() 在返回之前强制转换节点。如果节点无法强制转换为所需类型,它将抛出一个 InvalidCastException

Sprite mySprite = GetNode<Sprite>("MySprite");
mySprite.SetFrame(0);

GetNodeOrNull <T>() 使用 as 运算符,如果节点无法强制转换为所需类型,则返回 null

Sprite mySprite = GetNodeOrNull<Sprite>("MySprite");
// Only call SetFrame() if mySprite is not null
mySprite?.SetFrame(0);

使用IS运算符进行类型检查

要检查节点是否可以强制转换为 Sprite,可以使用 is 运算符。如果节点无法转换为 Sprite,则 is 运算符返回 false,否则返回 true

if (GetNode("MySprite") is Sprite)
{
    // Yup, it's a sprite!
}

对于更高级的类型检查,您可以查看 模式匹配

C# 信号

有关完整的C#示例,请参阅逐步 编写脚本 教程中的 处理信号 部分。

在C#中声明一个信号是通过一个 delegate 上的 [Signal] 属性完成的。

[Signal]
delegate void MySignal();

[Signal]
delegate void MySignalWithArguments(string foo, int bar);

These signals can then be connected either in the editor or from code with Connect. If you want to connect a signal in the editor, you need to (re)build the project assemblies to see the new signal. This build can be manually triggered by clicking the “Build” button at the top right corner of the editor window.

public void MyCallback()
{
    GD.Print("My callback!");
}

public void MyCallbackWithArguments(string foo, int bar)
{
    GD.Print("My callback with: ", foo, " and ", bar, "!");
}

public void SomeFunction()
{
    instance.Connect("MySignal", this, "MyCallback");
    instance.Connect(nameof(MySignalWithArguments), this, "MyCallbackWithArguments");
}

发射信号是通过 EmitSignal 方法完成的。

public void SomeFunction()
{
    EmitSignal(nameof(MySignal));
    EmitSignal("MySignalWithArguments", "hello there", 28);
}

请注意,您始终可以使用 nameof 关键字引用信号名称(应用于 委托(delegate) 本身)。

建立连接时可以通过传递对象数组来绑定值。

public int Value { get; private set; } = 0;

private void ModifyValue(int modifier)
{
    Value += modifier;
}

public void SomeFunction()
{
    var plusButton = (Button)GetNode("PlusButton");
    var minusButton = (Button)GetNode("MinusButton");

    plusButton.Connect("pressed", this, "ModifyValue", new object[] { 1 });
    minusButton.Connect("pressed", this, "ModifyValue", new object[] { -1 });
}

信号支持所有 内置类型 的参数和绑定值,以及从 Godot.Object 派生的类。因此,任何 NodeReference 将自动兼容,但是自定义数据对象将需要从 Godot.Object 或其子类之中扩展。

public class DataObject : Godot.Object
{
    public string Field1 { get; set; }
    public string Field2 { get; set; }
}

最后,可以通过调用 AddUserSignal 来创建信号,但要注意它应该在使用所述信号之前执行(使用 ConnectEmitSignal)。

public void SomeFunction()
{
    AddUserSignal("MyOtherSignal");
    EmitSignal("MyOtherSignal");
}

Preprocessor defines

Godot has a set of defines that allow you to change your C# code depending on the environment you are compiling to.

注解

If you created your project before Godot 3.2, you have to modify or regenerate your csproj file to use this feature (compare <DefineConstants> with a new 3.2+ project).

示例

For example, you can change code based on the platform:

    public override void _Ready()
    {
#if GODOT_SERVER
        // Don't try to load meshes or anything, this is a server!
        LaunchServer();
#elif GODOT_32 || GODOT_MOBILE || GODOT_WEB
        // Use simple objects when running on less powerful systems.
        SpawnSimpleObjects();
#else
        SpawnComplexObjects();
#endif
    }

Or you can detect which engine your code is in, useful for making cross-engine libraries:

    public void MyPlatformPrinter()
    {
#if GODOT
        GD.Print("This is Godot.");
#elif UNITY_5_3_OR_NEWER
        print("This is Unity.");
#else
        throw new InvalidWorkflowException("Only Godot and Unity are supported.");
#endif
    }

宏定义的完整列表

  • GODOT is always defined for Godot projects.
  • One of GODOT_64 or GODOT_32 is defined depending on if the architecture is 64-bit or 32-bit.
  • One of GODOT_X11, GODOT_WINDOWS, GODOT_OSX, GODOT_ANDROID, GODOT_IOS, GODOT_HTML5, or GODOT_SERVER depending on the OS. These names may change in the future. These are created from the get_name() method of the :ref:OS <class_OS> singleton, but not every possible OS the method returns is an OS that Godot with Mono runs on.

When exporting, the following may also be defined depending on the export features:

  • One of GODOT_PC, GODOT_MOBILE, or GODOT_WEB depending on the platform type.
  • One of GODOT_ARM64_V8A or GODOT_ARMEABI_V7A on Android only depending on the architecture.
  • One of GODOT_ARM64 or GODOT_ARMV7 on iOS only depending on the architecture.
  • Any of GODOT_S3TC, GODOT_ETC, and GODOT_ETC2 depending on the texture compression type.
  • Any custom features added in the export menu will be capitalized and prefixed: foo -> GODOT_FOO.

To see an example project, see the OS testing demo: https://github.com/godotengine/godot-demo-projects/tree/master/misc/os_test