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);

这些信号可以在编辑器或者在代码中使用``Connect``进行连接。如果想在编辑器中连接一个信号,你需要(重新)生成项目组件才能看到这个新的信号。生成操作可以点击 编辑窗口右上角的“Build” 按钮手动触发。

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有一组定义,可让您根据要编译的环境来更改C#代码。

注解

如果是Godot 3.2之前创建的项目,你需要修改或重新生成你的 csproj 文件来使用这个功能(可与一个3.2+新项目中的 <DefineConstants> 相对比)。

示例

例如,你可以根据平台更改代码:

    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
    }

或者你可以检测代码所在的引擎,这对于制作跨引擎库很有用:

    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 项目都会定义 GODOT
  • 按照架构是 64 位还是 32 位,会定义 GODOT_64GODOT_32
  • 按照操作系统,会定义 GODOT_X11GODOT_WINDOWSGODOT_OSXGODOT_ANDROIDGODOT_IOSGODOT_HTML5,或者 GODOT_SERVER 中的一个。这些名称在未来可能会被更改。这些名称是由 :ref:OS <class_OS> 单例(singleton)中的 get_name() 方法创建的,但是 Godot 与 Mono 只能在其中一部分平台上运行。

在**导出**时,根据导出功能,还可能定义以下内容:

  • GODOT_PC``GODOT_MOBILE``或``GODOT_WEB``中的一种,取决于平台类型。
  • ``GODOT_ARM64_V8A``或``GODOT_ARMEABI_V7A``中的一个,仅在安卓系统上,取决于架构。
  • ``GODOT_ARM64``或``GODOT_ARMV7``中的一个,仅在iOS上,取决于架构。
  • GODOT_S3TC``GODOT_ETC``和``GODOT_ETC2``中的任一种,取决于纹理压缩类型。
  • 所有在导出菜单中添加的自定义功能都会被大写并加上前缀: foo -> GODOT_FOO

如果想要参考一个示例项目,可以参考该OS测试demo:https://github.com/godotengine/godot-demo-projects/tree/master/misc/os_test