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# 基础

介绍

这个页面简单介绍了 C# 是什么,以及如何在 Godot 中使用 C#。然后,你可能想看看 如何使用特定功能 ,阅读有关 C# 和 GDScript API 之间的差异 并(重新)访问逐步教程的 编写脚本部分

C# 是由 Microsoft 开发的高级编程语言。在 Godot 中,它是使用现代 .NET 运行时实现的。

注意

在 Godot 4 中用 C# 编写的项目目前无法导出到 Web 平台。如果要在 Web 平台上使用 C#,请考虑使用 Godot 3。 (C# 的)Android 和 iOS 平台支持从 Godot 4.2 开始提供,但仍处于实验阶段,并且 存在一些限制

备注

不是 一个关于 C# 语言整体的全面教程。如果你还不熟悉其语法或功能,请参阅 Microsoft C# 指南 或在其他地方寻找合适的介绍。

前置需求

Godot 捆绑了运行已编译游戏所需的 .NET 组件。不过,Godot 并不捆绑构建和编译游戏所需的工具,例如 MSBuild 和 C# 编译器。这些工具包含在 .NET SDK 中,需要单独安装。

综上所述,你必须安装了 .NET SDK 支持 .NET 的 Godot 版本。

.NET 下载页面 下载并安装SDK的最新稳定版本。Godot 4.5 需要 .NET 8 或更高版本,但导出到 Android 需要 .NET 9 或更高版本。

重要

如果你使用 64 位版本的 Godot,请务必安装 64 位版本的 SDK。

如果你正在从源代码构建 Godot,请确保按照 使用 .NET 编译 页面中概述的步骤来启用 .NET 支持。

配置外部编辑器

Godot 内置脚本编辑器对 C# 的支持较为有限。建议使用外部 IDE 或编辑器,例如 Visual Studio CodeVisual Studio。这些工具为 C# 提供自动补全、调试和其他实用功能。要在 Godot 中选择外部编辑器,请点击 Editor → Editor Settings,然后向下滚动到 Dotnet。在 Dotnet 下点击 Editor,然后选择你想使用的外部编辑器。Godot 目前支持以下外部编辑器:

  • Visual Studio 2022

  • Visual Studio Code

  • MonoDevelop

  • Visual Studio for Mac

  • JetBrains Rider

关于如何配置外部编辑器,请参见以下章节:

JetBrains Rider

阅读完 "前置需求" 部分,就可以下载安装 JetBrains Rider

在 Godot 的 Editor(编辑器) → Editor Settings(编辑器设置) 菜单中:

  • 设置 Dotnet(.NET) -> Editor(编辑器) -> External Editor(外部编辑器)JetBrains Rider

在 Rider 中:

  • 设置 MSBuild version.NET Core

  • 如果你使用的 Rider 版本低于 2024.2,请安装 Godot support 插件。该功能现已内置到 Rider 中。

Visual Studio Code

看完 "前置需求" 部分,就可以下载安装 Visual Studio Code (又名 VS Code)。

在 Godot 的 Editor(编辑器) → Editor Settings(编辑器设置) 菜单中:

  • 设置 Dotnet -> Editor(编辑器) -> External Editor(外部编辑器)Visual Studio Code

在 Visual Studio Code 中:

  • 安装 C# 扩展。

要配置项目进行调试,你需要在 .vscode 文件夹中包含 tasks.jsonlaunch.json 文件,并进行必要的配置。

这是一个 launch.json 示例:

{
    "version": "0.2.0",
    "configurations": [
        {
            "name": "Play",
            "type": "coreclr",
            "request": "launch",
            "preLaunchTask": "build",
            "program": "${env:GODOT4}",
            "args": [],
            "cwd": "${workspaceFolder}",
            "stopAtEntry": false,
        }
    ]
}

要使这个启动配置生效,你需要设置一个 GODOT4 环境变量指向 Godot 可执行文件,或者将 program 参数替换为 Godot 可执行文件的路径。

这是一个 tasks.json 示例:

{
    "version": "2.0.0",
    "tasks": [
        {
            "label": "build",
            "command": "dotnet",
            "type": "process",
            "args": [
                "build"
            ],
            "problemMatcher": "$msCompile"
        }
    ]
}

现在,当你在 Visual Studio Code 中启动调试器时,你的 Godot 项目将会运行。

Visual Studio(仅限 Windows)

下载并安装最新版本的 Visual Studio 。如果你选择了正确的工作负载,Visual Studio 将包含所需的 SDK,所以你不需要手动安装 "前置需求" 部分列出的内容。

在安装 Visual Studio 时,请选择以下工作负载:

  • .NET 桌面开发

在 Godot 的 Editor(编辑器) → Editor Settings(编辑器设置) 菜单中:

  • 设置 Dotnet -> Editor(编辑器) -> External Editor(外部编辑器)Visual Studio

备注

如果你看到了类似“Unable to find package Godot.NET.Sdk”的错误,你的 NuGet 配置可能有问题,需要进行修复。

修复 NuGet 配置文件的简单方法就是重新生成一个。在文件浏览器窗口中前往 %AppData%\NuGet。将 NuGet.Config 文件重命名或删除。重新构建 Godot 项目时,就会自动用默认值创建该文件。

要使用 Visual Studio 调试 C# 脚本,请打开在编辑器中打开第一个 C# 脚本后生成的 .sln 文件。在 Debug 菜单中,进入你的项目的 Debug Properties 菜单项。点击 Create a new profile 按钮并选择 Executable。在 Executable 字段中,浏览并选择 C# 版本 Godot 编辑器的路径;如果已经为 Godot 可执行文件路径创建了环境变量,也可以输入 %GODOT4%。该路径必须指向 Godot 的主可执行文件,而不是“console”版本。对于 Working Directory,请输入单个句点 .,表示当前目录。还要勾选 Enable native code debugging 复选框。现在可以关闭此窗口,点击调试配置文件下拉菜单中的向下箭头,并选择新的启动配置文件。点击绿色的开始按钮后,你的游戏就会以调试模式开始运行。

创建 C# 脚本

成功为 Godot 设置 C# 之后,在场景中的节点的上下文菜单中选择 Attach Script (添加脚本) 时,你应该会看到以下选项:

../../../_images/attachcsharpscript.webp

请注意,虽然在某些细节上有所差别,但在使用C#进行脚本编写时,大多数概念都是相通的。如果你是 Godot 的新手,你可能希望在这个时候查看 脚本语言 上的教程。虽然在文档中缺乏详细的 C# 示例,但大多数概念可以从 GDScript 中转移过来。

项目设置和工作流程

当你创建第一个 C# 脚本时,Godot 会为你的 Godot 项目初始化 C# 项目文件。这包括生成 C# 解决方案( .sln )和项目文件( .csproj ),以及一些实用文件和文件夹( .godot/mono ) 。除了 .godot/mono 之外,所有这些文件都很重要,应该提交到你的版本控制系统中。.godot 文件夹下的所有内容都可以安全地添加到你的版本控制系统的忽略列表中。在排除故障时, 有时删除 .godot/mono 文件夹并让它重新生成可能有所帮助。

示例

这是一个空白的 C# 脚本,带有一些注释,以演示其工作方式。

using Godot;

public partial class YourCustomClass : Node
{
    // Member variables here, example:
    private int _a = 2;
    private string _b = "textvar";

    public override void _Ready()
    {
        // Called every time the node is added to the scene.
        // Initialization here.
        GD.Print("Hello from C# to Godot :)");
    }

    public override void _Process(double delta)
    {
        // Called every frame. Delta is time since the last frame.
        // Update game logic here.
    }
}

如你所见,在 GDScript 中的全局作用域中,像 Godot 的 print 这样的函数可以在 GD 静态类中使用,该类是 Godot 命名空间的一部分。要查看 GD 类中所有方法的完整列表,请参阅: @GDScript@GlobalScope 的类参考页面。

备注

请记住,你希望附加到节点上的类应与 .cs 文件的名称相同。否则,你将会遇到以下错误:

"Cannot find class XXX for script res://XXX.cs"

C# 和 GDScript 之间的一般差异

C# API 使用 PascalCase 而不是 GDScript/C++ 中使用的 snake_case。 在可能的情况下,字段和 getters/setters 已转换为属性。一般来说,C# Godot API 一贯力求尽可能合理。

有关更多信息,请参见 C# API 与 GDScript 的差异 页面。

警告

如果想在编辑器中看到新的导出变量或信号,就需要重新构建项目程序集。可以通过点击编辑器右上角的 Build 按钮手动触发这种构建。

../../../_images/build_dotnet.webp

你还需要重新构建项目集,以应用 "工具" 脚本中的更改。

目前的陷阱和已知问题

由于 Godot 中 C# 支持相对较新,因此存在一些发展过程中的问题和需要解决的事项。以下是在 Godot 中使用 C# 时应注意的最重要问题的列表,但如果有疑问,还可以查看官方的 .NET 问题的跟踪

  • 编写编辑器插件是可能的,但是目前相当复杂。

  • 热重载时,当前状态不被保存和恢复,导出变量除外。

  • 附加 C# 脚本需要引用一个类,该类名需要匹配其文件名。

  • Get()/Set()Call()/CallDeferred() 等方法,以及信号连接方法 Connect() 都依赖于 Godot 的 snake_case API 命名规范。所以,CallDeferred("AddChild") 无法调用 AddChild ,因为API需要原始 snake_case 版本的 add_child。不过,自定义属性和方法没有此限制。建议在 PropertyNameMethodNameSignalName 中使用公开的 StringName ,以避免额外的 StringName 分配以及担心蛇形(snake_case)命名。

截止到 Godot 4.0 版本,支持将 .NET 项目导出到桌面平台(Linux、Windows和macOS)。其他平台将在未来的 4.x 版本中获得支持。

常见陷阱

你可能会在尝试修改 Godot 对象中的一些值时遇到以下错误,例如在尝试改变一个 Node2D 的 X 坐标时:

public partial class MyNode2D : Node2D
{
    public override void _Ready()
    {
        Position.X = 100.0f;
        // CS1612: Cannot modify the return value of 'Node2D.Position' because
        // it is not a variable.
    }
}

这是完全正常的。C# 中的结构体(在这个例子中,是一个 Vector2 )在赋值时会被复制,意味着当你从一个属性或索引器中获取这样一个对象时,你得到的是它的一个副本,而不是它本身。修改这个副本而不重新赋值是没有任何效果的。

解决方法很简单:获取整个结构体,修改你想要修改的值,然后重新赋值给属性。

var newPosition = Position;
newPosition.X = 100.0f;
Position = newPosition;

自 C# 10 起,还可以在结构体上使用 with 表达式 ,让你只需一行实现相同的效果。

Position = Position with { X = 100.0f };

你可以在 C# 语言参考 中了解更多关于这个错误的信息。

C# 在 Godot 中的性能

参见

要了解 Godot 所支持语言的性能对比,请参阅 哪种编程语言最快?

大多数基于 GodotObject 的 Godot C# 对象(例如任何像 Control 这样的 NodeCamera3D 这样的 Node3D )的属性,需要使用本地(互操作)调用,因为它们与 Godot 的 C++ 核心进行通信。如果你需要在单个代码位置多次修改或读取这些属性的值,请考虑将其分配给本地变量:

using Godot;

public partial class YourCustomClass : Node3D
{
    private void ExpensiveReposition()
    {
        for (var i = 0; i < 10; i++)
        {
            // Position is read and set 10 times which incurs native interop.
            // Furthermore the object is repositioned 10 times in 3D space which
            // takes additional time.
            Position += new Vector3(i, i);
        }
    }

    private void Reposition()
    {
        // A variable is used to avoid native interop for Position on every loop.
        var newPosition = Position;
        for (var i = 0; i < 10; i++)
        {
            newPosition += new Vector3(i, i);
        }
        // Setting Position only once avoids native interop and repositioning in 3D space.
        Position = newPosition;
    }
}

将原始数组(例如 byte[] )或 string 传递给 Godot 的 C# API 需要进行数据封装,这在性能上相对较昂贵。

string 隐式转换为 NodePathStringName 会产生本地互操作和数据封装的成本,因为必须将 string 封装并传递到相应的本地构造函数。

在 Godot 中使用 NuGet 包

NuGet 包可以与 Godot 一起安装和使用,就像任何 C# 项目一样。许多 IDE 都可以直接添加软件包,也可以通过在项目根目录下的 .csproj 文件中添加软件包引用来手动添加它们:

    <ItemGroup>
        <PackageReference Include="Newtonsoft.Json" Version="11.0.2" />
    </ItemGroup>
    ...
</Project>

Godot 会在下次构建项目时自动下载并设置新添加的 NuGet 包。

分析 C# 代码

以下工具可用于对托管代码进行性能和内存分析:

  • JetBrains Rider 配合 dotTrace/dotMemory 插件。

  • 独立版的 JetBrains dotTrace/dotMemory。

  • Visual Studio。

使用 JetBrains tools 和 Visual Studio 可以同时对托管和非托管代码进行分析,但仅限于 Windows。